1
0
mirror of https://github.com/Suiranoil/SkinRestorer.git synced 2026-01-16 04:42:12 +00:00

add caching to skin providers

This commit is contained in:
2024-08-29 20:31:35 +03:00
parent 9e0c41482f
commit 3a8a513180
8 changed files with 93 additions and 30 deletions

View File

@@ -118,7 +118,7 @@ public final class SkinRestorer {
boolean save
) {
return CompletableFuture.supplyAsync(
() -> SkinRestorer.getProvider(context.name()).map(provider -> provider.getSkin(context.argument(), context.variant()))
() -> SkinRestorer.getProvider(context.name()).map(provider -> provider.fetchSkin(context.argument(), context.variant()))
)
.thenApplyAsync(result -> {
if (result.isEmpty())

View File

@@ -73,7 +73,7 @@ public abstract class ServerLoginPacketListenerImplMixin {
SkinRestorer.LOGGER.debug("fetching {}'s skin", profile.getName());
var result = SkinRestorer.getProvider(context.name()).map(
provider -> provider.getSkin(context.argument(), context.variant())
provider -> provider.fetchSkin(context.argument(), context.variant())
).orElseGet(() -> Result.error(new IllegalArgumentException("skin provider is not registered: " + context.name())));
if (!result.isError()) {

View File

@@ -1,5 +1,8 @@
package net.lionarius.skinrestorer.skin.provider;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse;
@@ -9,12 +12,15 @@ import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.WebUtils;
import net.minecraft.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public final class ElyBySkinProvider implements SkinProvider {
@@ -22,12 +28,23 @@ public final class ElyBySkinProvider implements SkinProvider {
private static final URI API_URI;
private static final LoadingCache<String, Optional<Property>> SKIN_CACHE;
static {
try {
API_URI = new URI("http://skinsystem.ely.by");
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
SKIN_CACHE = CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.build(new CacheLoader<>() {
@Override
public @NotNull Optional<Property> load(@NotNull String key) throws Exception {
return ElyBySkinProvider.loadSkin(key);
}
});
}
@Override
@@ -41,20 +58,25 @@ public final class ElyBySkinProvider implements SkinProvider {
}
@Override
public Result<Optional<Property>, Exception> getSkin(String username, SkinVariant variant) {
if (!StringUtil.isValidPlayerName(username))
return Result.error(new IllegalArgumentException("invalid username"));
public Result<Optional<Property>, Exception> fetchSkin(String username, SkinVariant variant) {
try {
var profile = ElyBySkinProvider.getElyByProfile(username);
var textures = PlayerUtils.getPlayerSkin(profile);
if (!StringUtil.isValidPlayerName(username))
throw new IllegalArgumentException("invalid username");
return Result.ofNullable(textures);
var usernameLowerCase = username.toLowerCase(Locale.ROOT);
return Result.success(SKIN_CACHE.get(usernameLowerCase));
} catch (Exception e) {
return Result.error(e);
}
}
private static Optional<Property> loadSkin(String username) throws Exception {
var profile = ElyBySkinProvider.getElyByProfile(username);
var textures = PlayerUtils.getPlayerSkin(profile);
return Optional.ofNullable(textures);
}
private static GameProfile getElyByProfile(String username) throws IOException {
var request = HttpRequest.newBuilder()
.uri(ElyBySkinProvider.API_URI

View File

@@ -21,11 +21,11 @@ public final class EmptySkinProvider implements SkinProvider {
}
@Override
public Result<Optional<Property>, Exception> getSkin(String argument, SkinVariant variant) {
return this.getSkin();
public Result<Optional<Property>, Exception> fetchSkin(String argument, SkinVariant variant) {
return this.fetchSkin();
}
public Result<Optional<Property>, Exception> getSkin() {
public Result<Optional<Property>, Exception> fetchSkin() {
return Result.ofNullable(null);
}
}

View File

@@ -1,18 +1,24 @@
package net.lionarius.skinrestorer.skin.provider;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.JsonObject;
import com.mojang.authlib.properties.Property;
import it.unimi.dsi.fastutil.Pair;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.util.JsonUtils;
import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.WebUtils;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public final class MineskinSkinProvider implements SkinProvider {
@@ -20,12 +26,23 @@ public final class MineskinSkinProvider implements SkinProvider {
private static final URI API_URI;
private static final LoadingCache<Pair<URI, SkinVariant>, Optional<Property>> SKIN_CACHE;
static {
try {
API_URI = new URI("https://api.mineskin.org");
} catch (URISyntaxException e) {
throw new IllegalArgumentException(e);
}
SKIN_CACHE = CacheBuilder.newBuilder()
.expireAfterWrite(300, TimeUnit.SECONDS)
.build(new CacheLoader<>() {
@Override
public @NotNull Optional<Property> load(@NotNull Pair<URI, SkinVariant> key) throws Exception {
return MineskinSkinProvider.loadSkin(key.first(), key.second());
}
});
}
@Override
@@ -39,20 +56,24 @@ public final class MineskinSkinProvider implements SkinProvider {
}
@Override
public Result<Optional<Property>, Exception> getSkin(String url, SkinVariant variant) {
public Result<Optional<Property>, Exception> fetchSkin(String url, SkinVariant variant) {
try {
var uri = new URI(url);
var result = MineskinSkinProvider.uploadToMineskin(uri, variant);
var texture = result.getAsJsonObject("data").getAsJsonObject("texture");
var textures = new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString());
return Result.ofNullable(textures);
return Result.success(SKIN_CACHE.get(Pair.of(uri, variant)));
} catch (Exception e) {
return Result.error(e);
}
}
private static Optional<Property> loadSkin(URI uri, SkinVariant variant) throws Exception {
var result = MineskinSkinProvider.uploadToMineskin(uri, variant);
var texture = result.getAsJsonObject("data").getAsJsonObject("texture");
var textures = new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString());
return Optional.of(textures);
}
private static JsonObject uploadToMineskin(URI url, SkinVariant variant) throws IOException {
var body = ("{\"variant\":\"%s\",\"name\":\"%s\",\"visibility\":%d,\"url\":\"%s\"}")
.formatted(variant.toString(), "none", 0, url);

View File

@@ -1,5 +1,8 @@
package net.lionarius.skinrestorer.skin.provider;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse;
@@ -12,6 +15,7 @@ import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.WebUtils;
import net.minecraft.server.players.GameProfileCache;
import net.minecraft.util.StringUtil;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.URI;
@@ -19,6 +23,7 @@ import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public final class MojangSkinProvider implements SkinProvider {
@@ -27,9 +32,11 @@ public final class MojangSkinProvider implements SkinProvider {
private static final URI API_URI;
private static final URI SESSION_SERVER_URI;
public static final String CACHE_FILENAME = "mojang_profile_cache.json";
public static final String PROFILE_CACHE_FILENAME = "mojang_profile_cache.json";
private static final GameProfileCache PROFILE_CACHE;
private static final LoadingCache<UUID, Optional<Property>> SKIN_CACHE;
static {
try {
API_URI = new URI("https://api.mojang.com");
@@ -47,7 +54,16 @@ public final class MojangSkinProvider implements SkinProvider {
callback.onProfileLookupFailed(name, e);
}
}
}, SkinRestorer.getConfigDir().resolve(CACHE_FILENAME).toFile());
}, SkinRestorer.getConfigDir().resolve(PROFILE_CACHE_FILENAME).toFile());
SKIN_CACHE = CacheBuilder.newBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.build(new CacheLoader<>() {
@Override
public @NotNull Optional<Property> load(@NotNull UUID key) throws Exception {
return MojangSkinProvider.loadSkin(key);
}
});
}
public static SkinProviderContext skinProviderContextFromProfile(GameProfile gameProfile) {
@@ -65,24 +81,28 @@ public final class MojangSkinProvider implements SkinProvider {
}
@Override
public Result<Optional<Property>, Exception> getSkin(String username, SkinVariant variant) {
if (!StringUtil.isValidPlayerName(username))
return Result.error(new IllegalArgumentException("invalid username"));
public Result<Optional<Property>, Exception> fetchSkin(String username, SkinVariant variant) {
try {
if (!StringUtil.isValidPlayerName(username))
throw new IllegalArgumentException("invalid username");
var cachedProfile = MojangSkinProvider.PROFILE_CACHE.get(username);
if (cachedProfile.isEmpty())
throw new IllegalArgumentException("no profile found for " + username);
var profile = MojangSkinProvider.getProfileWithProperties(cachedProfile.get().getId());
var textures = PlayerUtils.getPlayerSkin(profile);
return Result.ofNullable(textures);
return Result.success(SKIN_CACHE.get(cachedProfile.get().getId()));
} catch (Exception e) {
return Result.error(e);
}
}
private static Optional<Property> loadSkin(UUID uuid) throws Exception {
var profile = MojangSkinProvider.getProfileWithProperties(uuid);
var textures = PlayerUtils.getPlayerSkin(profile);
return Optional.ofNullable(textures);
}
private static GameProfile getProfile(final String name) throws IOException {
var request = HttpRequest.newBuilder()
.uri(MojangSkinProvider.API_URI

View File

@@ -16,5 +16,5 @@ public interface SkinProvider {
boolean hasVariantSupport();
Result<Optional<Property>, Exception> getSkin(String argument, SkinVariant variant);
Result<Optional<Property>, Exception> fetchSkin(String argument, SkinVariant variant);
}

View File

@@ -26,7 +26,7 @@ public final class FileUtils {
return Files.isRegularFile(file)
&& !name.startsWith(Translation.LEGACY_TRANSLATION_FILENAME)
&& !name.startsWith(Config.CONFIG_FILENAME)
&& !name.startsWith(MojangSkinProvider.CACHE_FILENAME)
&& !name.startsWith(MojangSkinProvider.PROFILE_CACHE_FILENAME)
&& name.endsWith(SkinIO.FILE_EXTENSION);
}).toList();