package launchserver.texture; import com.eclipsesource.json.Json; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; import launcher.LauncherAPI; import launcher.client.PlayerProfile.Texture; import launcher.helper.IOHelper; import launcher.helper.JVMHelper; import launcher.helper.LogHelper; import launcher.helper.VerifyHelper; import launcher.serialize.config.entry.BlockConfigEntry; import launchserver.helpers.HTTPRequestHelper; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.UUID; // Пардон, но я их рот топтал // Я лучше вынесу полный класс с логикой сюда, чтобы это все не пошло по косяку public class ElyByTextureProvider extends TextureProvider { @LauncherAPI public static final long CACHE_DURATION_MS = VerifyHelper.verifyLong( Long.parseLong(System.getProperty("launcher.mysql.cacheDurationHours", Integer.toString(24))), VerifyHelper.L_NOT_NEGATIVE, "launcher.mysql.cacheDurationHours can't be < 0") * 60L * 60L * 1000L; // Instance private final Map<String, CacheData> cache = new HashMap<>(1024); public ElyByTextureProvider(BlockConfigEntry block) { super(block); } @Override public void close() { // Do nothing } private CacheData getCached(String username) { CacheData result = cache.get(username); // Have cached result? if (result != null && System.currentTimeMillis() < result.until) { if (result.exc != null) { JVMHelper.UNSAFE.throwException(result.exc); } return result; } try { // TODO Don't query UUID by username if using mojang auth handler (not implemented yet) URL uuidURL = new URL("https://authserver.ely.by/api/users/profiles/minecraft/" + IOHelper.urlEncode(username)); JsonObject uuidResponse = HTTPRequestHelper.makeAuthlibRequest(uuidURL, null, "ElyBy"); if (uuidResponse == null) { throw new IllegalArgumentException("Empty UUID response"); } String uuidResolved = uuidResponse.get("id").asString(); // Obtain player profile URL profileURL = new URL("http://skinsystem.ely.by/profile/" + IOHelper.urlEncode(username)); // ... // Я просто выпилю эту систему, если еще такие приколы найду: // https://wiki.vg/Mojang_API#UUID_to_Profile_and_Skin.2FCape // https://docs.ely.by/ru/skins-system.html => /profile/{nickname} JsonObject profileResponse = HTTPRequestHelper.makeAuthlibRequest(profileURL, null, "ElyBy"); if (profileResponse == null) { throw new IllegalArgumentException("Empty Authlib response"); } JsonArray properties = (JsonArray) profileResponse.get("properties"); if (properties == null) { LogHelper.subDebug("No properties"); return cache(username, null, null, null); } // Find textures property JsonObject texturesProperty = null; for (JsonValue property : properties) { JsonObject property0 = property.asObject(); if (property0.get("name").asString().equals("textures")) { byte[] asBytes = Base64.getDecoder().decode(property0.get("value").asString()); String asString = new String(asBytes, StandardCharsets.UTF_8); texturesProperty = Json.parse(asString).asObject(); break; } } if (texturesProperty == null) { LogHelper.subDebug("No textures property"); return cache(username, null, null, null); } // Extract skin&cloak texture texturesProperty = (JsonObject) texturesProperty.get("textures"); JsonObject skinProperty = (JsonObject) texturesProperty.get("SKIN"); Texture skinTexture = skinProperty == null ? null : new Texture(skinProperty.get("url").asString(), false); JsonObject cloakProperty = (JsonObject) texturesProperty.get("CAPE"); Texture cloakTexture = cloakProperty == null ? null : new Texture(cloakProperty.get("url").asString(), true); // We're done return cache(username, skinTexture, cloakTexture, null); } catch (Throwable exc) { cache(username, null, null, exc); JVMHelper.UNSAFE.throwException(exc); } // We're dones return result; } private CacheData cache(String username, Texture skin, Texture cloak, Throwable exc) { long until = CACHE_DURATION_MS == 0L ? Long.MIN_VALUE : System.currentTimeMillis() + CACHE_DURATION_MS; CacheData data = exc == null ? new CacheData(skin, cloak, until) : new CacheData(exc, until); if (CACHE_DURATION_MS != 0L) { cache.put(username, data); } return data; } private static final class CacheData { protected final Texture skin, cloak; protected final Throwable exc; protected final long until; private CacheData(Texture skin, Texture cloak, long until) { this.skin = skin; this.cloak = cloak; this.until = until; exc = null; } private CacheData(Throwable exc, long until) { this.exc = exc; this.until = until; skin = cloak = null; } } @Override public synchronized Texture getSkinTexture(UUID uuid, String username) { return getCached(username).skin; } @Override public synchronized Texture getCloakTexture(UUID uuid, String username) { return getCached(username).cloak; } }