forked from MinecraftUtilities/Backend
wow! avatars!!
This commit is contained in:
parent
50265b6228
commit
0819b228ba
18
src/main/java/cc/fascinated/Consts.java
Normal file
18
src/main/java/cc/fascinated/Consts.java
Normal file
@ -0,0 +1,18 @@
|
||||
package cc.fascinated;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
public class Consts {
|
||||
|
||||
@Getter
|
||||
private static String SITE_URL;
|
||||
|
||||
@Value("${site-url}")
|
||||
public void setSiteUrl(String name) {
|
||||
SITE_URL = name;
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import lombok.Getter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.Helper;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
|
@ -2,13 +2,15 @@ package cc.fascinated.api.controller;
|
||||
|
||||
import cc.fascinated.player.PlayerManagerService;
|
||||
import cc.fascinated.player.impl.Player;
|
||||
import cc.fascinated.player.impl.Skin;
|
||||
import cc.fascinated.player.impl.SkinPart;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@RequestMapping(value = "/")
|
||||
public class PlayerController {
|
||||
|
||||
private final PlayerManagerService playerManagerService;
|
||||
@ -18,7 +20,7 @@ public class PlayerController {
|
||||
this.playerManagerService = playerManagerService;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}") @ResponseBody
|
||||
@GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE) @ResponseBody
|
||||
public ResponseEntity<Player> getPlayer(@PathVariable String id) {
|
||||
Player player = playerManagerService.getPlayer(id);
|
||||
if (player == null) {
|
||||
@ -26,4 +28,17 @@ public class PlayerController {
|
||||
}
|
||||
return ResponseEntity.ok(player);
|
||||
}
|
||||
|
||||
@GetMapping(value = "/avatar/{id}")
|
||||
public ResponseEntity<byte[]> getPlayerHead(@PathVariable String id) {
|
||||
Player player = playerManagerService.getPlayer(id);
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
Skin skin = player.getSkin();
|
||||
SkinPart head = skin.getHead();
|
||||
return ResponseEntity.ok()
|
||||
.contentType(MediaType.IMAGE_PNG)
|
||||
.body(head.getPartData());
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package cc.fascinated.mojang;
|
||||
import cc.fascinated.Main;
|
||||
import cc.fascinated.mojang.types.MojangApiProfile;
|
||||
import cc.fascinated.mojang.types.MojangSessionServerProfile;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -35,7 +36,7 @@ public class MojangAPIService {
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = Main.getCLIENT().send(request, HttpResponse.BodyHandlers.ofString());
|
||||
return Main.getGSON().fromJson(response.body(), MojangSessionServerProfile.class);
|
||||
return Main.getGSON().fromJson(response.body(), new TypeToken<MojangSessionServerProfile>(){}.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,6 +53,6 @@ public class MojangAPIService {
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = Main.getCLIENT().send(request, HttpResponse.BodyHandlers.ofString());
|
||||
return Main.getGSON().fromJson(response.body(), MojangApiProfile.class);
|
||||
return Main.getGSON().fromJson(response.body(), new TypeToken<MojangApiProfile>(){}.getType());
|
||||
}
|
||||
}
|
||||
|
@ -10,4 +10,13 @@ public class MojangApiProfile {
|
||||
private String name;
|
||||
|
||||
public MojangApiProfile() {}
|
||||
|
||||
/**
|
||||
* Check if the profile is valid.
|
||||
*
|
||||
* @return if the profile is valid
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return id != null && name != null;
|
||||
}
|
||||
}
|
||||
|
@ -5,17 +5,21 @@ import cc.fascinated.mojang.types.MojangApiProfile;
|
||||
import cc.fascinated.mojang.types.MojangSessionServerProfile;
|
||||
import cc.fascinated.player.impl.Player;
|
||||
import cc.fascinated.util.UUIDUtils;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
import net.jodah.expiringmap.ExpirationPolicy;
|
||||
import net.jodah.expiringmap.ExpiringMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
@Service @Log4j2
|
||||
public class PlayerManagerService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PlayerManagerService.class);
|
||||
/**
|
||||
* The cache of players.
|
||||
*/
|
||||
@ -59,12 +63,13 @@ public class PlayerManagerService {
|
||||
MojangSessionServerProfile profile = uuid == null ? null : mojangAPIService.getSessionServerProfile(uuid.toString());
|
||||
if (profile == null) {
|
||||
MojangApiProfile apiProfile = mojangAPIService.getApiProfile(id);
|
||||
if (apiProfile == null) {
|
||||
if (apiProfile == null || !apiProfile.isValid()) {
|
||||
return null;
|
||||
}
|
||||
profile = mojangAPIService.getSessionServerProfile(apiProfile.getId().length() == 32 ? UUIDUtils.addUUIDDashes(apiProfile.getId()) : apiProfile.getId());
|
||||
}
|
||||
if (profile == null) { // The player cannot be found using their name or UUID
|
||||
log.info("Player with id {} could not be found", id);
|
||||
return null;
|
||||
}
|
||||
Player player = new Player(profile);
|
||||
|
@ -1,11 +1,13 @@
|
||||
package cc.fascinated.player.impl;
|
||||
|
||||
import cc.fascinated.Consts;
|
||||
import cc.fascinated.Main;
|
||||
import cc.fascinated.mojang.types.MojangSessionServerProfile;
|
||||
import cc.fascinated.mojang.types.MojangSessionServerProfileProperties;
|
||||
import cc.fascinated.util.UUIDUtils;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.Getter;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@ -22,6 +24,11 @@ public class Player {
|
||||
*/
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* The avatar URL of the player
|
||||
*/
|
||||
private final String avatarUrl;
|
||||
|
||||
/**
|
||||
* The skin of the player
|
||||
* <p>
|
||||
@ -41,6 +48,7 @@ public class Player {
|
||||
public Player(MojangSessionServerProfile profile) {
|
||||
this.uuid = UUID.fromString(UUIDUtils.addUUIDDashes(profile.getId()));
|
||||
this.name = profile.getName();
|
||||
this.avatarUrl = Consts.getSITE_URL() + "/avatar/" + this.uuid;
|
||||
|
||||
MojangSessionServerProfileProperties textureProperty = profile.getTextureProperty();
|
||||
if (textureProperty == null) {
|
||||
|
@ -1,9 +1,16 @@
|
||||
package cc.fascinated.player.impl;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import cc.fascinated.Main;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
@Getter @AllArgsConstructor
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
|
||||
@Getter
|
||||
public class Skin {
|
||||
|
||||
/**
|
||||
@ -15,4 +22,40 @@ public class Skin {
|
||||
* The model of the skin
|
||||
*/
|
||||
private final SkinType model;
|
||||
|
||||
/**
|
||||
* The bytes of the skin
|
||||
*/
|
||||
@JsonIgnore
|
||||
private final byte[] skinBytes;
|
||||
|
||||
/**
|
||||
* The head of the skin
|
||||
*/
|
||||
@JsonIgnore
|
||||
private final SkinPart head;
|
||||
|
||||
public Skin(String url, SkinType model) {
|
||||
this.url = url;
|
||||
this.model = model;
|
||||
this.skinBytes = this.getSkinData();
|
||||
|
||||
// The skin parts
|
||||
this.head = new SkinPart(this.skinBytes, 8, 8, 8, 8, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
|
95
src/main/java/cc/fascinated/player/impl/SkinPart.java
Normal file
95
src/main/java/cc/fascinated/player/impl/SkinPart.java
Normal file
@ -0,0 +1,95 @@
|
||||
package cc.fascinated.player.impl;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.DataBufferByte;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
@Getter @Log4j2
|
||||
public class SkinPart {
|
||||
|
||||
/**
|
||||
* The whole skin data.
|
||||
*/
|
||||
private final byte[] data;
|
||||
|
||||
/**
|
||||
* The X coordinate of the part.
|
||||
*/
|
||||
private final int x;
|
||||
|
||||
/**
|
||||
* The Y coordinate of the part.
|
||||
*/
|
||||
private final int y;
|
||||
|
||||
/**
|
||||
* The width of the part.
|
||||
*/
|
||||
private final int width;
|
||||
|
||||
/**
|
||||
* The height of the part.
|
||||
*/
|
||||
private final int height;
|
||||
|
||||
/**
|
||||
* The scale of the part output.
|
||||
*/
|
||||
private final int scale;
|
||||
|
||||
/**
|
||||
* The part data from the skin.
|
||||
*/
|
||||
private byte[] partBytes;
|
||||
|
||||
public SkinPart(byte[] data, int x, int y, int width, int height, int scale) {
|
||||
this.data = data;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the part data from the skin.
|
||||
*
|
||||
* @return the part data
|
||||
*/
|
||||
public byte[] getPartData() {
|
||||
if (this.partBytes != null) {
|
||||
return this.partBytes;
|
||||
}
|
||||
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(new ByteArrayInputStream(this.data));
|
||||
if (image == null) {
|
||||
return null;
|
||||
}
|
||||
BufferedImage partImage = image.getSubimage(this.x, this.y, this.width, this.height);
|
||||
|
||||
// Scale the image
|
||||
int width = partImage.getWidth() * this.scale;
|
||||
int height = partImage.getHeight() * this.scale;
|
||||
BufferedImage scaledImage = new BufferedImage(width, height, partImage.getType());
|
||||
Graphics2D graphics2D = scaledImage.createGraphics();
|
||||
graphics2D.drawImage(partImage, 0, 0, width, height, 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,8 @@ server:
|
||||
address: 0.0.0.0
|
||||
port: 7500
|
||||
|
||||
public-url: http://localhost:7500
|
||||
|
||||
mojang:
|
||||
session-server: https://sessionserver.mojang.com
|
||||
api: https://api.mojang.com
|
Loading…
Reference in New Issue
Block a user