forked from MinecraftUtilities/Backend
more cleanup
This commit is contained in:
parent
1f45d26f53
commit
4cdffd47fd
@ -1,7 +1,6 @@
|
|||||||
package cc.fascinated;
|
package cc.fascinated;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@ -16,11 +15,8 @@ import java.util.Objects;
|
|||||||
@SpringBootApplication @Log4j2
|
@SpringBootApplication @Log4j2
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
@Getter
|
public static final Gson GSON = new Gson();
|
||||||
private static final Gson GSON = new Gson();
|
public static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
|
||||||
|
|
||||||
@Getter
|
|
||||||
private static final HttpClient CLIENT = HttpClient.newHttpClient();
|
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
@ -3,8 +3,7 @@ package cc.fascinated.controller;
|
|||||||
import cc.fascinated.service.PlayerService;
|
import cc.fascinated.service.PlayerService;
|
||||||
import cc.fascinated.model.player.Player;
|
import cc.fascinated.model.player.Player;
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
import cc.fascinated.model.player.SkinPart;
|
import cc.fascinated.util.PlayerUtils;
|
||||||
import lombok.NonNull;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.CacheControl;
|
import org.springframework.http.CacheControl;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@ -13,7 +12,6 @@ import org.springframework.http.ResponseEntity;
|
|||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@ -21,9 +19,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class PlayerController {
|
public class PlayerController {
|
||||||
|
|
||||||
private final CacheControl cacheControl = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic();
|
private final CacheControl cacheControl = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic();
|
||||||
@NonNull
|
|
||||||
private final SkinPart defaultHead = Objects.requireNonNull(Skin.getDefaultHead(), "Default head is null");
|
|
||||||
|
|
||||||
private final PlayerService playerManagerService;
|
private final PlayerService playerManagerService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@ -48,22 +43,20 @@ public class PlayerController {
|
|||||||
@PathVariable String id,
|
@PathVariable String id,
|
||||||
@RequestParam(required = false, defaultValue = "250") int size) {
|
@RequestParam(required = false, defaultValue = "250") int size) {
|
||||||
Player player = playerManagerService.getPlayer(id);
|
Player player = playerManagerService.getPlayer(id);
|
||||||
byte[] headBytes = new byte[0];
|
byte[] partBytes = new byte[0];
|
||||||
if (player != null) { // The player exists
|
if (player != null) { // The player exists
|
||||||
Skin skin = player.getSkin();
|
Skin skin = player.getSkin();
|
||||||
SkinPart skinPart = skin.getPart(part);
|
Skin.Parts skinPart = Skin.Parts.fromName(part);
|
||||||
if (skinPart != null) {
|
partBytes = PlayerUtils.getSkinPartBytes(skin, skinPart, size);
|
||||||
headBytes = skinPart.getPartData(size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headBytes == null) { // Fallback to the default head
|
if (partBytes == null) { // Fallback to the default head
|
||||||
headBytes = defaultHead.getPartData(size);
|
partBytes = PlayerUtils.getSkinPartBytes(Skin.DEFAULT_SKIN, Skin.Parts.HEAD, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.cacheControl(cacheControl)
|
.cacheControl(cacheControl)
|
||||||
.contentType(MediaType.IMAGE_PNG)
|
.contentType(MediaType.IMAGE_PNG)
|
||||||
.body(headBytes);
|
.body(partBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cc.fascinated.model.player;
|
package cc.fascinated.model.player;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
@ -10,4 +11,17 @@ public class Cape {
|
|||||||
* The URL of the cape
|
* The URL of the cape
|
||||||
*/
|
*/
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the cape from a {@link JsonObject}.
|
||||||
|
*
|
||||||
|
* @param json the JSON object
|
||||||
|
* @return the cape
|
||||||
|
*/
|
||||||
|
public static Cape fromJson(JsonObject json) {
|
||||||
|
if (json == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new Cape(json.get("url").getAsString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import cc.fascinated.util.Tuple;
|
|||||||
import cc.fascinated.util.UUIDUtils;
|
import cc.fascinated.util.UUIDUtils;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@ -33,15 +32,9 @@ public class Player {
|
|||||||
*/
|
*/
|
||||||
private Cape cape;
|
private Cape cape;
|
||||||
|
|
||||||
/**
|
|
||||||
* The raw properties of the player
|
|
||||||
*/
|
|
||||||
private final List<MojangProfile.ProfileProperty> rawProperties;
|
|
||||||
|
|
||||||
public Player(MojangProfile profile) {
|
public Player(MojangProfile profile) {
|
||||||
this.uuid = UUID.fromString(UUIDUtils.addUUIDDashes(profile.getId()));
|
this.uuid = UUID.fromString(UUIDUtils.addUuidDashes(profile.getId()));
|
||||||
this.name = profile.getName();
|
this.name = profile.getName();
|
||||||
this.rawProperties = profile.getProperties();
|
|
||||||
|
|
||||||
// Get the skin and cape
|
// Get the skin and cape
|
||||||
Tuple<Skin, Cape> skinAndCape = profile.getSkinAndCape();
|
Tuple<Skin, Cape> skinAndCape = profile.getSkinAndCape();
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
package cc.fascinated.model.player;
|
package cc.fascinated.model.player;
|
||||||
|
|
||||||
import cc.fascinated.Main;
|
|
||||||
import cc.fascinated.config.Config;
|
import cc.fascinated.config.Config;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.http.HttpRequest;
|
|
||||||
import java.net.http.HttpResponse;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@Getter @Log4j2
|
@Getter @Log4j2
|
||||||
public class Skin {
|
public class Skin {
|
||||||
|
/**
|
||||||
|
* The default skin, usually used when the skin is not found.
|
||||||
|
*/
|
||||||
|
public static final Skin DEFAULT_SKIN = new Skin("http://textures.minecraft.net/texture/60a5bd016b3c9a1b9272e4929e30827a67be4ebb219017adbbc4a4d22ebd5b1",
|
||||||
|
Model.DEFAULT);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The URL of the skin
|
* The URL of the skin
|
||||||
@ -27,78 +26,44 @@ public class Skin {
|
|||||||
/**
|
/**
|
||||||
* The model of the skin
|
* The model of the skin
|
||||||
*/
|
*/
|
||||||
private final SkinType model;
|
private final Model model;
|
||||||
|
|
||||||
/**
|
|
||||||
* The bytes of the skin
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
private final byte[] skinBytes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The skin parts for this skin
|
|
||||||
*/
|
|
||||||
@JsonIgnore
|
|
||||||
private final Map<SkinPartEnum, SkinPart> parts = new HashMap<>();
|
|
||||||
|
|
||||||
@JsonProperty("parts")
|
@JsonProperty("parts")
|
||||||
private final Map<String, String> partUrls = new HashMap<>();
|
private final Map<String, String> partUrls = new HashMap<>();
|
||||||
|
|
||||||
public Skin(String playerUuid, String url, SkinType model) {
|
public Skin(String url, Model model) {
|
||||||
this.url = url;
|
this.url = url;
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.skinBytes = this.getSkinData();
|
}
|
||||||
|
|
||||||
// The skin parts
|
/**
|
||||||
this.parts.put(SkinPartEnum.HEAD, new SkinPart(this.skinBytes, SkinPartEnum.HEAD));
|
* Gets the skin from a {@link JsonObject}.
|
||||||
|
*
|
||||||
|
* @param json the JSON object
|
||||||
|
* @return the skin
|
||||||
|
*/
|
||||||
|
public static Skin fromJson(JsonObject json) {
|
||||||
|
if (json == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String url = json.get("url").getAsString();
|
||||||
|
JsonObject metadata = json.getAsJsonObject("metadata");
|
||||||
|
Model model = Model.fromName(metadata == null ? "slim" : // Fall back to slim if the model is not found
|
||||||
|
metadata.get("model").getAsString());
|
||||||
|
return new Skin(url, model);
|
||||||
|
}
|
||||||
|
|
||||||
for (Map.Entry<SkinPartEnum, SkinPart> entry : this.parts.entrySet()) {
|
/**
|
||||||
String partName = entry.getKey().name().toLowerCase();
|
* Populates the part URLs for the skin.
|
||||||
|
*
|
||||||
|
* @param playerUuid the player's UUID
|
||||||
|
*/
|
||||||
|
public Skin populatePartUrls(String playerUuid) {
|
||||||
|
for (Parts part : Parts.values()) {
|
||||||
|
String partName = part.name().toLowerCase();
|
||||||
this.partUrls.put(partName, Config.INSTANCE.getWebPublicUrl() + "/player/" + partName + "/" + playerUuid + "?size=250");
|
this.partUrls.put(partName, Config.INSTANCE.getWebPublicUrl() + "/player/" + partName + "/" + playerUuid + "?size=250");
|
||||||
}
|
}
|
||||||
}
|
return this;
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default/fallback head.
|
|
||||||
*
|
|
||||||
* @return the default head
|
|
||||||
*/
|
|
||||||
public static SkinPart getDefaultHead() {
|
|
||||||
try (InputStream stream = Main.class.getClassLoader().getResourceAsStream("images/default_head.png")) {
|
|
||||||
if (stream == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
byte[] bytes = stream.readAllBytes();
|
|
||||||
return new SkinPart(bytes, SkinPartEnum.HEAD);
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.warn("Failed to load default head", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the skin data from the URL.
|
|
||||||
*
|
|
||||||
* @return the skin data
|
|
||||||
*/
|
|
||||||
@SneakyThrows @JsonIgnore
|
|
||||||
public byte[] getSkinData() {
|
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
|
||||||
.uri(new URI(this.url))
|
|
||||||
.GET()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
return Main.getCLIENT().send(request, HttpResponse.BodyHandlers.ofByteArray()).body();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a part from the skin.
|
|
||||||
*
|
|
||||||
* @param part the part name
|
|
||||||
* @return the part
|
|
||||||
*/
|
|
||||||
public SkinPart getPart(String part) {
|
|
||||||
return this.parts.get(SkinPartEnum.valueOf(part.toUpperCase()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,7 +71,7 @@ public class Skin {
|
|||||||
* information about the part.
|
* information about the part.
|
||||||
*/
|
*/
|
||||||
@Getter @AllArgsConstructor
|
@Getter @AllArgsConstructor
|
||||||
public enum SkinPartEnum {
|
public enum Parts {
|
||||||
|
|
||||||
HEAD(8, 8, 8, 8, 250);
|
HEAD(8, 8, 8, 8, 250);
|
||||||
|
|
||||||
@ -124,13 +89,43 @@ public class Skin {
|
|||||||
* The scale of the part.
|
* The scale of the part.
|
||||||
*/
|
*/
|
||||||
private final int defaultSize;
|
private final int defaultSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the skin part from its name.
|
||||||
|
*
|
||||||
|
* @param name the name of the part
|
||||||
|
* @return the skin part
|
||||||
|
*/
|
||||||
|
public static Parts fromName(String name) {
|
||||||
|
for (Parts part : values()) {
|
||||||
|
if (part.name().equalsIgnoreCase(name)) {
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the skin.
|
* The model of the skin.
|
||||||
*/
|
*/
|
||||||
public enum SkinType {
|
public enum Model {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
SLIM
|
SLIM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the model from its name.
|
||||||
|
*
|
||||||
|
* @param name the name of the model
|
||||||
|
* @return the model
|
||||||
|
*/
|
||||||
|
public static Model fromName(String name) {
|
||||||
|
for (Model model : values()) {
|
||||||
|
if (model.name().equalsIgnoreCase(name)) {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
package cc.fascinated.model.player;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.log4j.Log4j2;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
|
|
||||||
@Getter @Log4j2
|
|
||||||
public class SkinPart {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The whole skin data.
|
|
||||||
*/
|
|
||||||
private final byte[] data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The information about the part.
|
|
||||||
*/
|
|
||||||
private final Skin.SkinPartEnum skinPartEnum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The part data from the skin.
|
|
||||||
*/
|
|
||||||
private byte[] partBytes;
|
|
||||||
|
|
||||||
public SkinPart(byte[] data, Skin.SkinPartEnum skinPartEnum) {
|
|
||||||
this.data = data;
|
|
||||||
this.skinPartEnum = skinPartEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the part data from the skin.
|
|
||||||
*
|
|
||||||
* @return the part data
|
|
||||||
*/
|
|
||||||
public byte[] getPartData(int size) {
|
|
||||||
if (size == -1) {
|
|
||||||
size = this.skinPartEnum.getDefaultSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(this.data));
|
|
||||||
if (image == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Get the part of the image (e.g. the head)
|
|
||||||
BufferedImage partImage = image.getSubimage(this.skinPartEnum.getX(), this.skinPartEnum.getY(), this.skinPartEnum.getWidth(), this.skinPartEnum.getHeight());
|
|
||||||
|
|
||||||
// Scale the image
|
|
||||||
BufferedImage scaledImage = new BufferedImage(size, size, partImage.getType());
|
|
||||||
Graphics2D graphics2D = scaledImage.createGraphics();
|
|
||||||
graphics2D.drawImage(partImage, 0, 0, size, size, null);
|
|
||||||
graphics2D.dispose();
|
|
||||||
partImage = scaledImage;
|
|
||||||
|
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
||||||
ImageIO.write(partImage, "png", byteArrayOutputStream);
|
|
||||||
this.partBytes = byteArrayOutputStream.toByteArray();
|
|
||||||
return this.partBytes;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
log.error("Failed to read image from skin data.", ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -49,7 +49,7 @@ public class PlayerService {
|
|||||||
UUID uuid = null;
|
UUID uuid = null;
|
||||||
if (id.length() == 32 || id.length() == 36) { // Check if the id is a UUID
|
if (id.length() == 32 || id.length() == 36) { // Check if the id is a UUID
|
||||||
try {
|
try {
|
||||||
uuid = UUID.fromString(id.length() == 32 ? UUIDUtils.addUUIDDashes(id) : id);
|
uuid = UUID.fromString(id.length() == 32 ? UUIDUtils.addUuidDashes(id) : id);
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
} else { // Check if the id is a name
|
} else { // Check if the id is a name
|
||||||
uuid = playerNameToUUIDCache.get(id.toUpperCase());
|
uuid = playerNameToUUIDCache.get(id.toUpperCase());
|
||||||
@ -68,7 +68,7 @@ public class PlayerService {
|
|||||||
}
|
}
|
||||||
// Get the profile of the player using their UUID
|
// Get the profile of the player using their UUID
|
||||||
profile = mojangAPIService.getProfile(apiProfile.getId().length() == 32 ?
|
profile = mojangAPIService.getProfile(apiProfile.getId().length() == 32 ?
|
||||||
UUIDUtils.addUUIDDashes(apiProfile.getId()) : apiProfile.getId());
|
UUIDUtils.addUuidDashes(apiProfile.getId()) : apiProfile.getId());
|
||||||
}
|
}
|
||||||
if (profile == null) { // The player cannot be found using their name or UUID
|
if (profile == null) { // The player cannot be found using their name or UUID
|
||||||
log.info("Player with id {} could not be found", id);
|
log.info("Player with id {} could not be found", id);
|
||||||
|
@ -4,12 +4,14 @@ import cc.fascinated.Main;
|
|||||||
import cc.fascinated.model.player.Cape;
|
import cc.fascinated.model.player.Cape;
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
import cc.fascinated.util.Tuple;
|
import cc.fascinated.util.Tuple;
|
||||||
|
import cc.fascinated.util.UUIDUtils;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Getter @NoArgsConstructor
|
@Getter @NoArgsConstructor
|
||||||
@ -36,36 +38,35 @@ public class MojangProfile {
|
|||||||
* @return the skin and cape of the player
|
* @return the skin and cape of the player
|
||||||
*/
|
*/
|
||||||
public Tuple<Skin, Cape> getSkinAndCape() {
|
public Tuple<Skin, Cape> getSkinAndCape() {
|
||||||
ProfileProperty textureProperty = getTextureProperty();
|
ProfileProperty textureProperty = getProfileProperty("textures");
|
||||||
if (textureProperty == null) {
|
if (textureProperty == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the texture property
|
JsonObject json = Main.GSON.fromJson(textureProperty.getDecodedValue(), JsonObject.class); // Decode the texture property
|
||||||
String decoded = new String(java.util.Base64.getDecoder().decode(textureProperty.getValue()));
|
JsonObject texturesJson = json.getAsJsonObject("textures"); // Parse the decoded JSON and get the textures object
|
||||||
|
|
||||||
// Parse the decoded JSON
|
return new Tuple<>(Skin.fromJson(texturesJson.getAsJsonObject("SKIN")).populatePartUrls(this.getFormattedUuid()),
|
||||||
JsonObject json = Main.getGSON().fromJson(decoded, JsonObject.class);
|
Cape.fromJson(texturesJson.getAsJsonObject("CAPE")));
|
||||||
JsonObject texturesJson = json.getAsJsonObject("textures");
|
|
||||||
JsonObject skinJson = texturesJson.getAsJsonObject("SKIN");
|
|
||||||
JsonObject capeJson = texturesJson.getAsJsonObject("CAPE");
|
|
||||||
JsonObject metadataJson = skinJson.get("metadata").getAsJsonObject();
|
|
||||||
|
|
||||||
Skin skin = new Skin(id, skinJson.get("url").getAsString(),
|
|
||||||
Skin.SkinType.valueOf(metadataJson.get("model").getAsString().toUpperCase()));
|
|
||||||
Cape cape = new Cape(capeJson.get("url").getAsString());
|
|
||||||
|
|
||||||
return new Tuple<>(skin, cape);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the texture property of the player.
|
* Gets the formatted UUID of the player.
|
||||||
*
|
*
|
||||||
* @return the texture property
|
* @return the formatted UUID
|
||||||
*/
|
*/
|
||||||
public ProfileProperty getTextureProperty() {
|
public String getFormattedUuid() {
|
||||||
|
return id.length() == 32 ? UUIDUtils.addUuidDashes(id) : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a profile property for the player
|
||||||
|
*
|
||||||
|
* @return the profile property
|
||||||
|
*/
|
||||||
|
public ProfileProperty getProfileProperty(String name) {
|
||||||
for (ProfileProperty property : properties) {
|
for (ProfileProperty property : properties) {
|
||||||
if (property.getName().equals("textures")) {
|
if (property.getName().equals(name)) {
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +90,15 @@ public class MojangProfile {
|
|||||||
*/
|
*/
|
||||||
private String signature;
|
private String signature;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the value for this property.
|
||||||
|
*
|
||||||
|
* @return the decoded value
|
||||||
|
*/
|
||||||
|
public String getDecodedValue() {
|
||||||
|
return new String(Base64.getDecoder().decode(this.value));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the property is signed.
|
* Check if the property is signed.
|
||||||
*
|
*
|
||||||
|
71
src/main/java/cc/fascinated/util/PlayerUtils.java
Normal file
71
src/main/java/cc/fascinated/util/PlayerUtils.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package cc.fascinated.util;
|
||||||
|
|
||||||
|
import cc.fascinated.Main;
|
||||||
|
import cc.fascinated.model.player.Skin;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import lombok.extern.log4j.Log4j2;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
|
||||||
|
@UtilityClass @Log4j2
|
||||||
|
public class PlayerUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the skin data from the URL.
|
||||||
|
*
|
||||||
|
* @return the skin data
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@JsonIgnore
|
||||||
|
public static byte[] getSkinData(String url) {
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(new URI(url))
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Main.HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofByteArray()).body();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the part data from the skin.
|
||||||
|
*
|
||||||
|
* @return the part data
|
||||||
|
*/
|
||||||
|
public static byte[] getSkinPartBytes(Skin skin, Skin.Parts part, int size) {
|
||||||
|
if (size == -1) {
|
||||||
|
size = part.getDefaultSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
BufferedImage image = ImageIO.read(new ByteArrayInputStream(PlayerUtils.getSkinData(skin.getUrl())));
|
||||||
|
if (image == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Get the part of the image (e.g. the head)
|
||||||
|
BufferedImage partImage = image.getSubimage(part.getX(), part.getY(), part.getWidth(), part.getHeight());
|
||||||
|
|
||||||
|
// Scale the image
|
||||||
|
BufferedImage scaledImage = new BufferedImage(size, size, partImage.getType());
|
||||||
|
Graphics2D graphics2D = scaledImage.createGraphics();
|
||||||
|
graphics2D.drawImage(partImage, 0, 0, size, size, null);
|
||||||
|
graphics2D.dispose();
|
||||||
|
partImage = scaledImage;
|
||||||
|
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(partImage, "png", byteArrayOutputStream);
|
||||||
|
return byteArrayOutputStream.toByteArray();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Failed to get {} part bytes for {}", part.name(), skin.getUrl(), ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ public class UUIDUtils {
|
|||||||
* @param idNoDashes the UUID without dashes
|
* @param idNoDashes the UUID without dashes
|
||||||
* @return the UUID with dashes
|
* @return the UUID with dashes
|
||||||
*/
|
*/
|
||||||
public static String addUUIDDashes(String idNoDashes) {
|
public static String addUuidDashes(String idNoDashes) {
|
||||||
StringBuilder idBuff = new StringBuilder(idNoDashes);
|
StringBuilder idBuff = new StringBuilder(idNoDashes);
|
||||||
idBuff.insert(20, '-');
|
idBuff.insert(20, '-');
|
||||||
idBuff.insert(16, '-');
|
idBuff.insert(16, '-');
|
||||||
|
Loading…
Reference in New Issue
Block a user