From f36390a3d57224fc25c960ef4353bdc810606cbd Mon Sep 17 00:00:00 2001 From: Lionarius Date: Sat, 4 Oct 2025 22:12:25 +0300 Subject: [PATCH 01/13] port to 1.21.9 --- build.gradle | 2 +- .../lionarius/skinrestorer/SkinRestorer.java | 27 ++++---- .../skinrestorer/command/SkinCommand.java | 44 +++++++----- .../skinshuffle/SkinShuffleCompatibility.java | 2 +- .../mixin/GameProfileCacheAccessor.java | 14 ---- .../skinrestorer/mixin/PlayerAccessor.java | 15 ++++ .../skinrestorer/mixin/PlayerListMixin.java | 2 +- .../ServerLoginPacketListenerImplMixin.java | 16 ++--- .../mixin/SkullBlockEntityMixin.java | 69 ------------------- .../skin/provider/ElyBySkinProvider.java | 2 +- .../skin/provider/MojangSkinProvider.java | 27 ++++---- .../skinrestorer/util/JsonUtils.java | 2 - .../skinrestorer/util/PlayerUtils.java | 24 ++++--- .../resources/META-INF/accesstransformer.cfg | 1 - .../main/resources/skinrestorer.accesswidener | 2 - .../main/resources/skinrestorer.mixins.json | 3 +- fabric/gradle.properties | 2 +- .../skinshuffle/SkinShuffleCompatibility.java | 2 +- forge/gradle.properties | 6 +- .../skinshuffle/SkinShufflePacketHandler.java | 2 +- gradle.properties | 10 +-- neoforge/build.gradle | 2 - neoforge/gradle.properties | 2 +- .../SkinShuffleModEventHandler.java | 3 +- 24 files changed, 112 insertions(+), 169 deletions(-) delete mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java delete mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java diff --git a/build.gradle b/build.gradle index b092859..5be6436 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { // see https://fabricmc.net/develop/ for new versions - id 'fabric-loom' version '1.10-SNAPSHOT' apply false + id 'fabric-loom' version '1.11-SNAPSHOT' apply false // see https://projects.neoforged.net/neoforged/moddevgradle for new versions id 'net.neoforged.moddev' version '2.0.+' apply false // see https://files.minecraftforge.net/net/minecraftforge/gradle/ForgeGradle/ for new versions diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index f2270f1..2296958 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -1,12 +1,12 @@ package net.lionarius.skinrestorer; import com.google.common.base.Throwables; -import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; import net.lionarius.skinrestorer.command.SkinCommand; import net.lionarius.skinrestorer.config.Config; import net.lionarius.skinrestorer.config.provider.BuiltInProviderConfig; import net.lionarius.skinrestorer.exception.TransparentException; +import net.lionarius.skinrestorer.mixin.PlayerAccessor; import net.lionarius.skinrestorer.platform.Services; import net.lionarius.skinrestorer.skin.SkinIO; import net.lionarius.skinrestorer.skin.SkinStorage; @@ -108,23 +108,26 @@ public final class SkinRestorer { MineskinSkinProvider.reload(); } - public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value, boolean save) { + public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value, boolean save) { var acceptedPlayers = new HashSet(); - for (var profile : targets) { - if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) - value = value.setOriginalValue(PlayerUtils.getPlayerSkin(profile)); + for (var player : targets) { + var profile = player.getGameProfile(); + var skin = PlayerUtils.getPlayerSkin(profile); - if (PlayerUtils.areSkinPropertiesEquals(value.value(), PlayerUtils.getPlayerSkin(profile))) + if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) + value = value.setOriginalValue(skin); + + if (PlayerUtils.areSkinPropertiesEquals(value.value(), skin)) continue; if (save) - SkinRestorer.getSkinStorage().setSkin(profile.getId(), value); + SkinRestorer.getSkinStorage().setSkin(profile.id(), value); - PlayerUtils.applyRestoredSkin(profile, value.value()); + var newProfile = PlayerUtils.applyRestoredSkin(profile, value.value()); + ((PlayerAccessor) player).setGameProfile(newProfile); - var player = server.getPlayerList().getPlayer(profile.getId()); - if (player == null) + if (player.connection == null) continue; PlayerUtils.refreshPlayer(player); @@ -136,13 +139,13 @@ public final class SkinRestorer { return acceptedPlayers; } - public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value) { + public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value) { return SkinRestorer.applySkin(server, targets, value, true); } public static CompletableFuture, String>> setSkinAsync( MinecraftServer server, - Collection targets, + Collection targets, SkinProviderContext context, boolean save ) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java index 8dd93ea..c2ab49d 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java +++ b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java @@ -1,6 +1,5 @@ package net.lionarius.skinrestorer.command; -import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; @@ -18,10 +17,12 @@ import net.lionarius.skinrestorer.util.PlayerUtils; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.NameAndId; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -72,37 +73,41 @@ public final class SkinCommand { SkinProviderContext context = null; var save = true; - if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { - if (profile.getProperties().containsKey(PlayerUtils.TEXTURES_KEY)) { + if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) { + if (profile.properties().containsKey(PlayerUtils.TEXTURES_KEY)) { save = false; context = MojangSkinProvider.skinProviderContextFromProfile(profile); } } else { - context = SkinRestorer.getSkinStorage().getSkin(profile.getId()).toProviderContext(); + context = SkinRestorer.getSkinStorage().getSkin(profile.id()).toProviderContext(); } if (context == null) return 0; - return SkinCommand.setSubcommand(src, Collections.singleton(profile), context, save, false); + return SkinCommand.setSubcommand(src, Collections.singleton(new NameAndId(profile)), context, save, false); } private static int resetSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, boolean setByOperator ) { var updatedPlayers = new HashSet(); - for (var profile : targets) { + for (var nameAndId : targets) { SkinValue skin = null; - if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) - skin = SkinRestorer.getSkinStorage().getSkin(profile.getId()).replaceValueWithOriginal(); + if (SkinRestorer.getSkinStorage().hasSavedSkin(nameAndId.id())) + skin = SkinRestorer.getSkinStorage().getSkin(nameAndId.id()).replaceValueWithOriginal(); if (skin == null) continue; - var updatedPlayer = SkinRestorer.applySkin(src.getServer(), Collections.singleton(profile), skin, false); - SkinRestorer.getSkinStorage().deleteSkin(profile.getId()); + var player = src.getServer().getPlayerList().getPlayer(nameAndId.id()); + if (player == null) + continue; + + var updatedPlayer = SkinRestorer.applySkin(src.getServer(), Collections.singleton(player), skin, false); + SkinRestorer.getSkinStorage().deleteSkin(nameAndId.id()); updatedPlayers.addAll(updatedPlayer); } @@ -118,19 +123,24 @@ public final class SkinCommand { if (src.getPlayer() == null) return 0; - return resetSubcommand(src, Collections.singleton(src.getPlayer().getGameProfile()), false); + return resetSubcommand(src, Collections.singleton(src.getPlayer().nameAndId()), false); } private static int setSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, SkinProviderContext context, boolean save, boolean setByOperator ) { src.sendSystemMessage(Translation.translatableWithFallback(Translation.COMMAND_SKIN_LOADING_KEY)); - SkinRestorer.setSkinAsync(src.getServer(), targets, context, save).thenAccept(result -> { + var profileTargets = targets.stream() + .map(nameAndId -> src.getServer().getPlayerList().getPlayer(nameAndId.id())) + .filter(Objects::nonNull) + .toList(); + + SkinRestorer.setSkinAsync(src.getServer(), profileTargets, context, save).thenAccept(result -> { if (result.isError()) { src.sendFailure(Translation.translatableWithFallback( Translation.COMMAND_SKIN_FAILED_KEY, @@ -149,7 +159,7 @@ public final class SkinCommand { private static int setSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, SkinProviderContext context, boolean setByOperator ) { @@ -163,7 +173,7 @@ public final class SkinCommand { if (src.getPlayer() == null) return 0; - return setSubcommand(src, Collections.singleton(src.getPlayer().getGameProfile()), context, false); + return setSubcommand(src, Collections.singleton(src.getPlayer().nameAndId()), context, false); } private static int configReloadSubcommand(CommandContext context) { @@ -256,7 +266,7 @@ public final class SkinCommand { } private static RequiredArgumentBuilder makeTargetsArgument( - BiFunction, Collection, Integer> consumer + BiFunction, Collection, Integer> consumer ) { return argument("targets", GameProfileArgument.gameProfile()) .requires(source -> source.hasPermission(2)) diff --git a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java index 3b60331..431fe20 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java @@ -43,7 +43,7 @@ public class SkinShuffleCompatibility { server.execute(() -> { SkinRestorer.applySkin( server, - Collections.singleton(player.getGameProfile()), + Collections.singleton(player), new SkinValue(SkinShuffleSkinProvider.PROVIDER_NAME, null, null, property), !server.usesAuthentication() ); diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java deleted file mode 100644 index f129fc3..0000000 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.lionarius.skinrestorer.mixin; - -import net.minecraft.server.players.GameProfileCache; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Map; - -@Mixin(GameProfileCache.class) -public interface GameProfileCacheAccessor { - - @Accessor - Map getProfilesByName(); -} diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java new file mode 100644 index 0000000..61a4d04 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java @@ -0,0 +1,15 @@ +package net.lionarius.skinrestorer.mixin; + +import com.mojang.authlib.GameProfile; +import net.minecraft.world.entity.player.Player; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + + +@Mixin(Player.class) +public interface PlayerAccessor { + @Accessor("gameProfile") + @Mutable + void setGameProfile(GameProfile properties); +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java index ccaed02..4752301 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java @@ -57,6 +57,6 @@ public abstract class PlayerListMixin { @Unique private static void skinrestorer$tryApplySkin(MinecraftServer server, ServerPlayer player) { if (SkinRestorer.getSkinStorage().hasSavedSkin(player.getUUID())) - SkinRestorer.applySkin(server, Collections.singleton(player.getGameProfile()), SkinRestorer.getSkinStorage().getSkin(player.getUUID())); + SkinRestorer.applySkin(server, Collections.singleton(player), SkinRestorer.getSkinStorage().getSkin(player.getUUID())); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java index ca9780c..09a97dc 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java @@ -27,7 +27,7 @@ public abstract class ServerLoginPacketListenerImplMixin { private CompletableFuture skinrestorer$pendingSkin; @Inject(method = "verifyLoginAndFinishConnectionSetup", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/network/chat/Component;"), + target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lnet/minecraft/server/players/NameAndId;)Lnet/minecraft/network/chat/Component;"), cancellable = true) public void waitForSkin(CallbackInfo ci) { if (skinrestorer$pendingSkin == null) { @@ -37,14 +37,14 @@ public abstract class ServerLoginPacketListenerImplMixin { assert profile != null; var originalSkin = PlayerUtils.getPlayerSkin(profile); - if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { + if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) { if (originalSkin != null) { // update to the latest official skin - var value = SkinRestorer.getSkinStorage().getSkin(profile.getId()); - SkinRestorer.getSkinStorage().setSkin(profile.getId(), value.setOriginalValue(originalSkin)); + var value = SkinRestorer.getSkinStorage().getSkin(profile.id()); + SkinRestorer.getSkinStorage().setSkin(profile.id(), value.setOriginalValue(originalSkin)); } if (SkinRestorer.getConfig().refreshSkinOnJoin()) { - var currentSkin = SkinRestorer.getSkinStorage().getSkin(profile.getId()); + var currentSkin = SkinRestorer.getSkinStorage().getSkin(profile.id()); var context = currentSkin.toProviderContext(); skinrestorer$fetchSkin(profile, context); @@ -56,7 +56,7 @@ public abstract class ServerLoginPacketListenerImplMixin { if (originalSkin == null && SkinRestorer.getConfig().fetchSkinOnFirstJoin()) { var context = new SkinProviderContext( SkinRestorer.getConfig().firstJoinSkinProvider().getName(), - profile.getName(), + profile.name(), null ); skinrestorer$fetchSkin(profile, context); @@ -72,7 +72,7 @@ public abstract class ServerLoginPacketListenerImplMixin { @Unique private static void skinrestorer$fetchSkin(GameProfile profile, SkinProviderContext context) { - SkinRestorer.LOGGER.debug("Fetching {}'s skin", profile.getName()); + SkinRestorer.LOGGER.debug("Fetching {}'s skin", profile.name()); var result = SkinRestorer.getProvider(context.name()).map( provider -> provider.fetchSkin(context.argument(), context.variant()) @@ -80,7 +80,7 @@ public abstract class ServerLoginPacketListenerImplMixin { if (!result.isError()) { var value = SkinValue.fromProviderContextWithValue(context, result.getSuccessValue().orElse(null)); - SkinRestorer.getSkinStorage().setSkin(profile.getId(), value); + SkinRestorer.getSkinStorage().setSkin(profile.id(), value); } else { SkinRestorer.LOGGER.warn("Failed to fetch skin '{}:{}'", context.name(), context.argument(), result.getErrorValue()); } diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java deleted file mode 100644 index 21d4d38..0000000 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java +++ /dev/null @@ -1,69 +0,0 @@ -package net.lionarius.skinrestorer.mixin; - -import com.mojang.authlib.GameProfile; -import net.lionarius.skinrestorer.SkinRestorer; -import net.lionarius.skinrestorer.util.PlayerUtils; -import net.minecraft.Util; -import net.minecraft.server.Services; -import net.minecraft.world.level.block.entity.SkullBlockEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Locale; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.function.BooleanSupplier; - -@Mixin(SkullBlockEntity.class) -public abstract class SkullBlockEntityMixin { - - @Inject(method = "fetchProfileByName", at = @At("HEAD"), - cancellable = true) - private static void fetchProfileByName(String name, Services services, CallbackInfoReturnable>> cir) { - if (name == null) - return; - - var profileOpt = Optional.empty(); - var gameProfileInfo = ((GameProfileCacheAccessor) services.profileCache()).getProfilesByName().get(name.toLowerCase(Locale.ROOT)); - - if (gameProfileInfo != null) - profileOpt = Optional.of(gameProfileInfo.getProfile()); - - skinrestorer$replaceSkin(profileOpt, cir); - } - - @Inject(method = "fetchProfileById", at = @At("HEAD"), - cancellable = true) - private static void fetchProfileById(UUID id, Services services, BooleanSupplier cacheUninitialized, CallbackInfoReturnable>> cir) { - if (id == null) - return; - - var profileOpt = services.profileCache().get(id); - - skinrestorer$replaceSkin(profileOpt, cir); - } - - @Unique - private static void skinrestorer$replaceSkin(Optional profileOpt, CallbackInfoReturnable>> cir) { - if (SkinRestorer.getMinecraftServer() == null) - return; - - if (profileOpt.isEmpty()) - return; - - var profile = PlayerUtils.cloneGameProfile(profileOpt.get()); - - if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { - cir.setReturnValue(CompletableFuture.supplyAsync(() -> { - var skin = SkinRestorer.getSkinStorage().getSkin(profile.getId(), false); - PlayerUtils.applyRestoredSkin(profile, skin.value()); - - return Optional.of(profile); - }, Util.backgroundExecutor().forName("getProfile"))); - } - } -} diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java index b8496f1..a8bdfc7 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java @@ -106,6 +106,6 @@ public final class ElyBySkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with name " + username); - return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).toProfile(); + return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).profile(); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java index 56f6bff..b41e10f 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java @@ -8,6 +8,7 @@ import com.mojang.authlib.*; import com.mojang.authlib.properties.Property; import com.mojang.authlib.yggdrasil.YggdrasilEnvironment; import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse; +import com.mojang.authlib.yggdrasil.response.NameAndId; import com.mojang.util.UndashedUuid; import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.exception.TransparentException; @@ -16,7 +17,7 @@ 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 net.minecraft.server.players.GameProfileCache; +import net.minecraft.server.players.CachedUserNameToIdResolver; import net.minecraft.util.StringUtil; import org.jetbrains.annotations.NotNull; @@ -31,13 +32,11 @@ import java.util.concurrent.TimeUnit; public final class MojangSkinProvider implements SkinProvider { public static final String PROVIDER_NAME = "mojang"; - + public static final String PROFILE_CACHE_FILENAME = "mojang_profile_cache.json"; private static final Environment ENVIRONMENT; private static final URI SERVICES_SERVER_URI; private static final URI SESSION_SERVER_URI; - - public static final String PROFILE_CACHE_FILENAME = "mojang_profile_cache.json"; - private static final GameProfileCache PROFILE_CACHE; + private static final CachedUserNameToIdResolver PROFILE_CACHE; private static LoadingCache> SKIN_CACHE; @@ -51,13 +50,13 @@ public final class MojangSkinProvider implements SkinProvider { throw new IllegalArgumentException(e); } - PROFILE_CACHE = new GameProfileCache(new GameProfileRepository() { + PROFILE_CACHE = new CachedUserNameToIdResolver(new GameProfileRepository() { @Override public void findProfilesByNames(String[] names, ProfileLookupCallback callback) { for (var name : names) { try { var profile = MojangSkinProvider.getProfile(name); - callback.onProfileLookupSucceeded(profile); + callback.onProfileLookupSucceeded(profile.name(), profile.id()); } catch (IOException e) { throw new TransparentException(e); } @@ -65,10 +64,10 @@ public final class MojangSkinProvider implements SkinProvider { } @Override - public Optional findProfileByName(String name) { + public Optional findProfileByName(String name) { try { var profile = MojangSkinProvider.getProfile(name); - return Optional.of(profile); + return Optional.of(new NameAndId(profile.id(), profile.name())); } catch (IOException e) { throw new TransparentException(e); } @@ -95,7 +94,7 @@ public final class MojangSkinProvider implements SkinProvider { } public static SkinProviderContext skinProviderContextFromProfile(GameProfile gameProfile) { - return new SkinProviderContext(MojangSkinProvider.PROVIDER_NAME, gameProfile.getName(), null); + return new SkinProviderContext(MojangSkinProvider.PROVIDER_NAME, gameProfile.name(), null); } @Override @@ -118,7 +117,7 @@ public final class MojangSkinProvider implements SkinProvider { if (cachedProfile.isEmpty()) throw new IllegalArgumentException("no profile found for " + username); - return Result.success(SKIN_CACHE.get(cachedProfile.get().getId())); + return Result.success(SKIN_CACHE.get(cachedProfile.get().id())); } catch (UncheckedExecutionException e) { return Result.error((Exception) e.getCause()); } catch (Exception e) { @@ -133,7 +132,7 @@ public final class MojangSkinProvider implements SkinProvider { return Optional.ofNullable(textures); } - private static GameProfile getProfile(final String name) throws IOException { + private static NameAndId getProfile(final String name) throws IOException { var request = HttpRequest.newBuilder() .uri(MojangSkinProvider.SERVICES_SERVER_URI .resolve("/minecraft/profile/lookup/name/") @@ -148,7 +147,7 @@ public final class MojangSkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with name " + name); - return JsonUtils.fromJson(response.body(), GameProfile.class); + return JsonUtils.fromJson(response.body(), NameAndId.class); } private static GameProfile getProfileWithProperties(UUID uuid) throws IOException { @@ -166,6 +165,6 @@ public final class MojangSkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with uuid " + uuid); - return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).toProfile(); + return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).profile(); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java b/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java index 7eef4e1..a3726b3 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java @@ -4,7 +4,6 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import com.mojang.util.UUIDTypeAdapter; @@ -22,7 +21,6 @@ public final class JsonUtils { .registerTypeAdapterFactory(new PostProcessingEnabler()) .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) .registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()) - .registerTypeAdapter(GameProfile.class, new GameProfile.Serializer()) .setPrettyPrinting() .create(); diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java index 5ee7122..031a510 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java @@ -1,12 +1,13 @@ package net.lionarius.skinrestorer.util; import com.google.common.collect.Iterables; +import com.google.common.collect.LinkedHashMultimap; import com.google.gson.JsonObject; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse; import net.lionarius.skinrestorer.mixin.ChunkMapAccessor; -import net.lionarius.skinrestorer.mixin.TrackedEntityAccessorInvoker; import net.minecraft.network.chat.Component; import net.minecraft.network.protocol.game.*; import net.minecraft.server.level.ChunkMap; @@ -97,21 +98,26 @@ public final class PlayerUtils { } public static GameProfile cloneGameProfile(GameProfile profile) { - var newProfile = new GameProfile(profile.getId(), profile.getName()); - newProfile.getProperties().putAll(profile.getProperties()); + var newProfile = new GameProfile(profile.id(), profile.name()); + newProfile.properties().putAll(profile.properties()); return newProfile; } public static Property getPlayerSkin(GameProfile profile) { - return Iterables.getFirst(profile.getProperties().get(TEXTURES_KEY), null); + return Iterables.getFirst(profile.properties().get(TEXTURES_KEY), null); } - public static void applyRestoredSkin(GameProfile profile, Property skin) { - profile.getProperties().removeAll(TEXTURES_KEY); + public static GameProfile applyRestoredSkin(GameProfile profile, Property skin) { + var propertiesMap = profile.properties(); - if (skin != null) - profile.getProperties().put(TEXTURES_KEY, skin); + var newProperties = LinkedHashMultimap.create(propertiesMap); + newProperties.removeAll(TEXTURES_KEY); + if (skin != null) { + newProperties.put(TEXTURES_KEY, skin); + } + + return new GameProfile(profile.id(), profile.name(), new PropertyMap(newProperties)); } public static boolean areSkinPropertiesEquals(Property x, Property y) { @@ -135,7 +141,7 @@ public final class PlayerUtils { public static GameProfile toProfile(MinecraftProfilePropertiesResponse response) { final GameProfile profile = new GameProfile(response.id(), response.name()); - profile.getProperties().putAll(response.properties()); + profile.properties().putAll(response.properties()); return profile; } } diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg index 8c8e31a..e69de29 100644 --- a/common/src/main/resources/META-INF/accesstransformer.cfg +++ b/common/src/main/resources/META-INF/accesstransformer.cfg @@ -1 +0,0 @@ -public net.minecraft.server.players.GameProfileCache$GameProfileInfo diff --git a/common/src/main/resources/skinrestorer.accesswidener b/common/src/main/resources/skinrestorer.accesswidener index 28ef0d1..9ab2173 100644 --- a/common/src/main/resources/skinrestorer.accesswidener +++ b/common/src/main/resources/skinrestorer.accesswidener @@ -1,3 +1 @@ accessWidener v2 named - -accessible class net/minecraft/server/players/GameProfileCache$GameProfileInfo diff --git a/common/src/main/resources/skinrestorer.mixins.json b/common/src/main/resources/skinrestorer.mixins.json index 1956e5a..741efe1 100644 --- a/common/src/main/resources/skinrestorer.mixins.json +++ b/common/src/main/resources/skinrestorer.mixins.json @@ -10,8 +10,7 @@ "PlayerListMixin", "ServerLoginPacketListenerImplMixin", "TrackedEntityAccessorInvoker", - "SkullBlockEntityMixin", - "GameProfileCacheAccessor" + "PlayerAccessor" ], "injectors": { "defaultRequire": 1 diff --git a/fabric/gradle.properties b/fabric/gradle.properties index d4f3a28..7dc2ea4 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,6 +1,6 @@ # Fabric, see https://fabricmc.net/develop/ for new versions fabric_loader_version=0.15.0 -fabric_api_version=0.127.0+1.21.6 +fabric_api_version=0.133.14+1.21.9 optional_dependencies=fabric-api additional_modloaders=quilt diff --git a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java index 662ae14..b8073a4 100644 --- a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java @@ -32,6 +32,6 @@ public final class SkinShuffleCompatibility { } private static void handleSkinRefreshPacket(SkinShuffleSkinRefreshPayload payload, ServerPlayNetworking.Context context) { - net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility.handleSkinRefresh(context.player().getServer(), context.player(), payload); + net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), context.player(), payload); } } diff --git a/forge/gradle.properties b/forge/gradle.properties index 1d49e44..d1e8843 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -1,5 +1,5 @@ # Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=56.0.0 -forge_loader_version_range=[56,) +forge_version=59.0.0 +forge_loader_version_range=[59,) # Forge sometimes skips minor minecraft versions (like 1.20.5) -forge_minecraft_version=1.21.6 +forge_minecraft_version=1.21.9 diff --git a/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java b/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java index badc589..efd5049 100644 --- a/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java +++ b/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java @@ -40,6 +40,6 @@ public class SkinShufflePacketHandler { if (!context.isServerSide() || sender == null) return; - SkinShuffleCompatibility.handleSkinRefresh(sender.getServer(), sender, payload); + SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), sender, payload); } } diff --git a/gradle.properties b/gradle.properties index 11b2125..2ee5965 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,9 @@ group=net.lionarius java_version=21 # Common -minecraft_version=1.21.6 -minecraft_version_list=1.21.6,1.21.7,1.21.8 -minecraft_version_range=[1.21.6, 1.22) +minecraft_version=1.21.9 +minecraft_version_list=1.21.9 +minecraft_version_range=[1.21.9, 1.22) mod_id=skinrestorer mod_name=SkinRestorer mod_version=2.4.3 @@ -21,8 +21,8 @@ description=A server-side mod for managing skins. mineskin_client_version=3.0.6-SNAPSHOT # ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions -parchment_minecraft=1.21.5 -parchment_version=2025.06.15 +parchment_minecraft=1.21.8 +parchment_version=2025.09.14 # Publishing curseforge_id=443823 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 0385d9e..0c9aca9 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -55,6 +55,4 @@ dependencies { prefer mineskin_client_version } } - - additionalRuntimeClasspath "org.mineskin:java-client:${mineskin_client_version}" } diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index 4fe7510..4ecb081 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -1,3 +1,3 @@ # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=21.6.0-beta +neoforge_version=21.9.0-beta neoforge_loader_version_range=[4,) diff --git a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java index fdd2fdc..99c41f4 100644 --- a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java +++ b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java @@ -1,5 +1,6 @@ package net.lionarius.skinrestorer.neoforge.compat.skinshuffle; +import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.compat.skinshuffle.*; import net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility; import net.minecraft.server.level.ServerPlayer; @@ -26,6 +27,6 @@ public final class SkinShuffleModEventHandler { private static void handleSkinRefreshPacket(SkinShuffleSkinRefreshPayload payload, IPayloadContext context) { var player = (ServerPlayer) context.player(); - SkinShuffleCompatibility.handleSkinRefresh(player.getServer(), player, payload); + SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), player, payload); } } From b5bed1c4412e59ef5fc04e5d8f21315450236c3b Mon Sep 17 00:00:00 2001 From: Lionarius Date: Sat, 4 Oct 2025 22:56:48 +0300 Subject: [PATCH 02/13] backport to 1.21.6 --- build.gradle | 2 +- .../lionarius/skinrestorer/SkinRestorer.java | 27 ++++---- .../skinrestorer/command/SkinCommand.java | 44 +++++------- .../skinshuffle/SkinShuffleCompatibility.java | 2 +- .../mixin/GameProfileCacheAccessor.java | 14 ++++ .../skinrestorer/mixin/PlayerAccessor.java | 15 ---- .../skinrestorer/mixin/PlayerListMixin.java | 2 +- .../ServerLoginPacketListenerImplMixin.java | 16 ++--- .../mixin/SkullBlockEntityMixin.java | 69 +++++++++++++++++++ .../skin/provider/ElyBySkinProvider.java | 2 +- .../skin/provider/MojangSkinProvider.java | 27 ++++---- .../skinrestorer/util/JsonUtils.java | 2 + .../skinrestorer/util/PlayerUtils.java | 23 +++---- .../resources/META-INF/accesstransformer.cfg | 1 + .../main/resources/skinrestorer.accesswidener | 2 + .../main/resources/skinrestorer.mixins.json | 3 +- fabric/gradle.properties | 2 +- .../skinshuffle/SkinShuffleCompatibility.java | 2 +- forge/gradle.properties | 6 +- .../skinshuffle/SkinShufflePacketHandler.java | 2 +- gradle.properties | 10 +-- neoforge/build.gradle | 2 + neoforge/gradle.properties | 2 +- .../SkinShuffleModEventHandler.java | 3 +- 24 files changed, 168 insertions(+), 112 deletions(-) create mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java delete mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java diff --git a/build.gradle b/build.gradle index 5be6436..b092859 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { // see https://fabricmc.net/develop/ for new versions - id 'fabric-loom' version '1.11-SNAPSHOT' apply false + id 'fabric-loom' version '1.10-SNAPSHOT' apply false // see https://projects.neoforged.net/neoforged/moddevgradle for new versions id 'net.neoforged.moddev' version '2.0.+' apply false // see https://files.minecraftforge.net/net/minecraftforge/gradle/ForgeGradle/ for new versions diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 2296958..f2270f1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -1,12 +1,12 @@ package net.lionarius.skinrestorer; import com.google.common.base.Throwables; +import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; import net.lionarius.skinrestorer.command.SkinCommand; import net.lionarius.skinrestorer.config.Config; import net.lionarius.skinrestorer.config.provider.BuiltInProviderConfig; import net.lionarius.skinrestorer.exception.TransparentException; -import net.lionarius.skinrestorer.mixin.PlayerAccessor; import net.lionarius.skinrestorer.platform.Services; import net.lionarius.skinrestorer.skin.SkinIO; import net.lionarius.skinrestorer.skin.SkinStorage; @@ -108,26 +108,23 @@ public final class SkinRestorer { MineskinSkinProvider.reload(); } - public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value, boolean save) { + public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value, boolean save) { var acceptedPlayers = new HashSet(); - for (var player : targets) { - var profile = player.getGameProfile(); - var skin = PlayerUtils.getPlayerSkin(profile); + for (var profile : targets) { + if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) + value = value.setOriginalValue(PlayerUtils.getPlayerSkin(profile)); - if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) - value = value.setOriginalValue(skin); - - if (PlayerUtils.areSkinPropertiesEquals(value.value(), skin)) + if (PlayerUtils.areSkinPropertiesEquals(value.value(), PlayerUtils.getPlayerSkin(profile))) continue; if (save) - SkinRestorer.getSkinStorage().setSkin(profile.id(), value); + SkinRestorer.getSkinStorage().setSkin(profile.getId(), value); - var newProfile = PlayerUtils.applyRestoredSkin(profile, value.value()); - ((PlayerAccessor) player).setGameProfile(newProfile); + PlayerUtils.applyRestoredSkin(profile, value.value()); - if (player.connection == null) + var player = server.getPlayerList().getPlayer(profile.getId()); + if (player == null) continue; PlayerUtils.refreshPlayer(player); @@ -139,13 +136,13 @@ public final class SkinRestorer { return acceptedPlayers; } - public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value) { + public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value) { return SkinRestorer.applySkin(server, targets, value, true); } public static CompletableFuture, String>> setSkinAsync( MinecraftServer server, - Collection targets, + Collection targets, SkinProviderContext context, boolean save ) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java index c2ab49d..8dd93ea 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java +++ b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java @@ -1,5 +1,6 @@ package net.lionarius.skinrestorer.command; +import com.mojang.authlib.GameProfile; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; @@ -17,12 +18,10 @@ import net.lionarius.skinrestorer.util.PlayerUtils; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.players.NameAndId; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; @@ -73,41 +72,37 @@ public final class SkinCommand { SkinProviderContext context = null; var save = true; - if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) { - if (profile.properties().containsKey(PlayerUtils.TEXTURES_KEY)) { + if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { + if (profile.getProperties().containsKey(PlayerUtils.TEXTURES_KEY)) { save = false; context = MojangSkinProvider.skinProviderContextFromProfile(profile); } } else { - context = SkinRestorer.getSkinStorage().getSkin(profile.id()).toProviderContext(); + context = SkinRestorer.getSkinStorage().getSkin(profile.getId()).toProviderContext(); } if (context == null) return 0; - return SkinCommand.setSubcommand(src, Collections.singleton(new NameAndId(profile)), context, save, false); + return SkinCommand.setSubcommand(src, Collections.singleton(profile), context, save, false); } private static int resetSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, boolean setByOperator ) { var updatedPlayers = new HashSet(); - for (var nameAndId : targets) { + for (var profile : targets) { SkinValue skin = null; - if (SkinRestorer.getSkinStorage().hasSavedSkin(nameAndId.id())) - skin = SkinRestorer.getSkinStorage().getSkin(nameAndId.id()).replaceValueWithOriginal(); + if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) + skin = SkinRestorer.getSkinStorage().getSkin(profile.getId()).replaceValueWithOriginal(); if (skin == null) continue; - var player = src.getServer().getPlayerList().getPlayer(nameAndId.id()); - if (player == null) - continue; - - var updatedPlayer = SkinRestorer.applySkin(src.getServer(), Collections.singleton(player), skin, false); - SkinRestorer.getSkinStorage().deleteSkin(nameAndId.id()); + var updatedPlayer = SkinRestorer.applySkin(src.getServer(), Collections.singleton(profile), skin, false); + SkinRestorer.getSkinStorage().deleteSkin(profile.getId()); updatedPlayers.addAll(updatedPlayer); } @@ -123,24 +118,19 @@ public final class SkinCommand { if (src.getPlayer() == null) return 0; - return resetSubcommand(src, Collections.singleton(src.getPlayer().nameAndId()), false); + return resetSubcommand(src, Collections.singleton(src.getPlayer().getGameProfile()), false); } private static int setSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, SkinProviderContext context, boolean save, boolean setByOperator ) { src.sendSystemMessage(Translation.translatableWithFallback(Translation.COMMAND_SKIN_LOADING_KEY)); - var profileTargets = targets.stream() - .map(nameAndId -> src.getServer().getPlayerList().getPlayer(nameAndId.id())) - .filter(Objects::nonNull) - .toList(); - - SkinRestorer.setSkinAsync(src.getServer(), profileTargets, context, save).thenAccept(result -> { + SkinRestorer.setSkinAsync(src.getServer(), targets, context, save).thenAccept(result -> { if (result.isError()) { src.sendFailure(Translation.translatableWithFallback( Translation.COMMAND_SKIN_FAILED_KEY, @@ -159,7 +149,7 @@ public final class SkinCommand { private static int setSubcommand( CommandSourceStack src, - Collection targets, + Collection targets, SkinProviderContext context, boolean setByOperator ) { @@ -173,7 +163,7 @@ public final class SkinCommand { if (src.getPlayer() == null) return 0; - return setSubcommand(src, Collections.singleton(src.getPlayer().nameAndId()), context, false); + return setSubcommand(src, Collections.singleton(src.getPlayer().getGameProfile()), context, false); } private static int configReloadSubcommand(CommandContext context) { @@ -266,7 +256,7 @@ public final class SkinCommand { } private static RequiredArgumentBuilder makeTargetsArgument( - BiFunction, Collection, Integer> consumer + BiFunction, Collection, Integer> consumer ) { return argument("targets", GameProfileArgument.gameProfile()) .requires(source -> source.hasPermission(2)) diff --git a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java index 431fe20..3b60331 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java @@ -43,7 +43,7 @@ public class SkinShuffleCompatibility { server.execute(() -> { SkinRestorer.applySkin( server, - Collections.singleton(player), + Collections.singleton(player.getGameProfile()), new SkinValue(SkinShuffleSkinProvider.PROVIDER_NAME, null, null, property), !server.usesAuthentication() ); diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java new file mode 100644 index 0000000..f129fc3 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/GameProfileCacheAccessor.java @@ -0,0 +1,14 @@ +package net.lionarius.skinrestorer.mixin; + +import net.minecraft.server.players.GameProfileCache; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(GameProfileCache.class) +public interface GameProfileCacheAccessor { + + @Accessor + Map getProfilesByName(); +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java deleted file mode 100644 index 61a4d04..0000000 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerAccessor.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.lionarius.skinrestorer.mixin; - -import com.mojang.authlib.GameProfile; -import net.minecraft.world.entity.player.Player; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - - -@Mixin(Player.class) -public interface PlayerAccessor { - @Accessor("gameProfile") - @Mutable - void setGameProfile(GameProfile properties); -} diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java index 4752301..ccaed02 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/PlayerListMixin.java @@ -57,6 +57,6 @@ public abstract class PlayerListMixin { @Unique private static void skinrestorer$tryApplySkin(MinecraftServer server, ServerPlayer player) { if (SkinRestorer.getSkinStorage().hasSavedSkin(player.getUUID())) - SkinRestorer.applySkin(server, Collections.singleton(player), SkinRestorer.getSkinStorage().getSkin(player.getUUID())); + SkinRestorer.applySkin(server, Collections.singleton(player.getGameProfile()), SkinRestorer.getSkinStorage().getSkin(player.getUUID())); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java index 09a97dc..ca9780c 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java @@ -27,7 +27,7 @@ public abstract class ServerLoginPacketListenerImplMixin { private CompletableFuture skinrestorer$pendingSkin; @Inject(method = "verifyLoginAndFinishConnectionSetup", at = @At(value = "INVOKE", - target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lnet/minecraft/server/players/NameAndId;)Lnet/minecraft/network/chat/Component;"), + target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/network/chat/Component;"), cancellable = true) public void waitForSkin(CallbackInfo ci) { if (skinrestorer$pendingSkin == null) { @@ -37,14 +37,14 @@ public abstract class ServerLoginPacketListenerImplMixin { assert profile != null; var originalSkin = PlayerUtils.getPlayerSkin(profile); - if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.id())) { + if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { if (originalSkin != null) { // update to the latest official skin - var value = SkinRestorer.getSkinStorage().getSkin(profile.id()); - SkinRestorer.getSkinStorage().setSkin(profile.id(), value.setOriginalValue(originalSkin)); + var value = SkinRestorer.getSkinStorage().getSkin(profile.getId()); + SkinRestorer.getSkinStorage().setSkin(profile.getId(), value.setOriginalValue(originalSkin)); } if (SkinRestorer.getConfig().refreshSkinOnJoin()) { - var currentSkin = SkinRestorer.getSkinStorage().getSkin(profile.id()); + var currentSkin = SkinRestorer.getSkinStorage().getSkin(profile.getId()); var context = currentSkin.toProviderContext(); skinrestorer$fetchSkin(profile, context); @@ -56,7 +56,7 @@ public abstract class ServerLoginPacketListenerImplMixin { if (originalSkin == null && SkinRestorer.getConfig().fetchSkinOnFirstJoin()) { var context = new SkinProviderContext( SkinRestorer.getConfig().firstJoinSkinProvider().getName(), - profile.name(), + profile.getName(), null ); skinrestorer$fetchSkin(profile, context); @@ -72,7 +72,7 @@ public abstract class ServerLoginPacketListenerImplMixin { @Unique private static void skinrestorer$fetchSkin(GameProfile profile, SkinProviderContext context) { - SkinRestorer.LOGGER.debug("Fetching {}'s skin", profile.name()); + SkinRestorer.LOGGER.debug("Fetching {}'s skin", profile.getName()); var result = SkinRestorer.getProvider(context.name()).map( provider -> provider.fetchSkin(context.argument(), context.variant()) @@ -80,7 +80,7 @@ public abstract class ServerLoginPacketListenerImplMixin { if (!result.isError()) { var value = SkinValue.fromProviderContextWithValue(context, result.getSuccessValue().orElse(null)); - SkinRestorer.getSkinStorage().setSkin(profile.id(), value); + SkinRestorer.getSkinStorage().setSkin(profile.getId(), value); } else { SkinRestorer.LOGGER.warn("Failed to fetch skin '{}:{}'", context.name(), context.argument(), result.getErrorValue()); } diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java new file mode 100644 index 0000000..21d4d38 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java @@ -0,0 +1,69 @@ +package net.lionarius.skinrestorer.mixin; + +import com.mojang.authlib.GameProfile; +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.util.PlayerUtils; +import net.minecraft.Util; +import net.minecraft.server.Services; +import net.minecraft.world.level.block.entity.SkullBlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Locale; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.function.BooleanSupplier; + +@Mixin(SkullBlockEntity.class) +public abstract class SkullBlockEntityMixin { + + @Inject(method = "fetchProfileByName", at = @At("HEAD"), + cancellable = true) + private static void fetchProfileByName(String name, Services services, CallbackInfoReturnable>> cir) { + if (name == null) + return; + + var profileOpt = Optional.empty(); + var gameProfileInfo = ((GameProfileCacheAccessor) services.profileCache()).getProfilesByName().get(name.toLowerCase(Locale.ROOT)); + + if (gameProfileInfo != null) + profileOpt = Optional.of(gameProfileInfo.getProfile()); + + skinrestorer$replaceSkin(profileOpt, cir); + } + + @Inject(method = "fetchProfileById", at = @At("HEAD"), + cancellable = true) + private static void fetchProfileById(UUID id, Services services, BooleanSupplier cacheUninitialized, CallbackInfoReturnable>> cir) { + if (id == null) + return; + + var profileOpt = services.profileCache().get(id); + + skinrestorer$replaceSkin(profileOpt, cir); + } + + @Unique + private static void skinrestorer$replaceSkin(Optional profileOpt, CallbackInfoReturnable>> cir) { + if (SkinRestorer.getMinecraftServer() == null) + return; + + if (profileOpt.isEmpty()) + return; + + var profile = PlayerUtils.cloneGameProfile(profileOpt.get()); + + if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { + cir.setReturnValue(CompletableFuture.supplyAsync(() -> { + var skin = SkinRestorer.getSkinStorage().getSkin(profile.getId(), false); + PlayerUtils.applyRestoredSkin(profile, skin.value()); + + return Optional.of(profile); + }, Util.backgroundExecutor().forName("getProfile"))); + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java index a8bdfc7..b8496f1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/ElyBySkinProvider.java @@ -106,6 +106,6 @@ public final class ElyBySkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with name " + username); - return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).profile(); + return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).toProfile(); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java index b41e10f..56f6bff 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MojangSkinProvider.java @@ -8,7 +8,6 @@ import com.mojang.authlib.*; import com.mojang.authlib.properties.Property; import com.mojang.authlib.yggdrasil.YggdrasilEnvironment; import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse; -import com.mojang.authlib.yggdrasil.response.NameAndId; import com.mojang.util.UndashedUuid; import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.exception.TransparentException; @@ -17,7 +16,7 @@ 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 net.minecraft.server.players.CachedUserNameToIdResolver; +import net.minecraft.server.players.GameProfileCache; import net.minecraft.util.StringUtil; import org.jetbrains.annotations.NotNull; @@ -32,11 +31,13 @@ import java.util.concurrent.TimeUnit; public final class MojangSkinProvider implements SkinProvider { public static final String PROVIDER_NAME = "mojang"; - public static final String PROFILE_CACHE_FILENAME = "mojang_profile_cache.json"; + private static final Environment ENVIRONMENT; private static final URI SERVICES_SERVER_URI; private static final URI SESSION_SERVER_URI; - private static final CachedUserNameToIdResolver PROFILE_CACHE; + + public static final String PROFILE_CACHE_FILENAME = "mojang_profile_cache.json"; + private static final GameProfileCache PROFILE_CACHE; private static LoadingCache> SKIN_CACHE; @@ -50,13 +51,13 @@ public final class MojangSkinProvider implements SkinProvider { throw new IllegalArgumentException(e); } - PROFILE_CACHE = new CachedUserNameToIdResolver(new GameProfileRepository() { + PROFILE_CACHE = new GameProfileCache(new GameProfileRepository() { @Override public void findProfilesByNames(String[] names, ProfileLookupCallback callback) { for (var name : names) { try { var profile = MojangSkinProvider.getProfile(name); - callback.onProfileLookupSucceeded(profile.name(), profile.id()); + callback.onProfileLookupSucceeded(profile); } catch (IOException e) { throw new TransparentException(e); } @@ -64,10 +65,10 @@ public final class MojangSkinProvider implements SkinProvider { } @Override - public Optional findProfileByName(String name) { + public Optional findProfileByName(String name) { try { var profile = MojangSkinProvider.getProfile(name); - return Optional.of(new NameAndId(profile.id(), profile.name())); + return Optional.of(profile); } catch (IOException e) { throw new TransparentException(e); } @@ -94,7 +95,7 @@ public final class MojangSkinProvider implements SkinProvider { } public static SkinProviderContext skinProviderContextFromProfile(GameProfile gameProfile) { - return new SkinProviderContext(MojangSkinProvider.PROVIDER_NAME, gameProfile.name(), null); + return new SkinProviderContext(MojangSkinProvider.PROVIDER_NAME, gameProfile.getName(), null); } @Override @@ -117,7 +118,7 @@ public final class MojangSkinProvider implements SkinProvider { if (cachedProfile.isEmpty()) throw new IllegalArgumentException("no profile found for " + username); - return Result.success(SKIN_CACHE.get(cachedProfile.get().id())); + return Result.success(SKIN_CACHE.get(cachedProfile.get().getId())); } catch (UncheckedExecutionException e) { return Result.error((Exception) e.getCause()); } catch (Exception e) { @@ -132,7 +133,7 @@ public final class MojangSkinProvider implements SkinProvider { return Optional.ofNullable(textures); } - private static NameAndId getProfile(final String name) throws IOException { + private static GameProfile getProfile(final String name) throws IOException { var request = HttpRequest.newBuilder() .uri(MojangSkinProvider.SERVICES_SERVER_URI .resolve("/minecraft/profile/lookup/name/") @@ -147,7 +148,7 @@ public final class MojangSkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with name " + name); - return JsonUtils.fromJson(response.body(), NameAndId.class); + return JsonUtils.fromJson(response.body(), GameProfile.class); } private static GameProfile getProfileWithProperties(UUID uuid) throws IOException { @@ -165,6 +166,6 @@ public final class MojangSkinProvider implements SkinProvider { if (response.statusCode() != 200) throw new IllegalArgumentException("no profile with uuid " + uuid); - return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).profile(); + return JsonUtils.fromJson(response.body(), MinecraftProfilePropertiesResponse.class).toProfile(); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java b/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java index a3726b3..7eef4e1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/JsonUtils.java @@ -4,6 +4,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; import com.mojang.util.UUIDTypeAdapter; @@ -21,6 +22,7 @@ public final class JsonUtils { .registerTypeAdapterFactory(new PostProcessingEnabler()) .registerTypeAdapter(UUID.class, new UUIDTypeAdapter()) .registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()) + .registerTypeAdapter(GameProfile.class, new GameProfile.Serializer()) .setPrettyPrinting() .create(); diff --git a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java index 031a510..fbb1476 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java @@ -1,11 +1,9 @@ package net.lionarius.skinrestorer.util; import com.google.common.collect.Iterables; -import com.google.common.collect.LinkedHashMultimap; import com.google.gson.JsonObject; import com.mojang.authlib.GameProfile; import com.mojang.authlib.properties.Property; -import com.mojang.authlib.properties.PropertyMap; import com.mojang.authlib.yggdrasil.response.MinecraftProfilePropertiesResponse; import net.lionarius.skinrestorer.mixin.ChunkMapAccessor; import net.minecraft.network.chat.Component; @@ -98,26 +96,21 @@ public final class PlayerUtils { } public static GameProfile cloneGameProfile(GameProfile profile) { - var newProfile = new GameProfile(profile.id(), profile.name()); - newProfile.properties().putAll(profile.properties()); + var newProfile = new GameProfile(profile.getId(), profile.getName()); + newProfile.getProperties().putAll(profile.getProperties()); return newProfile; } public static Property getPlayerSkin(GameProfile profile) { - return Iterables.getFirst(profile.properties().get(TEXTURES_KEY), null); + return Iterables.getFirst(profile.getProperties().get(TEXTURES_KEY), null); } - public static GameProfile applyRestoredSkin(GameProfile profile, Property skin) { - var propertiesMap = profile.properties(); + public static void applyRestoredSkin(GameProfile profile, Property skin) { + profile.getProperties().removeAll(TEXTURES_KEY); - var newProperties = LinkedHashMultimap.create(propertiesMap); - newProperties.removeAll(TEXTURES_KEY); - if (skin != null) { - newProperties.put(TEXTURES_KEY, skin); - } - - return new GameProfile(profile.id(), profile.name(), new PropertyMap(newProperties)); + if (skin != null) + profile.getProperties().put(TEXTURES_KEY, skin); } public static boolean areSkinPropertiesEquals(Property x, Property y) { @@ -141,7 +134,7 @@ public final class PlayerUtils { public static GameProfile toProfile(MinecraftProfilePropertiesResponse response) { final GameProfile profile = new GameProfile(response.id(), response.name()); - profile.properties().putAll(response.properties()); + profile.getProperties().putAll(response.properties()); return profile; } } diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg index e69de29..8c8e31a 100644 --- a/common/src/main/resources/META-INF/accesstransformer.cfg +++ b/common/src/main/resources/META-INF/accesstransformer.cfg @@ -0,0 +1 @@ +public net.minecraft.server.players.GameProfileCache$GameProfileInfo diff --git a/common/src/main/resources/skinrestorer.accesswidener b/common/src/main/resources/skinrestorer.accesswidener index 9ab2173..28ef0d1 100644 --- a/common/src/main/resources/skinrestorer.accesswidener +++ b/common/src/main/resources/skinrestorer.accesswidener @@ -1 +1,3 @@ accessWidener v2 named + +accessible class net/minecraft/server/players/GameProfileCache$GameProfileInfo diff --git a/common/src/main/resources/skinrestorer.mixins.json b/common/src/main/resources/skinrestorer.mixins.json index 741efe1..1956e5a 100644 --- a/common/src/main/resources/skinrestorer.mixins.json +++ b/common/src/main/resources/skinrestorer.mixins.json @@ -10,7 +10,8 @@ "PlayerListMixin", "ServerLoginPacketListenerImplMixin", "TrackedEntityAccessorInvoker", - "PlayerAccessor" + "SkullBlockEntityMixin", + "GameProfileCacheAccessor" ], "injectors": { "defaultRequire": 1 diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 7dc2ea4..d4f3a28 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,6 +1,6 @@ # Fabric, see https://fabricmc.net/develop/ for new versions fabric_loader_version=0.15.0 -fabric_api_version=0.133.14+1.21.9 +fabric_api_version=0.127.0+1.21.6 optional_dependencies=fabric-api additional_modloaders=quilt diff --git a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java index b8073a4..662ae14 100644 --- a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/compat/skinshuffle/SkinShuffleCompatibility.java @@ -32,6 +32,6 @@ public final class SkinShuffleCompatibility { } private static void handleSkinRefreshPacket(SkinShuffleSkinRefreshPayload payload, ServerPlayNetworking.Context context) { - net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), context.player(), payload); + net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility.handleSkinRefresh(context.player().getServer(), context.player(), payload); } } diff --git a/forge/gradle.properties b/forge/gradle.properties index d1e8843..1d49e44 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -1,5 +1,5 @@ # Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=59.0.0 -forge_loader_version_range=[59,) +forge_version=56.0.0 +forge_loader_version_range=[56,) # Forge sometimes skips minor minecraft versions (like 1.20.5) -forge_minecraft_version=1.21.9 +forge_minecraft_version=1.21.6 diff --git a/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java b/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java index efd5049..badc589 100644 --- a/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java +++ b/forge/src/main/java/net/lionarius/skinrestorer/forge/compat/skinshuffle/SkinShufflePacketHandler.java @@ -40,6 +40,6 @@ public class SkinShufflePacketHandler { if (!context.isServerSide() || sender == null) return; - SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), sender, payload); + SkinShuffleCompatibility.handleSkinRefresh(sender.getServer(), sender, payload); } } diff --git a/gradle.properties b/gradle.properties index 2ee5965..cbfc991 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,9 @@ group=net.lionarius java_version=21 # Common -minecraft_version=1.21.9 -minecraft_version_list=1.21.9 -minecraft_version_range=[1.21.9, 1.22) +minecraft_version=1.21.6 +minecraft_version_list=1.21.6,1.21.7,1.21.8 +minecraft_version_range=[1.21.6, 1.21.8] mod_id=skinrestorer mod_name=SkinRestorer mod_version=2.4.3 @@ -21,8 +21,8 @@ description=A server-side mod for managing skins. mineskin_client_version=3.0.6-SNAPSHOT # ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions -parchment_minecraft=1.21.8 -parchment_version=2025.09.14 +parchment_minecraft=1.21.6 +parchment_version=2025.06.29 # Publishing curseforge_id=443823 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 0c9aca9..0385d9e 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -55,4 +55,6 @@ dependencies { prefer mineskin_client_version } } + + additionalRuntimeClasspath "org.mineskin:java-client:${mineskin_client_version}" } diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index 4ecb081..4fe7510 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -1,3 +1,3 @@ # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=21.9.0-beta +neoforge_version=21.6.0-beta neoforge_loader_version_range=[4,) diff --git a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java index 99c41f4..fdd2fdc 100644 --- a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java +++ b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/compat/skinshuffle/SkinShuffleModEventHandler.java @@ -1,6 +1,5 @@ package net.lionarius.skinrestorer.neoforge.compat.skinshuffle; -import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.compat.skinshuffle.*; import net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility; import net.minecraft.server.level.ServerPlayer; @@ -27,6 +26,6 @@ public final class SkinShuffleModEventHandler { private static void handleSkinRefreshPacket(SkinShuffleSkinRefreshPayload payload, IPayloadContext context) { var player = (ServerPlayer) context.player(); - SkinShuffleCompatibility.handleSkinRefresh(SkinRestorer.getMinecraftServer(), player, payload); + SkinShuffleCompatibility.handleSkinRefresh(player.getServer(), player, payload); } } From 69a561f67940af8affb895be6b04f656ad58f0ae Mon Sep 17 00:00:00 2001 From: Lionarius Date: Mon, 15 Dec 2025 23:24:17 +0300 Subject: [PATCH 03/13] port 1.21.11 --- build.gradle | 2 +- common/build.gradle | 2 +- .../java/net/lionarius/skinrestorer/SkinRestorer.java | 6 +++--- .../net/lionarius/skinrestorer/command/SkinCommand.java | 5 +++-- .../compat/skinshuffle/SkinShuffleCompatibility.java | 6 +++--- fabric/build.gradle | 2 +- fabric/gradle.properties | 2 +- .../skinrestorer/fabric/mixin/MinecraftServerMixin.java | 2 +- forge/build.gradle | 3 ++- forge/gradle.properties | 6 +++--- gradle.properties | 6 +++--- neoforge/build.gradle | 8 ++++---- neoforge/gradle.properties | 2 +- 13 files changed, 27 insertions(+), 25 deletions(-) diff --git a/build.gradle b/build.gradle index 5be6436..d50e37e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { // see https://fabricmc.net/develop/ for new versions - id 'fabric-loom' version '1.11-SNAPSHOT' apply false + id 'fabric-loom' version '1.13-SNAPSHOT' apply false // see https://projects.neoforged.net/neoforged/moddevgradle for new versions id 'net.neoforged.moddev' version '2.0.+' apply false // see https://files.minecraftforge.net/net/minecraftforge/gradle/ForgeGradle/ for new versions diff --git a/common/build.gradle b/common/build.gradle index c168537..88b391d 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -8,7 +8,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() - parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") +// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } compileOnly group: 'org.spongepowered', name: 'mixin', version: '0.8.5' diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 2296958..1220221 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -15,7 +15,7 @@ import net.lionarius.skinrestorer.skin.provider.*; import net.lionarius.skinrestorer.translation.Translation; import net.lionarius.skinrestorer.util.*; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.LevelResource; @@ -70,8 +70,8 @@ public final class SkinRestorer { return Optional.ofNullable(SkinRestorer.providersRegistry.get(name)); } - public static ResourceLocation resourceLocation(String name) { - return ResourceLocation.fromNamespaceAndPath(SkinRestorer.MOD_ID, name); + public static Identifier resourceLocation(String name) { + return Identifier.fromNamespaceAndPath(SkinRestorer.MOD_ID, name); } public static String assetPath(String name) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java index c2ab49d..b8628a2 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java +++ b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java @@ -15,6 +15,7 @@ import net.lionarius.skinrestorer.skin.provider.SkinProviderContext; import net.lionarius.skinrestorer.translation.Translation; import net.lionarius.skinrestorer.util.PlayerUtils; import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.NameAndId; @@ -55,7 +56,7 @@ public final class SkinCommand { base.then( literal("config") - .requires(commandSourceStack -> commandSourceStack.hasPermission(4)) + .requires(commandSourceStack -> Commands.LEVEL_OWNERS.check(commandSourceStack.permissions())) .then(literal("reload").executes(SkinCommand::configReloadSubcommand)) ); @@ -269,7 +270,7 @@ public final class SkinCommand { BiFunction, Collection, Integer> consumer ) { return argument("targets", GameProfileArgument.gameProfile()) - .requires(source -> source.hasPermission(2)) + .requires(source -> Commands.LEVEL_GAMEMASTERS.check(source.permissions())) .executes(context -> consumer.apply(context, GameProfileArgument.getGameProfiles(context, "targets"))); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java index 431fe20..6730985 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java @@ -5,7 +5,7 @@ import net.lionarius.skinrestorer.platform.Services; import net.lionarius.skinrestorer.skin.SkinValue; import net.lionarius.skinrestorer.skin.provider.SkinShuffleSkinProvider; import net.lionarius.skinrestorer.util.PlayerUtils; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -23,8 +23,8 @@ public class SkinShuffleCompatibility { return SkinShuffleCompatibility.SHOULD_APPLY; } - public static ResourceLocation resourceLocation(String name) { - return ResourceLocation.fromNamespaceAndPath(SkinShuffleCompatibility.MOD_ID, name); + public static Identifier resourceLocation(String name) { + return Identifier.fromNamespaceAndPath(SkinShuffleCompatibility.MOD_ID, name); } public static void onPlayerJoin(ServerPlayer player) { diff --git a/fabric/build.gradle b/fabric/build.gradle index 62adc91..27c744a 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -10,7 +10,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() - parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") +// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 7dc2ea4..9f16c0d 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,6 +1,6 @@ # Fabric, see https://fabricmc.net/develop/ for new versions fabric_loader_version=0.15.0 -fabric_api_version=0.133.14+1.21.9 +fabric_api_version=0.139.4+1.21.11 optional_dependencies=fabric-api additional_modloaders=quilt diff --git a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java index 6212f81..6ebb954 100644 --- a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java +++ b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public abstract class MinecraftServerMixin { @Inject(method = "runServer", - at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0)) + at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Util;getNanos()J", ordinal = 0)) private void onServerStarted(CallbackInfo ci) { SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this); } diff --git a/forge/build.gradle b/forge/build.gradle index c52c1d9..588a178 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -24,7 +24,8 @@ tasks.named('jarJar') { jar.finalizedBy('jarJar') minecraft { - mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" + mappings channel: 'official', version: minecraft_version +// mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" copyIdeResources = true //Calls processResources when in dev diff --git a/forge/gradle.properties b/forge/gradle.properties index d1e8843..211825c 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -1,5 +1,5 @@ # Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=59.0.0 -forge_loader_version_range=[59,) +forge_version=61.0.0 +forge_loader_version_range=[61,) # Forge sometimes skips minor minecraft versions (like 1.20.5) -forge_minecraft_version=1.21.9 +forge_minecraft_version=1.21.11 diff --git a/gradle.properties b/gradle.properties index 2ee5965..7f59327 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,9 @@ group=net.lionarius java_version=21 # Common -minecraft_version=1.21.9 -minecraft_version_list=1.21.9 -minecraft_version_range=[1.21.9, 1.22) +minecraft_version=1.21.11 +minecraft_version_list=1.21.11 +minecraft_version_range=[1.21.11, 1.22) mod_id=skinrestorer mod_name=SkinRestorer mod_version=2.4.3 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 0c9aca9..7caa74f 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -17,10 +17,10 @@ neoForge { accessTransformers.from(at.absolutePath) } - parchment { - minecraftVersion = parchment_minecraft - mappingsVersion = parchment_version - } +// parchment { +// minecraftVersion = parchment_minecraft +// mappingsVersion = parchment_version +// } runs { configureEach { diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index 4ecb081..b80f750 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -1,3 +1,3 @@ # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=21.9.0-beta +neoforge_version=21.11.0-beta neoforge_loader_version_range=[4,) From da754aa8302ae959b210ea69b41b783ffed0817c Mon Sep 17 00:00:00 2001 From: Lionarius Date: Mon, 15 Dec 2025 23:31:40 +0300 Subject: [PATCH 04/13] backport to 1.21.9 This reverts commit 69a561f67940af8affb895be6b04f656ad58f0ae. --- build.gradle | 2 +- common/build.gradle | 2 +- .../java/net/lionarius/skinrestorer/SkinRestorer.java | 6 +++--- .../net/lionarius/skinrestorer/command/SkinCommand.java | 5 ++--- .../compat/skinshuffle/SkinShuffleCompatibility.java | 6 +++--- fabric/build.gradle | 2 +- fabric/gradle.properties | 2 +- .../skinrestorer/fabric/mixin/MinecraftServerMixin.java | 2 +- forge/build.gradle | 3 +-- forge/gradle.properties | 6 +++--- gradle.properties | 6 +++--- neoforge/build.gradle | 8 ++++---- neoforge/gradle.properties | 2 +- 13 files changed, 25 insertions(+), 27 deletions(-) diff --git a/build.gradle b/build.gradle index d50e37e..5be6436 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { // see https://fabricmc.net/develop/ for new versions - id 'fabric-loom' version '1.13-SNAPSHOT' apply false + id 'fabric-loom' version '1.11-SNAPSHOT' apply false // see https://projects.neoforged.net/neoforged/moddevgradle for new versions id 'net.neoforged.moddev' version '2.0.+' apply false // see https://files.minecraftforge.net/net/minecraftforge/gradle/ForgeGradle/ for new versions diff --git a/common/build.gradle b/common/build.gradle index 88b391d..c168537 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -8,7 +8,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() -// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") + parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } compileOnly group: 'org.spongepowered', name: 'mixin', version: '0.8.5' diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 1220221..2296958 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -15,7 +15,7 @@ import net.lionarius.skinrestorer.skin.provider.*; import net.lionarius.skinrestorer.translation.Translation; import net.lionarius.skinrestorer.util.*; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.LevelResource; @@ -70,8 +70,8 @@ public final class SkinRestorer { return Optional.ofNullable(SkinRestorer.providersRegistry.get(name)); } - public static Identifier resourceLocation(String name) { - return Identifier.fromNamespaceAndPath(SkinRestorer.MOD_ID, name); + public static ResourceLocation resourceLocation(String name) { + return ResourceLocation.fromNamespaceAndPath(SkinRestorer.MOD_ID, name); } public static String assetPath(String name) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java index b8628a2..c2ab49d 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java +++ b/common/src/main/java/net/lionarius/skinrestorer/command/SkinCommand.java @@ -15,7 +15,6 @@ import net.lionarius.skinrestorer.skin.provider.SkinProviderContext; import net.lionarius.skinrestorer.translation.Translation; import net.lionarius.skinrestorer.util.PlayerUtils; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.GameProfileArgument; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.NameAndId; @@ -56,7 +55,7 @@ public final class SkinCommand { base.then( literal("config") - .requires(commandSourceStack -> Commands.LEVEL_OWNERS.check(commandSourceStack.permissions())) + .requires(commandSourceStack -> commandSourceStack.hasPermission(4)) .then(literal("reload").executes(SkinCommand::configReloadSubcommand)) ); @@ -270,7 +269,7 @@ public final class SkinCommand { BiFunction, Collection, Integer> consumer ) { return argument("targets", GameProfileArgument.gameProfile()) - .requires(source -> Commands.LEVEL_GAMEMASTERS.check(source.permissions())) + .requires(source -> source.hasPermission(2)) .executes(context -> consumer.apply(context, GameProfileArgument.getGameProfiles(context, "targets"))); } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java index 6730985..431fe20 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java +++ b/common/src/main/java/net/lionarius/skinrestorer/compat/skinshuffle/SkinShuffleCompatibility.java @@ -5,7 +5,7 @@ import net.lionarius.skinrestorer.platform.Services; import net.lionarius.skinrestorer.skin.SkinValue; import net.lionarius.skinrestorer.skin.provider.SkinShuffleSkinProvider; import net.lionarius.skinrestorer.util.PlayerUtils; -import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; @@ -23,8 +23,8 @@ public class SkinShuffleCompatibility { return SkinShuffleCompatibility.SHOULD_APPLY; } - public static Identifier resourceLocation(String name) { - return Identifier.fromNamespaceAndPath(SkinShuffleCompatibility.MOD_ID, name); + public static ResourceLocation resourceLocation(String name) { + return ResourceLocation.fromNamespaceAndPath(SkinShuffleCompatibility.MOD_ID, name); } public static void onPlayerJoin(ServerPlayer player) { diff --git a/fabric/build.gradle b/fabric/build.gradle index 27c744a..62adc91 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -10,7 +10,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() -// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") + parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 9f16c0d..7dc2ea4 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,6 +1,6 @@ # Fabric, see https://fabricmc.net/develop/ for new versions fabric_loader_version=0.15.0 -fabric_api_version=0.139.4+1.21.11 +fabric_api_version=0.133.14+1.21.9 optional_dependencies=fabric-api additional_modloaders=quilt diff --git a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java index 6ebb954..6212f81 100644 --- a/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java +++ b/fabric/src/main/java/net/lionarius/skinrestorer/fabric/mixin/MinecraftServerMixin.java @@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public abstract class MinecraftServerMixin { @Inject(method = "runServer", - at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Util;getNanos()J", ordinal = 0)) + at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0)) private void onServerStarted(CallbackInfo ci) { SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this); } diff --git a/forge/build.gradle b/forge/build.gradle index 588a178..c52c1d9 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -24,8 +24,7 @@ tasks.named('jarJar') { jar.finalizedBy('jarJar') minecraft { - mappings channel: 'official', version: minecraft_version -// mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" + mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" copyIdeResources = true //Calls processResources when in dev diff --git a/forge/gradle.properties b/forge/gradle.properties index 211825c..d1e8843 100644 --- a/forge/gradle.properties +++ b/forge/gradle.properties @@ -1,5 +1,5 @@ # Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions -forge_version=61.0.0 -forge_loader_version_range=[61,) +forge_version=59.0.0 +forge_loader_version_range=[59,) # Forge sometimes skips minor minecraft versions (like 1.20.5) -forge_minecraft_version=1.21.11 +forge_minecraft_version=1.21.9 diff --git a/gradle.properties b/gradle.properties index 7f59327..2ee5965 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,9 +3,9 @@ group=net.lionarius java_version=21 # Common -minecraft_version=1.21.11 -minecraft_version_list=1.21.11 -minecraft_version_range=[1.21.11, 1.22) +minecraft_version=1.21.9 +minecraft_version_list=1.21.9 +minecraft_version_range=[1.21.9, 1.22) mod_id=skinrestorer mod_name=SkinRestorer mod_version=2.4.3 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 7caa74f..0c9aca9 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -17,10 +17,10 @@ neoForge { accessTransformers.from(at.absolutePath) } -// parchment { -// minecraftVersion = parchment_minecraft -// mappingsVersion = parchment_version -// } + parchment { + minecraftVersion = parchment_minecraft + mappingsVersion = parchment_version + } runs { configureEach { diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties index b80f750..4ecb081 100644 --- a/neoforge/gradle.properties +++ b/neoforge/gradle.properties @@ -1,3 +1,3 @@ # NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions -neoforge_version=21.11.0-beta +neoforge_version=21.9.0-beta neoforge_loader_version_range=[4,) From 76e801c944ef8c8917ad1564df349a13d643b7e1 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Sat, 10 Jan 2026 14:55:31 +0300 Subject: [PATCH 05/13] use parchment --- common/build.gradle | 2 +- fabric/build.gradle | 2 +- forge/build.gradle | 3 +-- gradle.properties | 4 ++-- neoforge/build.gradle | 8 ++++---- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/common/build.gradle b/common/build.gradle index 88b391d..c168537 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -8,7 +8,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() -// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") + parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } compileOnly group: 'org.spongepowered', name: 'mixin', version: '0.8.5' diff --git a/fabric/build.gradle b/fabric/build.gradle index 27c744a..62adc91 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -10,7 +10,7 @@ dependencies { minecraft "com.mojang:minecraft:${minecraft_version}" mappings loom.layered { officialMojangMappings() -// parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") + parchment("org.parchmentmc.data:parchment-${parchment_minecraft}:${parchment_version}@zip") } modImplementation "net.fabricmc:fabric-loader:${fabric_loader_version}" diff --git a/forge/build.gradle b/forge/build.gradle index 588a178..c52c1d9 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -24,8 +24,7 @@ tasks.named('jarJar') { jar.finalizedBy('jarJar') minecraft { - mappings channel: 'official', version: minecraft_version -// mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" + mappings channel: 'parchment', version: "${parchment_minecraft}-${parchment_version}-${minecraft_version}" copyIdeResources = true //Calls processResources when in dev diff --git a/gradle.properties b/gradle.properties index 7f59327..8b7bb95 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,8 +21,8 @@ description=A server-side mod for managing skins. mineskin_client_version=3.0.6-SNAPSHOT # ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions -parchment_minecraft=1.21.8 -parchment_version=2025.09.14 +parchment_minecraft=1.21.11 +parchment_version=2025.12.20 # Publishing curseforge_id=443823 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 7caa74f..0c9aca9 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -17,10 +17,10 @@ neoForge { accessTransformers.from(at.absolutePath) } -// parchment { -// minecraftVersion = parchment_minecraft -// mappingsVersion = parchment_version -// } + parchment { + minecraftVersion = parchment_minecraft + mappingsVersion = parchment_version + } runs { configureEach { From ffd2a0dba741877cb8844f5e373a55dfccb95788 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Mon, 12 Jan 2026 23:28:32 +0300 Subject: [PATCH 06/13] update mineskin --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8b7bb95..8785f81 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ credits= description=A server-side mod for managing skins. # Dependencies -mineskin_client_version=3.0.6-SNAPSHOT +mineskin_client_version=3.2.1-SNAPSHOT # ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions parchment_minecraft=1.21.11 From e832e02fd4c37421929bec15a6400f2017a0a060 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Tue, 13 Jan 2026 02:04:21 +0300 Subject: [PATCH 07/13] add collection provider --- .../lionarius/skinrestorer/SkinRestorer.java | 2 + .../config/FirstJoinSkinProvider.java | 5 +- .../provider/BuiltInProviderConfig.java | 7 +- .../config/provider/ProvidersConfig.java | 17 +++- .../collection/CollectionProviderConfig.java | 32 +++++++ .../collection/CollectionSkinFile.java | 60 ++++++++++++ .../collection/CollectionSkinSource.java | 12 +++ .../CollectionSkinSourceListDeserializer.java | 30 ++++++ .../collection/CollectionSkinUrl.java | 41 +++++++++ .../ServerLoginPacketListenerImplMixin.java | 11 ++- .../skinrestorer/skin/SkinVariant.java | 5 +- .../skin/provider/CollectionSkinProvider.java | 91 +++++++++++++++++++ .../skin/provider/MineskinSkinProvider.java | 11 ++- .../skin/provider/SkinProvider.java | 2 + 14 files changed, 317 insertions(+), 9 deletions(-) create mode 100644 common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionProviderConfig.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinFile.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSource.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinUrl.java create mode 100644 common/src/main/java/net/lionarius/skinrestorer/skin/provider/CollectionSkinProvider.java diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index 1220221..43e2ef1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -88,6 +88,7 @@ public final class SkinRestorer { SkinRestorer.registerDefaultSkinProvider(MojangSkinProvider.PROVIDER_NAME, SkinProvider.MOJANG, SkinRestorer.getConfig().providersConfig().mojang()); SkinRestorer.registerDefaultSkinProvider(ElyBySkinProvider.PROVIDER_NAME, SkinProvider.ELY_BY, SkinRestorer.getConfig().providersConfig().ely_by()); SkinRestorer.registerDefaultSkinProvider(MineskinSkinProvider.PROVIDER_NAME, SkinProvider.MINESKIN, SkinRestorer.getConfig().providersConfig().mineskin()); + SkinRestorer.registerDefaultSkinProvider(CollectionSkinProvider.PROVIDER_NAME, SkinProvider.COLLECTION, SkinRestorer.getConfig().providersConfig().collection()); } private static void registerDefaultSkinProvider(String defaultName, SkinProvider provider, BuiltInProviderConfig config) { @@ -106,6 +107,7 @@ public final class SkinRestorer { MojangSkinProvider.reload(); ElyBySkinProvider.reload(); MineskinSkinProvider.reload(); + CollectionSkinProvider.reload(); } public static Collection applySkin(MinecraftServer server, Iterable targets, SkinValue value, boolean save) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java index 120f8de..64dc581 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java @@ -1,6 +1,7 @@ package net.lionarius.skinrestorer.config; import com.google.gson.annotations.SerializedName; +import net.lionarius.skinrestorer.skin.provider.CollectionSkinProvider; import net.lionarius.skinrestorer.skin.provider.ElyBySkinProvider; import net.lionarius.skinrestorer.skin.provider.MojangSkinProvider; @@ -8,7 +9,9 @@ public enum FirstJoinSkinProvider { @SerializedName(value = "MOJANG", alternate = {"mojang"}) MOJANG(MojangSkinProvider.PROVIDER_NAME), @SerializedName(value = "ELY.BY", alternate = {"ely.by", "ELY_BY", "ely_by"}) - ELY_BY(ElyBySkinProvider.PROVIDER_NAME); + ELY_BY(ElyBySkinProvider.PROVIDER_NAME), + @SerializedName(value = "COLLECTION", alternate = {"collection"}) + COLLECTION(CollectionSkinProvider.PROVIDER_NAME); private final String name; diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/BuiltInProviderConfig.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/BuiltInProviderConfig.java index c27e84c..3ea7a7c 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/config/provider/BuiltInProviderConfig.java +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/BuiltInProviderConfig.java @@ -8,8 +8,13 @@ public abstract class BuiltInProviderConfig implements GsonPostProcessable { protected String name; protected CacheConfig cache; + public BuiltInProviderConfig(String name, CacheConfig cache) { - this.enabled = true; + this(name, cache, true); + } + + public BuiltInProviderConfig(String name, CacheConfig cache, boolean enabled) { + this.enabled = enabled; this.name = name; this.cache = cache; } diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/ProvidersConfig.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/ProvidersConfig.java index 4e4f69b..e1de7f1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/config/provider/ProvidersConfig.java +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/ProvidersConfig.java @@ -1,23 +1,27 @@ package net.lionarius.skinrestorer.config.provider; import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.config.provider.collection.CollectionProviderConfig; import net.lionarius.skinrestorer.util.gson.GsonPostProcessable; public final class ProvidersConfig implements GsonPostProcessable { public static final ProvidersConfig DEFAULT = new ProvidersConfig( new MojangProviderConfig(), new ElyByProviderConfig(), - new MineskinProviderConfig() + new MineskinProviderConfig(), + new CollectionProviderConfig() ); private MojangProviderConfig mojang; private ElyByProviderConfig ely_by; private MineskinProviderConfig mineskin; + private CollectionProviderConfig collection; - public ProvidersConfig(MojangProviderConfig mojang, ElyByProviderConfig ely_by, MineskinProviderConfig mineskin) { + public ProvidersConfig(MojangProviderConfig mojang, ElyByProviderConfig ely_by, MineskinProviderConfig mineskin, CollectionProviderConfig collection) { this.mojang = mojang; this.ely_by = ely_by; this.mineskin = mineskin; + this.collection = collection; } public MojangProviderConfig mojang() { @@ -32,6 +36,10 @@ public final class ProvidersConfig implements GsonPostProcessable { return this.mineskin; } + public CollectionProviderConfig collection() { + return this.collection; + } + @Override public void gsonPostProcess() { if (this.mojang == null) { @@ -48,5 +56,10 @@ public final class ProvidersConfig implements GsonPostProcessable { SkinRestorer.LOGGER.warn("Mineskin provider config is null, using default"); this.mineskin = ProvidersConfig.DEFAULT.mineskin(); } + + if (this.collection == null) { + SkinRestorer.LOGGER.warn("Collection provider config is null, using default"); + this.collection = ProvidersConfig.DEFAULT.collection(); + } } } diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionProviderConfig.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionProviderConfig.java new file mode 100644 index 0000000..45d5c95 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionProviderConfig.java @@ -0,0 +1,32 @@ +package net.lionarius.skinrestorer.config.provider.collection; + +import com.google.gson.annotations.JsonAdapter; +import net.lionarius.skinrestorer.config.provider.BuiltInProviderConfig; +import net.lionarius.skinrestorer.config.provider.CacheConfig; +import net.lionarius.skinrestorer.skin.provider.CollectionSkinProvider; +import net.lionarius.skinrestorer.util.gson.GsonPostProcessable; + +import java.util.ArrayList; +import java.util.List; + +public final class CollectionProviderConfig extends BuiltInProviderConfig implements GsonPostProcessable { + private static final CacheConfig DEFAULT_CACHE_VALUE = new CacheConfig(true, 604800); + + @JsonAdapter(CollectionSkinSourceListDeserializer.class) + private List sources = new ArrayList<>(); + + public CollectionProviderConfig() { + super(CollectionSkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE, false); + } + + public List sources() { + return this.sources; + } + + @Override + public void gsonPostProcess() { + if (this.sources == null) { + this.sources = new ArrayList<>(); + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinFile.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinFile.java new file mode 100644 index 0000000..535e3a2 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinFile.java @@ -0,0 +1,60 @@ +package net.lionarius.skinrestorer.config.provider.collection; + +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.skin.SkinVariant; +import net.lionarius.skinrestorer.util.gson.GsonPostProcessable; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +public final class CollectionSkinFile implements CollectionSkinSource, GsonPostProcessable { + private String path = ""; + private SkinVariant variant = SkinVariant.CLASSIC; + + @Override + public @Nullable URI uri() { + if (this.path.isEmpty()) + return null; + + try { + var filePath = SkinRestorer.getConfigDir().resolve(this.path); + + if (!Files.exists(filePath)) { + SkinRestorer.LOGGER.warn("Skin file does not exist: {}", this.path); + return null; + } + + if (!Files.isRegularFile(filePath)) { + SkinRestorer.LOGGER.warn("Skin path is not a file: {}", this.path); + return null; + } + + if (!this.path.toLowerCase().endsWith(".png")) { + SkinRestorer.LOGGER.warn("Skin file is not a PNG file: {}", this.path); + return null; + } + + return filePath.toUri(); + } catch (Exception e) { + SkinRestorer.LOGGER.warn("Invalid file path: {}", this.path, e); + return null; + } + } + + @Override + public SkinVariant variant() { + return this.variant; + } + + @Override + public void gsonPostProcess() { + if (this.path == null) { + this.path = ""; + } + if (this.variant == null) { + this.variant = SkinVariant.CLASSIC; + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSource.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSource.java new file mode 100644 index 0000000..1a8579b --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSource.java @@ -0,0 +1,12 @@ +package net.lionarius.skinrestorer.config.provider.collection; + +import net.lionarius.skinrestorer.skin.SkinVariant; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; + +public interface CollectionSkinSource { + @Nullable URI uri(); + + SkinVariant variant(); +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java new file mode 100644 index 0000000..9535c66 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java @@ -0,0 +1,30 @@ +package net.lionarius.skinrestorer.config.provider.collection; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; + +public class CollectionSkinSourceListDeserializer implements JsonDeserializer> { + @Override + public List deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + List sources = new ArrayList<>(); + + if (json.isJsonArray()) { + for (JsonElement element : json.getAsJsonArray()) { + if (element.isJsonObject()) { + JsonObject obj = element.getAsJsonObject(); + if (obj.has("url")) { + sources.add(context.deserialize(obj, CollectionSkinUrl.class)); + } else if (obj.has("path")) { + sources.add(context.deserialize(obj, CollectionSkinFile.class)); + } + } + } + } + + return sources; + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinUrl.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinUrl.java new file mode 100644 index 0000000..6607219 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinUrl.java @@ -0,0 +1,41 @@ +package net.lionarius.skinrestorer.config.provider.collection; + +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.skin.SkinVariant; +import net.lionarius.skinrestorer.util.gson.GsonPostProcessable; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; + +public final class CollectionSkinUrl implements CollectionSkinSource, GsonPostProcessable { + private String url = ""; + private SkinVariant variant = SkinVariant.CLASSIC; + + @Override + public @Nullable URI uri() { + try { + if (this.url.isEmpty()) + return null; + + return new URI(this.url); + } catch (Exception e) { + SkinRestorer.LOGGER.warn("Invalid URI: {}", this.url, e); + return null; + } + } + + @Override + public SkinVariant variant() { + return this.variant; + } + + @Override + public void gsonPostProcess() { + if (this.url == null) { + this.url = ""; + } + if (this.variant == null) { + this.variant = SkinVariant.CLASSIC; + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java index 09a97dc..bda6c42 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java @@ -2,6 +2,7 @@ package net.lionarius.skinrestorer.mixin; import com.mojang.authlib.GameProfile; import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.config.FirstJoinSkinProvider; import net.lionarius.skinrestorer.skin.SkinValue; import net.lionarius.skinrestorer.skin.provider.SkinProviderContext; import net.lionarius.skinrestorer.util.PlayerUtils; @@ -53,9 +54,15 @@ public abstract class ServerLoginPacketListenerImplMixin { return null; } - if (originalSkin == null && SkinRestorer.getConfig().fetchSkinOnFirstJoin()) { + var config = SkinRestorer.getConfig(); + var provider = config.firstJoinSkinProvider(); + + var shouldFetch = (originalSkin == null && config.fetchSkinOnFirstJoin()) || + (originalSkin != null && config.forceFirstJoinSkinFetch() && provider != FirstJoinSkinProvider.MOJANG); + + if (shouldFetch) { var context = new SkinProviderContext( - SkinRestorer.getConfig().firstJoinSkinProvider().getName(), + provider.getName(), profile.name(), null ); diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/SkinVariant.java b/common/src/main/java/net/lionarius/skinrestorer/skin/SkinVariant.java index d30d5a6..e0494ce 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/SkinVariant.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/SkinVariant.java @@ -1,8 +1,11 @@ package net.lionarius.skinrestorer.skin; +import com.google.gson.annotations.SerializedName; + public enum SkinVariant { - + @SerializedName(value = "classic", alternate = {"CLASSIC"}) CLASSIC("classic"), + @SerializedName(value = "slim", alternate = {"SLIM"}) SLIM("slim"); private final String name; diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/CollectionSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/CollectionSkinProvider.java new file mode 100644 index 0000000..89970c2 --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/CollectionSkinProvider.java @@ -0,0 +1,91 @@ +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.common.util.concurrent.UncheckedExecutionException; +import com.mojang.authlib.properties.Property; +import it.unimi.dsi.fastutil.Pair; +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.config.provider.collection.CollectionSkinSource; +import net.lionarius.skinrestorer.skin.SkinVariant; +import net.lionarius.skinrestorer.util.Result; +import org.jetbrains.annotations.NotNull; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +public final class CollectionSkinProvider implements SkinProvider { + + public static final String PROVIDER_NAME = "collection"; + + private static LoadingCache> SKIN_CACHE; + + private static List> COLLECTION_SKINS; + + public static void reload() { + COLLECTION_SKINS = loadCollectionSkins(); + + createCache(); + } + + private static List> loadCollectionSkins() { + List> skins = new ArrayList<>(); + + var config = SkinRestorer.getConfig().providersConfig().collection(); + + for (CollectionSkinSource source : config.sources()) { + var uri = source.uri(); + if (uri != null) { + skins.add(Pair.of(uri, source.variant())); + } + } + + return skins; + } + + private static void createCache() { + var config = SkinRestorer.getConfig().providersConfig().collection(); + var time = config.cache().enabled() ? config.cache().duration() : 0; + + SKIN_CACHE = CacheBuilder.newBuilder() + .expireAfterWrite(time, TimeUnit.SECONDS) + .build(new CacheLoader<>() { + @Override + public @NotNull Optional load(@NotNull Integer key) throws Exception { + var skinEntry = COLLECTION_SKINS.get(key); + return MineskinSkinProvider.loadSkin(skinEntry.first(), skinEntry.second()); + } + }); + } + + @Override + public String getArgumentName() { + return "seed"; + } + + @Override + public boolean hasVariantSupport() { + return false; + } + + @Override + public Result, Exception> fetchSkin(String argument, SkinVariant variant) { + if (COLLECTION_SKINS.isEmpty()) { + return Result.error(new IllegalStateException("No collection skins configured")); + } + + var skinIndex = Math.abs(argument.hashCode()) % COLLECTION_SKINS.size(); + + try { + return Result.success(SKIN_CACHE.get(skinIndex)); + } catch (UncheckedExecutionException e) { + return Result.error((Exception) e.getCause()); + } catch (Exception e) { + return Result.error(e); + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MineskinSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MineskinSkinProvider.java index 0386927..e254a95 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MineskinSkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/MineskinSkinProvider.java @@ -22,6 +22,8 @@ import org.mineskin.response.QueueResponse; import java.net.InetSocketAddress; import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -94,13 +96,18 @@ public final class MineskinSkinProvider implements SkinProvider { } } - private static Optional loadSkin(URI uri, SkinVariant variant) throws Exception { + static Optional loadSkin(URI uri, SkinVariant variant) throws Exception { var mineskinVariant = switch (variant) { case CLASSIC -> Variant.CLASSIC; case SLIM -> Variant.SLIM; }; - var request = GenerateRequest.url(uri) + var request = "file".equals(uri.getScheme()) + ? GenerateRequest.upload(Files.newInputStream(Path.of(uri))) + .variant(mineskinVariant) + .name("skinrestorer-skin") + .visibility(Visibility.UNLISTED) + : GenerateRequest.url(uri) .variant(mineskinVariant) .name("skinrestorer-skin") .visibility(Visibility.UNLISTED); diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/SkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/SkinProvider.java index 8b5aad5..c588882 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/provider/SkinProvider.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/provider/SkinProvider.java @@ -13,6 +13,7 @@ public interface SkinProvider { MojangSkinProvider MOJANG = new MojangSkinProvider(); ElyBySkinProvider ELY_BY = new ElyBySkinProvider(); MineskinSkinProvider MINESKIN = new MineskinSkinProvider(); + CollectionSkinProvider COLLECTION = new CollectionSkinProvider(); SkinShuffleSkinProvider SKIN_SHUFFLE = new SkinShuffleSkinProvider(); Set BUILTIN_PROVIDER_NAMES = ImmutableSet.of( @@ -20,6 +21,7 @@ public interface SkinProvider { MojangSkinProvider.PROVIDER_NAME, ElyBySkinProvider.PROVIDER_NAME, MineskinSkinProvider.PROVIDER_NAME, + CollectionSkinProvider.PROVIDER_NAME, SkinShuffleSkinProvider.PROVIDER_NAME ); From 1aa606038eeca6f3fefb686114ba734801de444b Mon Sep 17 00:00:00 2001 From: Lionarius Date: Tue, 13 Jan 2026 02:33:31 +0300 Subject: [PATCH 08/13] add `forceFirstJoinSkinFetch` config --- .../main/java/net/lionarius/skinrestorer/config/Config.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/Config.java b/common/src/main/java/net/lionarius/skinrestorer/config/Config.java index d8acb49..4f405a6 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/config/Config.java +++ b/common/src/main/java/net/lionarius/skinrestorer/config/Config.java @@ -22,6 +22,8 @@ public final class Config implements GsonPostProcessable { private boolean fetchSkinOnFirstJoin = true; + private boolean forceFirstJoinSkinFetch = false; + private FirstJoinSkinProvider firstJoinSkinProvider = FirstJoinSkinProvider.MOJANG; private String proxy = ""; @@ -47,6 +49,10 @@ public final class Config implements GsonPostProcessable { return this.fetchSkinOnFirstJoin; } + public boolean forceFirstJoinSkinFetch() { + return this.forceFirstJoinSkinFetch; + } + public FirstJoinSkinProvider firstJoinSkinProvider() { return this.firstJoinSkinProvider; } From 7dcd1d4e81d8815b34be22aa5c81af7d2e649ee6 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Tue, 13 Jan 2026 19:57:33 +0300 Subject: [PATCH 09/13] reorder mixins --- common/src/main/resources/skinrestorer.mixins.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/resources/skinrestorer.mixins.json b/common/src/main/resources/skinrestorer.mixins.json index 741efe1..db8f62e 100644 --- a/common/src/main/resources/skinrestorer.mixins.json +++ b/common/src/main/resources/skinrestorer.mixins.json @@ -7,10 +7,10 @@ "refmap": "${mod_id}.refmap.json", "mixins": [ "ChunkMapAccessor", + "PlayerAccessor", "PlayerListMixin", "ServerLoginPacketListenerImplMixin", - "TrackedEntityAccessorInvoker", - "PlayerAccessor" + "TrackedEntityAccessorInvoker" ], "injectors": { "defaultRequire": 1 From 2e655ce1f3cbd1ff52e571f385418c07a5903422 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Wed, 14 Jan 2026 19:06:02 +0300 Subject: [PATCH 10/13] bump version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8785f81..2b6f914 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version_list=1.21.11 minecraft_version_range=[1.21.11, 1.22) mod_id=skinrestorer mod_name=SkinRestorer -mod_version=2.4.3 +mod_version=2.5.0 mod_author=Lionarius mod_homepage=https://modrinth.com/mod/skinrestorer mod_sources=https://github.com/Suiranoil/SkinRestorer From 301428147db84dcc495b3a2675c489b9c5531fc2 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Wed, 14 Jan 2026 21:53:09 +0300 Subject: [PATCH 11/13] update CHANGELOG --- CHANGELOG.md | 5 +++++ CHANGELOG_LATEST.md | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 27ff975..03007f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.5.0] - 2026-01-14 +### Added +- Added collection skin provider (allows assigning random skins from a predefined set) +- Added `forceFirstJoinSkinFetch` config option to force skin fetch on first join even if player already has a skin + ## [2.4.3] - 2025-07-25 ### Fixed - Fixed crash on client when loading player head skin (fixes [#63](https://github.com/Suiranoil/SkinRestorer/issues/63) and [#64](https://github.com/Suiranoil/SkinRestorer/issues/64)) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index 21da2bd..9f04717 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -1,3 +1,3 @@ -### Fixed -- Fixed crash on client when loading player head skin (fixes [#63](https://github.com/Suiranoil/SkinRestorer/issues/63) and [#64](https://github.com/Suiranoil/SkinRestorer/issues/64)) -- Fixed server freeze when loading player head skin +### Added +- Added collection skin provider (allows assigning random skins from a predefined set) (see [wiki](https://github.com/Suiranoil/SkinRestorer/wiki/Configuration#providerscollection)) +- Added `forceFirstJoinSkinFetch` config option to force skin fetch on first join even if player already has a skin (see [wiki](https://github.com/Suiranoil/SkinRestorer/wiki/Configuration#forcefirstjoinskinfetch)) From 0524b5dab8e3792f39c0cd6c882e76f2f6ea5c57 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Wed, 14 Jan 2026 23:39:53 +0300 Subject: [PATCH 12/13] the worst bug I encountered in gson 2.10.1 --- .../collection/CollectionSkinSourceListDeserializer.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java index 9535c66..7dff675 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/config/provider/collection/CollectionSkinSourceListDeserializer.java @@ -6,7 +6,12 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; -public class CollectionSkinSourceListDeserializer implements JsonDeserializer> { +public class CollectionSkinSourceListDeserializer implements JsonSerializer>, JsonDeserializer> { + @Override + public JsonElement serialize(List src, Type typeOfT, JsonSerializationContext context) { + return context.serialize(src, List.class); + } + @Override public List deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { From a0d8f3d5180bbb3322f6cd144d2d77d0198f71fc Mon Sep 17 00:00:00 2001 From: Lionarius Date: Wed, 14 Jan 2026 23:44:30 +0300 Subject: [PATCH 13/13] another backport to 1.21.9 --- gradle.properties | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle.properties b/gradle.properties index e8684b1..58a7e39 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ java_version=21 # Common minecraft_version=1.21.9 -minecraft_version_list=1.21.9 -minecraft_version_range=[1.21.9, 1.22) +minecraft_version_list=1.21.9,1.21.10 +minecraft_version_range=[1.21.9, 1.21.10] mod_id=skinrestorer mod_name=SkinRestorer mod_version=2.5.0 @@ -21,8 +21,8 @@ description=A server-side mod for managing skins. mineskin_client_version=3.2.1-SNAPSHOT # ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions -parchment_minecraft=1.21.11 -parchment_version=2025.12.20 +parchment_minecraft=1.21.9 +parchment_version=2025.10.05 # Publishing curseforge_id=443823