From e65501e6209b242e93258fd946898aa0cd3783e5 Mon Sep 17 00:00:00 2001 From: Lionarius Date: Sat, 5 Jul 2025 19:15:22 +0300 Subject: [PATCH] support custom skins for player head block --- .../mixin/SkullBlockEntityMixin.java | 54 +++++++++++++++++++ .../skinrestorer/skin/SkinStorage.java | 9 +++- .../skinrestorer/util/PlayerUtils.java | 7 +++ .../main/resources/skinrestorer.mixins.json | 3 +- 4 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java 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..e90370a --- /dev/null +++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java @@ -0,0 +1,54 @@ +package net.lionarius.skinrestorer.mixin; + +import com.mojang.authlib.GameProfile; +import net.lionarius.skinrestorer.SkinRestorer; +import net.lionarius.skinrestorer.util.PlayerUtils; +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.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) { + var profileOpt = services.profileCache().get(name); + + skinrestorer$replaceSkin(profileOpt, cir); + } + + @Inject(method = "fetchProfileById", at = @At("HEAD"), + cancellable = true) + private static void fetchProfileById(UUID id, Services services, BooleanSupplier cacheUninitialized, CallbackInfoReturnable>> cir) { + var profileOpt = services.profileCache().get(id); + + skinrestorer$replaceSkin(profileOpt, cir); + } + + @Unique + private static void skinrestorer$replaceSkin(Optional profileOpt, CallbackInfoReturnable>> cir) { + 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); + })); + } + } +} diff --git a/common/src/main/java/net/lionarius/skinrestorer/skin/SkinStorage.java b/common/src/main/java/net/lionarius/skinrestorer/skin/SkinStorage.java index a50a623..396243c 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/skin/SkinStorage.java +++ b/common/src/main/java/net/lionarius/skinrestorer/skin/SkinStorage.java @@ -17,15 +17,22 @@ public class SkinStorage { return this.skinMap.containsKey(uuid) || this.skinIO.skinExists(uuid); } - public SkinValue getSkin(UUID uuid) { + public SkinValue getSkin(UUID uuid, boolean cache) { if (!skinMap.containsKey(uuid)) { var skin = skinIO.loadSkin(uuid); + if (!cache) + return skin; + setSkin(uuid, skin); } return skinMap.get(uuid); } + public SkinValue getSkin(UUID uuid) { + return this.getSkin(uuid, true); + } + public void removeSkin(UUID uuid, boolean save) { var skin = skinMap.remove(uuid); if (skin != null && save) 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 d5cbe8f..93bdbbe 100644 --- a/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java +++ b/common/src/main/java/net/lionarius/skinrestorer/util/PlayerUtils.java @@ -96,6 +96,13 @@ public final class PlayerUtils { } } + public static GameProfile cloneGameProfile(GameProfile profile) { + 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.getProperties().get(TEXTURES_KEY), null); } diff --git a/common/src/main/resources/skinrestorer.mixins.json b/common/src/main/resources/skinrestorer.mixins.json index 224662d..c8366d5 100644 --- a/common/src/main/resources/skinrestorer.mixins.json +++ b/common/src/main/resources/skinrestorer.mixins.json @@ -9,7 +9,8 @@ "ChunkMapAccessor", "PlayerListMixin", "ServerLoginPacketListenerImplMixin", - "TrackedEntityAccessorInvoker" + "TrackedEntityAccessorInvoker", + "SkullBlockEntityMixin" ], "injectors": { "defaultRequire": 1