diff --git a/CHANGELOG.md b/CHANGELOG.md index 12e4da3..27ff975 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.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)) +- Fixed server freeze when loading player head skin + ## [2.4.2] - 2025-07-13 ### Fixed - Fix crash when head profile name is null (fixes [#60](https://github.com/Suiranoil/SkinRestorer/issues/60) and [#61](https://github.com/Suiranoil/SkinRestorer/issues/61)) diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md index bf4e1a8..21da2bd 100644 --- a/CHANGELOG_LATEST.md +++ b/CHANGELOG_LATEST.md @@ -1,2 +1,3 @@ ### Fixed -- Fix crash when head profile name is null (fixes [#60](https://github.com/Suiranoil/SkinRestorer/issues/60) and [#61](https://github.com/Suiranoil/SkinRestorer/issues/61)) +- 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 diff --git a/buildSrc/src/main/groovy/multiloader-publish.gradle b/buildSrc/src/main/groovy/multiloader-publish.gradle index affceee..9817b23 100644 --- a/buildSrc/src/main/groovy/multiloader-publish.gradle +++ b/buildSrc/src/main/groovy/multiloader-publish.gradle @@ -6,6 +6,14 @@ publishMods { file = project.layout.buildDirectory.file("libs/${project.archivesBaseName}-${project.version}.jar").map { it.asFile }.getOrNull() modLoaders.add(project.name) + + if (project.hasProperty('additional_modloaders') && !additional_modloaders.isEmpty()) + { + def loaders = additional_modloaders.split(',') + for (loader in loaders) + modLoaders.add(loader) + } + type = STABLE version = project.version displayName = "[${project.name.capitalize()}] ${mod_name} ${mod_version}" diff --git a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java index d8ac710..f2270f1 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java +++ b/common/src/main/java/net/lionarius/skinrestorer/SkinRestorer.java @@ -19,6 +19,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.storage.LevelResource; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +38,7 @@ public final class SkinRestorer { private static Path configDir; private static Config config; private static TickedScheduler tickedScheduler; + private static MinecraftServer minecraftServer; private SkinRestorer() {} @@ -60,6 +62,10 @@ public final class SkinRestorer { return SkinRestorer.tickedScheduler; } + public static @Nullable MinecraftServer getMinecraftServer() { + return SkinRestorer.minecraftServer; + } + public static Optional getProvider(String name) { return Optional.ofNullable(SkinRestorer.providersRegistry.get(name)); } @@ -173,6 +179,14 @@ public final class SkinRestorer { SkinRestorer.skinStorage = new SkinStorage(new SkinIO(worldSkinDirectory)); SkinRestorer.tickedScheduler = new TickedScheduler(server); server.addTickable(SkinRestorer.tickedScheduler); + + SkinRestorer.minecraftServer = server; + } + + public static void onServerStopped(MinecraftServer server) { + SkinRestorer.skinStorage = null; + SkinRestorer.tickedScheduler = null; + SkinRestorer.minecraftServer = null; } public static void onCommandRegister(CommandDispatcher dispatcher) { diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/ChunkMapAccessor.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/ChunkMapAccessor.java index 2d36edb..128d737 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/ChunkMapAccessor.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/ChunkMapAccessor.java @@ -9,5 +9,5 @@ import org.spongepowered.asm.mixin.gen.Accessor; public interface ChunkMapAccessor { @Accessor - Int2ObjectMap getEntityMap(); + Int2ObjectMap getEntityMap(); } 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/SkullBlockEntityMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java index 5415368..21d4d38 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java @@ -3,6 +3,7 @@ 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; @@ -11,6 +12,7 @@ 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; @@ -25,7 +27,11 @@ public abstract class SkullBlockEntityMixin { if (name == null) return; - var profileOpt = services.profileCache().get(name); + 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); } @@ -43,6 +49,9 @@ public abstract class SkullBlockEntityMixin { @Unique private static void skinrestorer$replaceSkin(Optional profileOpt, CallbackInfoReturnable>> cir) { + if (SkinRestorer.getMinecraftServer() == null) + return; + if (profileOpt.isEmpty()) return; @@ -54,7 +63,7 @@ public abstract class SkullBlockEntityMixin { PlayerUtils.applyRestoredSkin(profile, skin.value()); return Optional.of(profile); - })); + }, Util.backgroundExecutor().forName("getProfile"))); } } } 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 4a8d500..42f5cde 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java @@ -50,14 +50,14 @@ public final class PlayerUtils { ) )); - var trackedEntity = (TrackedEntityAccessorInvoker) ((ChunkMapAccessor) chunkMap).getEntityMap().get(player.getId()); + var trackedEntity = ((ChunkMapAccessor) chunkMap).getEntityMap().get(player.getId()); if (trackedEntity != null) { var seenBy = Set.copyOf(trackedEntity.getSeenBy()); for (var observerConnection : seenBy) { var observer = observerConnection.getPlayer(); trackedEntity.invokeRemovePlayer(observer); - var trackedObserverEntity = (TrackedEntityAccessorInvoker) ((ChunkMapAccessor) chunkMap).getEntityMap().get(observer.getId()); + var trackedObserverEntity = ((ChunkMapAccessor) chunkMap).getEntityMap().get(observer.getId()); if (trackedObserverEntity != null) { trackedObserverEntity.invokeRemovePlayer(player); trackedObserverEntity.invokeUpdatePlayer(player); diff --git a/common/src/main/resources/META-INF/accesstransformer.cfg b/common/src/main/resources/META-INF/accesstransformer.cfg new file mode 100644 index 0000000..8c8e31a --- /dev/null +++ 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 new file mode 100644 index 0000000..28ef0d1 --- /dev/null +++ b/common/src/main/resources/skinrestorer.accesswidener @@ -0,0 +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 c8366d5..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", - "SkullBlockEntityMixin" + "SkullBlockEntityMixin", + "GameProfileCacheAccessor" ], "injectors": { "defaultRequire": 1 diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 217fa2d..f441b3f 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -3,3 +3,4 @@ fabric_loader_version=0.15.0 fabric_api_version=0.100.1+1.21 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 f14d1ce..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 @@ -15,4 +15,10 @@ public abstract class MinecraftServerMixin { private void onServerStarted(CallbackInfo ci) { SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this); } + + @Inject(method = "runServer", + at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;onServerExit()V")) + private void onServerStopped(CallbackInfo ci) { + SkinRestorer.Events.onServerStopped((MinecraftServer) (Object) this); + } } diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 85c219d..8dee4fa 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -24,6 +24,7 @@ "${mod_id}.mixins.json", "${mod_id}.fabric.mixins.json" ], + "accessWidener": "${mod_id}.accesswidener", "depends": { "fabricloader": ">=${fabric_loader_version}", "minecraft": ">=${minecraft_version}", diff --git a/forge/build.gradle b/forge/build.gradle index 143574b..eebb369 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -33,7 +33,7 @@ minecraft { // Automatically enable forge AccessTransformers if the file exists // This location is hardcoded in Forge and can not be changed. // https://github.com/MinecraftForge/MinecraftForge/blob/be1698bb1554f9c8fa2f58e32b9ab70bc4385e60/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java#L123 - def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') + def at = project(":common").file('src/main/resources/META-INF/accesstransformer.cfg') if (at.exists()) { accessTransformer = at } @@ -88,3 +88,7 @@ sourceSets.each { it.output.resourcesDir = dir it.java.destinationDirectory = dir } + +tasks.named('processResources') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE +} diff --git a/forge/src/main/java/net/lionarius/skinrestorer/forge/SkinRestorerForge.java b/forge/src/main/java/net/lionarius/skinrestorer/forge/SkinRestorerForge.java index ef34aa0..b46d501 100644 --- a/forge/src/main/java/net/lionarius/skinrestorer/forge/SkinRestorerForge.java +++ b/forge/src/main/java/net/lionarius/skinrestorer/forge/SkinRestorerForge.java @@ -4,6 +4,7 @@ import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.server.ServerStartedEvent; +import net.minecraftforge.event.server.ServerStoppedEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @@ -27,4 +28,9 @@ public final class SkinRestorerForge { public static void onServerStarted(ServerStartedEvent event) { SkinRestorer.Events.onServerStarted(event.getServer()); } + + @SubscribeEvent + public static void onServerStopped(ServerStoppedEvent event) { + SkinRestorer.Events.onServerStopped(event.getServer()); + } } diff --git a/gradle.properties b/gradle.properties index 9314c48..7ac6010 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ minecraft_version_list=1.21,1.21.1,1.21.2,1.21.3,1.21.4 minecraft_version_range=[1.21, 1.21.4] mod_id=skinrestorer mod_name=SkinRestorer -mod_version=2.4.2 +mod_version=2.4.3 mod_author=Lionarius mod_homepage=https://modrinth.com/mod/skinrestorer mod_sources=https://github.com/Suiranoil/SkinRestorer diff --git a/neoforge/build.gradle b/neoforge/build.gradle index 72eb58a..0385d9e 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -12,9 +12,9 @@ neoForge { version = neoforge_version // Automatically enable neoforge AccessTransformers if the file exists - def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') + def at = project(":common").file('src/main/resources/META-INF/accesstransformer.cfg') if (at.exists()) { - minecraft.accessTransformers.file(at) + accessTransformers.from(at.absolutePath) } parchment { diff --git a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/SkinRestorerNeoForge.java b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/SkinRestorerNeoForge.java index b18b23c..e03e127 100644 --- a/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/SkinRestorerNeoForge.java +++ b/neoforge/src/main/java/net/lionarius/skinrestorer/neoforge/SkinRestorerNeoForge.java @@ -7,6 +7,7 @@ import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; @Mod(SkinRestorer.MOD_ID) @EventBusSubscriber(modid = SkinRestorer.MOD_ID) @@ -28,4 +29,9 @@ public final class SkinRestorerNeoForge { public static void onServerStarted(ServerStartedEvent event) { SkinRestorer.Events.onServerStarted(event.getServer()); } + + @SubscribeEvent + public static void onServerStopped(ServerStoppedEvent event) { + SkinRestorer.Events.onServerStopped(event.getServer()); + } }