From 45492368be370c0118140f1094297ff37fa63c50 Mon Sep 17 00:00:00 2001 From: CaveNightingale Date: Mon, 31 Oct 2022 19:59:36 +0800 Subject: [PATCH] Fix the bug that fetching skin on the main thread --- .../mixin/PlayerManagerMixin.java | 18 +---- .../mixin/ServerLoginNetworkHandlerMixin.java | 68 +++++++++++++++++++ src/main/resources/skinrestorer.mixins.json | 3 +- 3 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginNetworkHandlerMixin.java diff --git a/src/main/java/net/lionarius/skinrestorer/mixin/PlayerManagerMixin.java b/src/main/java/net/lionarius/skinrestorer/mixin/PlayerManagerMixin.java index 9a2b564..b635dbb 100644 --- a/src/main/java/net/lionarius/skinrestorer/mixin/PlayerManagerMixin.java +++ b/src/main/java/net/lionarius/skinrestorer/mixin/PlayerManagerMixin.java @@ -1,10 +1,6 @@ package net.lionarius.skinrestorer.mixin; -import com.mojang.authlib.properties.Property; -import net.lionarius.skinrestorer.MojangSkinProvider; import net.lionarius.skinrestorer.SkinRestorer; -import net.lionarius.skinrestorer.SkinStorage; -import net.minecraft.network.ClientConnection; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import org.spongepowered.asm.mixin.Mixin; @@ -14,27 +10,15 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.List; -import java.util.Objects; @Mixin(PlayerManager.class) public abstract class PlayerManagerMixin { - private static void applySkin(ServerPlayerEntity playerEntity, Property skin) { - playerEntity.getGameProfile().getProperties().removeAll("textures"); - playerEntity.getGameProfile().getProperties().put("textures", skin); - } + @Shadow public abstract List getPlayerList(); - @Inject(method = "onPlayerConnect", at = @At(value = "HEAD")) - private void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, CallbackInfo ci) { - if (SkinRestorer.getSkinStorage().getSkin(player.getUuid()) == SkinStorage.DEFAULT_SKIN) - SkinRestorer.getSkinStorage().setSkin(player.getUuid(), MojangSkinProvider.getSkin(player.getGameProfile().getName())); - - applySkin(player, SkinRestorer.getSkinStorage().getSkin(player.getUuid())); - } - @Inject(method = "remove", at = @At("TAIL")) private void remove(ServerPlayerEntity player, CallbackInfo ci) { SkinRestorer.getSkinStorage().removeSkin(player.getUuid()); diff --git a/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginNetworkHandlerMixin.java b/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginNetworkHandlerMixin.java new file mode 100644 index 0000000..fd6b2c7 --- /dev/null +++ b/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginNetworkHandlerMixin.java @@ -0,0 +1,68 @@ +package net.lionarius.skinrestorer.mixin; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import net.lionarius.skinrestorer.MojangSkinProvider; +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.SkinStorage; +import net.minecraft.network.packet.c2s.login.LoginHelloC2SPacket; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.concurrent.CompletableFuture; + +@Mixin(ServerLoginNetworkHandler.class) +public abstract class ServerLoginNetworkHandlerMixin { + @Shadow @Nullable GameProfile profile; + + @Shadow protected abstract GameProfile toOfflineProfile(GameProfile profile); + + @Shadow @Final + static Logger LOGGER; + private CompletableFuture skinrestorer_pendingSkin; + @Inject(method = "onHello", at = @At("RETURN")) + public void onHelloReturn(LoginHelloC2SPacket packet, CallbackInfo ci) { + assert profile != null; + GameProfile profile1; + if (!profile.isComplete()) { + profile1 = profile = toOfflineProfile(this.profile); + } else { + profile1 = profile; + } + + skinrestorer_pendingSkin = CompletableFuture.supplyAsync(() -> { + LOGGER.debug("Fetching {}'s skin", profile1.getName()); + if (SkinRestorer.getSkinStorage().getSkin(profile1.getId()) == SkinStorage.DEFAULT_SKIN) + SkinRestorer.getSkinStorage().setSkin(profile1.getId(), MojangSkinProvider.getSkin(profile1.getName())); + + return SkinRestorer.getSkinStorage().getSkin(profile1.getId()); + }); + } + + @Inject(method = "acceptPlayer", at = @At("HEAD"), cancellable = true) + public void waitForSkin(CallbackInfo ci) { + if (skinrestorer_pendingSkin != null && !skinrestorer_pendingSkin.isDone()) { + ci.cancel(); + } + } + + private static void applyRestoreSkin(ServerPlayerEntity playerEntity, Property skin) { + playerEntity.getGameProfile().getProperties().removeAll("textures"); + playerEntity.getGameProfile().getProperties().put("textures", skin); + } + + @Inject(method = "addToServer", at = @At("HEAD")) + public void applyRestoredSkinHook(ServerPlayerEntity player, CallbackInfo ci) { + if (skinrestorer_pendingSkin != null) { + applyRestoreSkin(player, skinrestorer_pendingSkin.getNow(SkinStorage.DEFAULT_SKIN)); + } + } +} diff --git a/src/main/resources/skinrestorer.mixins.json b/src/main/resources/skinrestorer.mixins.json index 32a5382..49bf6e6 100644 --- a/src/main/resources/skinrestorer.mixins.json +++ b/src/main/resources/skinrestorer.mixins.json @@ -5,7 +5,8 @@ "compatibilityLevel": "JAVA_16", "mixins": [ "CommandManagerMixin", - "PlayerManagerMixin" + "PlayerManagerMixin", + "ServerLoginNetworkHandlerMixin" ], "injectors": { "defaultRequire": 1