package launcher.client; import launcher.LauncherAPI; import launcher.helper.IOHelper; import launcher.helper.SecurityHelper; import launcher.helper.SecurityHelper.DigestAlgorithm; import launcher.helper.VerifyHelper; import launcher.serialize.HInput; import launcher.serialize.HOutput; import launcher.serialize.stream.StreamObject; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Objects; import java.util.UUID; public final class PlayerProfile extends StreamObject { @LauncherAPI public final UUID uuid; @LauncherAPI public final String username; @LauncherAPI public final Texture skin, cloak; @LauncherAPI public PlayerProfile(HInput input) throws IOException { uuid = input.readUUID(); username = VerifyHelper.verifyUsername(input.readString(64)); skin = input.readBoolean() ? new Texture(input) : null; cloak = input.readBoolean() ? new Texture(input) : null; } @LauncherAPI public PlayerProfile(UUID uuid, String username, Texture skin, Texture cloak) { this.uuid = Objects.requireNonNull(uuid, "uuid"); this.username = VerifyHelper.verifyUsername(username); this.skin = skin; this.cloak = cloak; } @LauncherAPI public static PlayerProfile newOfflineProfile(String username) { return new PlayerProfile(offlineUUID(username), username, null, null); } @LauncherAPI public static UUID offlineUUID(String username) { return UUID.nameUUIDFromBytes(IOHelper.encodeASCII("OfflinePlayer:" + username)); } @Override public void write(HOutput output) throws IOException { output.writeUUID(uuid); output.writeString(username, 64); // Write textures output.writeBoolean(skin != null); if (skin != null) { skin.write(output); } output.writeBoolean(cloak != null); if (cloak != null) { cloak.write(output); } } public static final class Texture extends StreamObject { private static final DigestAlgorithm DIGEST_ALGO = DigestAlgorithm.SHA256; // Instance @LauncherAPI public final String url; @LauncherAPI public final byte[] digest; @LauncherAPI public Texture(String url, byte[] digest) { this.url = IOHelper.verifyURL(url); this.digest = Objects.requireNonNull(digest, "digest"); } @LauncherAPI public Texture(String url, boolean cloak) throws IOException { this.url = IOHelper.verifyURL(url); // Fetch texture byte[] texture; try (InputStream input = IOHelper.newInput(new URL(url))) { texture = IOHelper.read(input); } try (ByteArrayInputStream input = new ByteArrayInputStream(texture)) { IOHelper.readTexture(input, cloak); // Verify texture } // Get digest of texture digest = SecurityHelper.digest(DIGEST_ALGO, new URL(url)); } @LauncherAPI public Texture(HInput input) throws IOException { url = IOHelper.verifyURL(input.readASCII(2048)); digest = input.readByteArray(-DIGEST_ALGO.bytes); } @Override public void write(HOutput output) throws IOException { output.writeASCII(url, 2048); output.writeByteArray(digest, -DIGEST_ALGO.bytes); } } }