Add Skin and Cape textures to players
This commit is contained in:
parent
035b86920a
commit
16403673d3
24
src/main/java/me/braydon/mc/common/Tuple.java
Normal file
24
src/main/java/me/braydon/mc/common/Tuple.java
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package me.braydon.mc.common;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple tuple utility.
|
||||||
|
*
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor @AllArgsConstructor @Setter @Getter
|
||||||
|
public class Tuple<L, R> {
|
||||||
|
/**
|
||||||
|
* The left value.
|
||||||
|
*/
|
||||||
|
private L left;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The right value.
|
||||||
|
*/
|
||||||
|
private R right;
|
||||||
|
}
|
30
src/main/java/me/braydon/mc/model/Cape.java
Normal file
30
src/main/java/me/braydon/mc/model/Cape.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package me.braydon.mc.model;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cape for a {@link Player}.
|
||||||
|
*
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @ToString
|
||||||
|
public final class Cape {
|
||||||
|
/**
|
||||||
|
* The texture URL of this cape.
|
||||||
|
*/
|
||||||
|
@NonNull private final String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a cape from the given Json object.
|
||||||
|
*
|
||||||
|
* @param jsonObject the json object to build from
|
||||||
|
* @return the built cape
|
||||||
|
*/
|
||||||
|
public static Cape fromJsonObject(JsonObject jsonObject) {
|
||||||
|
if (jsonObject == null) { // No object to parse
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Cape(jsonObject.get("url").getAsString());
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,16 @@ public class Player {
|
|||||||
*/
|
*/
|
||||||
@NonNull private String username;
|
@NonNull private String username;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The skin of this player.
|
||||||
|
*/
|
||||||
|
@NonNull private Skin skin;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cape of this player, null if none.
|
||||||
|
*/
|
||||||
|
private Cape cape;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The profile actions this player has, null if none.
|
* The profile actions this player has, null if none.
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,50 @@
|
|||||||
package me.braydon.mc.model;
|
package me.braydon.mc.model;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A skin for a {@link Player}.
|
||||||
|
*
|
||||||
* @author Braydon
|
* @author Braydon
|
||||||
*/
|
*/
|
||||||
|
@AllArgsConstructor(access = AccessLevel.PRIVATE) @Getter @ToString
|
||||||
public final class Skin {
|
public final class Skin {
|
||||||
|
public static final Skin DEFAULT_STEVE = new Skin("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1", Model.DEFAULT);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The texture URL of this skin.
|
||||||
|
*/
|
||||||
|
@NonNull private final String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model of this skin.
|
||||||
|
*/
|
||||||
|
@NonNull private final Model model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a skin from the given Json object.
|
||||||
|
*
|
||||||
|
* @param jsonObject the json object to build from
|
||||||
|
* @return the built skin
|
||||||
|
*/
|
||||||
|
public static Skin fromJsonObject(JsonObject jsonObject) {
|
||||||
|
if (jsonObject == null) { // No object to parse
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Model model = Model.DEFAULT; // The skin model
|
||||||
|
|
||||||
|
JsonObject metadataJsonObject = jsonObject.getAsJsonObject("metadata");
|
||||||
|
if (metadataJsonObject != null) { // Parse the skin model
|
||||||
|
model = Model.valueOf(metadataJsonObject.get("model").getAsString().toUpperCase());
|
||||||
|
}
|
||||||
|
return new Skin(jsonObject.get("url").getAsString(), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible models for a skin.
|
||||||
|
*/
|
||||||
|
public enum Model {
|
||||||
|
SLIM, DEFAULT
|
||||||
|
}
|
||||||
}
|
}
|
@ -4,8 +4,10 @@ import lombok.Getter;
|
|||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import me.braydon.mc.model.Cape;
|
||||||
import me.braydon.mc.model.Player;
|
import me.braydon.mc.model.Player;
|
||||||
import me.braydon.mc.model.ProfileAction;
|
import me.braydon.mc.model.ProfileAction;
|
||||||
|
import me.braydon.mc.model.Skin;
|
||||||
import org.springframework.data.redis.core.RedisHash;
|
import org.springframework.data.redis.core.RedisHash;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -26,8 +28,9 @@ public final class CachedPlayer extends Player implements Serializable {
|
|||||||
*/
|
*/
|
||||||
private long cached;
|
private long cached;
|
||||||
|
|
||||||
public CachedPlayer(@NonNull UUID uniqueId, @NonNull String username, ProfileAction[] profileActions, long cached) {
|
public CachedPlayer(@NonNull UUID uniqueId, @NonNull String username,
|
||||||
super(uniqueId, username, profileActions);
|
@NonNull Skin skin, Cape cape, ProfileAction[] profileActions, long cached) {
|
||||||
|
super(uniqueId, username, skin, cape, profileActions);
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,14 @@
|
|||||||
package me.braydon.mc.model.token;
|
package me.braydon.mc.model.token;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
|
import me.braydon.mc.RESTfulMC;
|
||||||
|
import me.braydon.mc.common.Tuple;
|
||||||
|
import me.braydon.mc.model.Cape;
|
||||||
import me.braydon.mc.model.ProfileAction;
|
import me.braydon.mc.model.ProfileAction;
|
||||||
|
import me.braydon.mc.model.Skin;
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A token representing a Mojang user profile.
|
* A token representing a Mojang user profile.
|
||||||
@ -31,6 +38,37 @@ public final class MojangProfileToken {
|
|||||||
*/
|
*/
|
||||||
@NonNull private ProfileAction[] profileActions;
|
@NonNull private ProfileAction[] profileActions;
|
||||||
|
|
||||||
|
public Tuple<Skin, Cape> getSkinAndCape() {
|
||||||
|
ProfileProperty textures = getPropertyByName("textures"); // Get the profile textures
|
||||||
|
if (textures == null) { // No profile textures
|
||||||
|
return new Tuple<>();
|
||||||
|
}
|
||||||
|
JsonObject jsonObject = RESTfulMC.GSON.fromJson(textures.getDecodedValue(), JsonObject.class); // Get the Json object
|
||||||
|
JsonObject texturesJsonObject = jsonObject.getAsJsonObject("textures"); // Get the textures object
|
||||||
|
|
||||||
|
// Return the tuple containing the skin and cape
|
||||||
|
return new Tuple<>(
|
||||||
|
Skin.fromJsonObject(texturesJsonObject.getAsJsonObject("SKIN")),
|
||||||
|
Cape.fromJsonObject(texturesJsonObject.getAsJsonObject("CAPE"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the profile property
|
||||||
|
* with the given name.
|
||||||
|
*
|
||||||
|
* @param name the property name
|
||||||
|
* @return the profile property, null if none
|
||||||
|
*/
|
||||||
|
public ProfileProperty getPropertyByName(@NonNull String name) {
|
||||||
|
for (ProfileProperty property : properties) {
|
||||||
|
if (property.getName().equalsIgnoreCase(name)) {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A property of a Mojang profile.
|
* A property of a Mojang profile.
|
||||||
*/
|
*/
|
||||||
@ -51,6 +89,17 @@ public final class MojangProfileToken {
|
|||||||
*/
|
*/
|
||||||
private String signature;
|
private String signature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the decoded Base64
|
||||||
|
* value of this property.
|
||||||
|
*
|
||||||
|
* @return the decoded value
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public String getDecodedValue() {
|
||||||
|
return new String(Base64.getDecoder().decode(value));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is this property signed?
|
* Is this property signed?
|
||||||
*
|
*
|
||||||
|
@ -2,13 +2,16 @@ package me.braydon.mc.service;
|
|||||||
|
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
import me.braydon.mc.common.Tuple;
|
||||||
import me.braydon.mc.common.UUIDUtils;
|
import me.braydon.mc.common.UUIDUtils;
|
||||||
import me.braydon.mc.common.web.JsonWebException;
|
import me.braydon.mc.common.web.JsonWebException;
|
||||||
import me.braydon.mc.common.web.JsonWebRequest;
|
import me.braydon.mc.common.web.JsonWebRequest;
|
||||||
import me.braydon.mc.exception.impl.BadRequestException;
|
import me.braydon.mc.exception.impl.BadRequestException;
|
||||||
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
import me.braydon.mc.exception.impl.ResourceNotFoundException;
|
||||||
|
import me.braydon.mc.model.Cape;
|
||||||
import me.braydon.mc.model.Player;
|
import me.braydon.mc.model.Player;
|
||||||
import me.braydon.mc.model.ProfileAction;
|
import me.braydon.mc.model.ProfileAction;
|
||||||
|
import me.braydon.mc.model.Skin;
|
||||||
import me.braydon.mc.model.cache.CachedPlayer;
|
import me.braydon.mc.model.cache.CachedPlayer;
|
||||||
import me.braydon.mc.model.cache.CachedPlayerName;
|
import me.braydon.mc.model.cache.CachedPlayerName;
|
||||||
import me.braydon.mc.model.token.MojangProfileToken;
|
import me.braydon.mc.model.token.MojangProfileToken;
|
||||||
@ -60,10 +63,10 @@ public final class MojangService {
|
|||||||
* and then return the response.
|
* and then return the response.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param query the query to search for the player by
|
* @param query the query to search for the player by
|
||||||
* @param bypassCache should the cache be bypassed?
|
* @param bypassCache should the cache be bypassed?
|
||||||
* @return the player
|
* @return the player
|
||||||
* @throws BadRequestException if the UUID is malformed
|
* @throws BadRequestException if the UUID is malformed
|
||||||
* @throws ResourceNotFoundException if the player is not found
|
* @throws ResourceNotFoundException if the player is not found
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
@ -100,11 +103,14 @@ public final class MojangService {
|
|||||||
MojangProfileToken token = JsonWebRequest.makeRequest(
|
MojangProfileToken token = JsonWebRequest.makeRequest(
|
||||||
UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET
|
UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET
|
||||||
).execute(MojangProfileToken.class);
|
).execute(MojangProfileToken.class);
|
||||||
|
Tuple<Skin, Cape> skinAndCape = token.getSkinAndCape(); // Get the skin and cape
|
||||||
ProfileAction[] profileActions = token.getProfileActions();
|
ProfileAction[] profileActions = token.getProfileActions();
|
||||||
|
|
||||||
// Build our player model, cache it, and then return it
|
// Build our player model, cache it, and then return it
|
||||||
CachedPlayer player = new CachedPlayer(
|
CachedPlayer player = new CachedPlayer(
|
||||||
uuid, token.getName(),
|
uuid, token.getName(),
|
||||||
|
skinAndCape.getLeft() == null ? Skin.DEFAULT_STEVE : skinAndCape.getLeft(),
|
||||||
|
skinAndCape.getRight(),
|
||||||
profileActions.length == 0 ? null : profileActions,
|
profileActions.length == 0 ? null : profileActions,
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user