From dc51271fe5165e33ef4c94b90199777b3628ebce Mon Sep 17 00:00:00 2001 From: MATRIX-feather Date: Thu, 28 Dec 2023 16:07:28 +0800 Subject: [PATCH] =?UTF-8?q?misc:=20=E4=BD=BF=E7=94=A8V2=E7=89=88=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E6=9D=A5=E8=8E=B7=E5=8F=96=E7=9A=AE=E8=82=A4=E3=80=81?= =?UTF-8?q?=E6=9B=B4=E6=8D=A2=E7=9A=AE=E8=82=A4=E5=89=8D=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E4=B9=8B=E5=89=8D=E7=9A=84Tab=E7=9A=AE=E8=82=A4=20fix:=20?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=A6=BB=E7=BA=BF=E5=AD=98=E5=82=A8=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E7=9B=B8=E5=85=B3=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/xiamomc/morph/MorphManager.java | 3 - .../renderer/network/PacketFactory.java | 14 ++ .../network/listeners/SpawnPacketHandler.java | 21 ++- .../network/registries/EntryIndex.java | 3 + .../network/registries/RenderRegistry.java | 20 +-- .../morph/events/CommonEventProcessor.java | 3 - .../xiamomc/morph/misc/DisguiseState.java | 37 +---- .../morph/misc/DisguiseStateGenerator.java | 15 +- .../morph/misc/skins/PlayerSkinProvider.java | 137 ++++++++++-------- .../xiamomc/morph/misc/skins/SingleSkin.java | 43 ++++-- .../xiamomc/morph/misc/skins/SkinStore.java | 42 ++++-- .../morph/misc/skins/SkinStoreRoot.java | 4 + .../providers/PlayerDisguiseProvider.java | 19 --- .../xiamomc/morph/utilities/NbtUtils.java | 7 + 14 files changed, 212 insertions(+), 156 deletions(-) diff --git a/src/main/java/xiamomc/morph/MorphManager.java b/src/main/java/xiamomc/morph/MorphManager.java index a3cb2479..6cbf557a 100644 --- a/src/main/java/xiamomc/morph/MorphManager.java +++ b/src/main/java/xiamomc/morph/MorphManager.java @@ -661,10 +661,7 @@ else if (!provider.isValid(key)) var compound = provider.getNbtCompound(outComingState, targetEntity); if (compound != null) - { - outComingState.setCachedNbtString(NbtUtils.getCompoundString(compound)); outComingState.getDisguiseWrapper().mergeCompound(compound); - } // 如果此伪装可以同步给客户端,那么初始化客户端状态 if (provider.validForClient(outComingState)) diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java index 7660b6f7..b8107d92 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/PacketFactory.java @@ -78,6 +78,9 @@ public List buildSpawnPackets(Player player, DisplayParameters gameProfile.setName(subStr); } + if (gameProfile.getName().isBlank()) + throw new IllegalArgumentException("GameProfile name is empty!"); + var packetPlayerInfo = new ClientboundPlayerInfoUpdatePacket( EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER), new ClientboundPlayerInfoUpdatePacket.Entry( @@ -88,6 +91,17 @@ public List buildSpawnPackets(Player player, DisplayParameters spawnUUID = uuid; packets.add(PacketContainer.fromPacket(packetPlayerInfo)); + + var watcher = parameters.getWatcher(); + var lastUUID = watcher.getOrDefault(EntryIndex.TABLIST_UUID, null); + + if (lastUUID != null) + { + var packetTabRemove = new ClientboundPlayerInfoRemovePacket(List.of(lastUUID)); + packets.add(PacketContainer.fromPacket(packetTabRemove)); + } + + parameters.getWatcher().write(EntryIndex.TABLIST_UUID, uuid); } var pitch = player.getPitch(); diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java index aa9fff34..c75675a0 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/listeners/SpawnPacketHandler.java @@ -8,20 +8,17 @@ import com.comphenix.protocol.injector.GamePhase; import com.mojang.authlib.GameProfile; import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; +import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket; import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.GameType; import org.bukkit.Bukkit; -import org.bukkit.Server; import org.bukkit.craftbukkit.v1_20_R3.entity.CraftPlayer; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.spigotmc.SpigotWorldConfig; -import xiamomc.morph.MorphPlugin; import xiamomc.morph.backends.server.renderer.network.DisplayParameters; import xiamomc.morph.backends.server.renderer.network.PacketFactory; +import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.SingleWatcher; import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.types.PlayerWatcher; import xiamomc.morph.backends.server.renderer.network.registries.EntryIndex; import xiamomc.morph.backends.server.renderer.network.registries.RenderRegistry; @@ -51,7 +48,8 @@ public SpawnPacketHandler() registry.onRegister(this, ep -> refreshStateForPlayer(ep.player(), getAffectedPlayers(ep.player()))); - registry.onUnRegister(this, this::unDisguiseForPlayer); + registry.onUnRegister(this, ep -> + unDisguiseForPlayer(ep.player(), ep.parameters())); } private List getAffectedPlayers(Player sourcePlayer) @@ -59,7 +57,7 @@ private List getAffectedPlayers(Player sourcePlayer) return WatcherUtils.getAffectedPlayers(sourcePlayer); } - private void unDisguiseForPlayer(@Nullable Player player) + private void unDisguiseForPlayer(@Nullable Player player, SingleWatcher disguiseWatcher) { if (player == null) return; @@ -75,6 +73,15 @@ private void unDisguiseForPlayer(@Nullable Player player) var removePacket = new ClientboundRemoveEntitiesPacket(player.getEntityId()); var rmPacketContainer = PacketContainer.fromPacket(removePacket); + + var lastUUID = disguiseWatcher.getOrDefault(EntryIndex.TABLIST_UUID, null); + if (lastUUID != null) + { + spawnPackets.add(PacketContainer.fromPacket( + new ClientboundPlayerInfoRemovePacket(List.of(lastUUID)) + )); + } + affectedPlayers.forEach(p -> { protocolManager.sendServerPacket(p, rmPacketContainer); diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java index fb839cd3..52b57ef6 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/EntryIndex.java @@ -1,6 +1,7 @@ package xiamomc.morph.backends.server.renderer.network.registries; import com.mojang.authlib.GameProfile; +import net.minecraft.Util; import org.bukkit.entity.EntityType; import xiamomc.morph.backends.server.renderer.network.datawatcher.watchers.SingleWatcher; import xiamomc.morph.misc.DisguiseEquipment; @@ -14,4 +15,6 @@ public class EntryIndex public static final RegistryKey EQUIPMENT = RegistryKey.of("equip", new DisguiseEquipment()); public static final RegistryKey DISPLAY_FAKE_EQUIPMENT = RegistryKey.of("display_fake_equip", false); + + public static final RegistryKey TABLIST_UUID = RegistryKey.of("tablist_uuid", Util.NIL_UUID); } diff --git a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java index 46097d3a..d61cf840 100644 --- a/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java +++ b/src/main/java/xiamomc/morph/backends/server/renderer/network/registries/RenderRegistry.java @@ -22,7 +22,7 @@ public record EventParameters(Player player, SingleWatcher parameters) } private final Map> onRegisterConsumers = new Object2ObjectOpenHashMap<>(); - private final Map> unRegisterConsumers = new Object2ObjectOpenHashMap<>(); + private final Map> unRegisterConsumers = new Object2ObjectOpenHashMap<>(); private final Map> onRegistryChangeConsumers = new Object2ObjectOpenHashMap<>(); public void onRegistryChange(Object source, Consumer consumer) @@ -41,14 +41,14 @@ private void callRegister(Player player, SingleWatcher watcher) onRegisterConsumers.forEach((source, consumer) -> consumer.accept(ep)); } - public void onUnRegister(Object source, Consumer consumer) + public void onUnRegister(Object source, Consumer consumer) { unRegisterConsumers.put(source, consumer); } - private void callUnregister(Player player) + private void callUnregister(Player player, SingleWatcher watcher) { - unRegisterConsumers.forEach((source, consumer) -> consumer.accept(player)); + unRegisterConsumers.forEach((source, consumer) -> consumer.accept(new EventParameters(player, watcher))); } //region Registry @@ -74,8 +74,8 @@ public void unregister(Player player) public void unregister(UUID uuid) { - watcherMap.remove(uuid); - callUnregister(Bukkit.getPlayer(uuid)); + var watcher = watcherMap.remove(uuid); + callUnregister(Bukkit.getPlayer(uuid), watcher); } /** @@ -112,10 +112,12 @@ public void register(@NotNull UUID uuid, @NotNull SingleWatcher watcher) public void reset() { - var players = watcherMap.keySet().stream().toList(); - watcherMap.clear(); + watcherMap.forEach((uuid, watcher) -> + { + callRegister(Bukkit.getPlayer(uuid), watcher); + }); - players.forEach(uuid -> callUnregister(Bukkit.getPlayer(uuid))); + watcherMap.clear(); } //endregion Registry diff --git a/src/main/java/xiamomc/morph/events/CommonEventProcessor.java b/src/main/java/xiamomc/morph/events/CommonEventProcessor.java index 89d33297..553fc6b3 100644 --- a/src/main/java/xiamomc/morph/events/CommonEventProcessor.java +++ b/src/main/java/xiamomc/morph/events/CommonEventProcessor.java @@ -398,9 +398,6 @@ public void onPlayerJoin(PlayerJoinEvent e) state.getSkillLookupIdentifier(), wrapper, state.shouldHandlePose(), false, state.getDisguisedItems()); - state.setCachedNbtString(nbt); - state.setCachedProfileNbtString(profile); - if (customName != null) { state.entityCustomName = customName; diff --git a/src/main/java/xiamomc/morph/misc/DisguiseState.java b/src/main/java/xiamomc/morph/misc/DisguiseState.java index 600482d9..fcbf814a 100644 --- a/src/main/java/xiamomc/morph/misc/DisguiseState.java +++ b/src/main/java/xiamomc/morph/misc/DisguiseState.java @@ -34,10 +34,7 @@ import xiamomc.morph.skills.SkillType; import xiamomc.morph.skills.impl.NoneMorphSkill; import xiamomc.morph.storage.playerdata.PlayerMeta; -import xiamomc.morph.utilities.EntityTypeUtils; -import xiamomc.morph.utilities.ItemUtils; -import xiamomc.morph.utilities.MathUtils; -import xiamomc.morph.utilities.SoundUtils; +import xiamomc.morph.utilities.*; import xiamomc.pluginbase.Annotations.Resolved; import java.util.Arrays; @@ -424,41 +421,27 @@ public boolean shouldHandlePose() //region NBT - private String cachedNbtString = "{}"; - public String getCachedNbtString() { - return cachedNbtString; - } - - public void setCachedNbtString(String newNbt) - { - if (newNbt == null || newNbt.isEmpty() || newNbt.isBlank()) newNbt = "{}"; - - this.cachedNbtString = newNbt; + return NbtUtils.getCompoundString(disguiseWrapper.getCompound()); } //endregion //region ProfileNBT - private String cachedProfileNbtString = "{}"; - public String getProfileNbtString() { - return cachedProfileNbtString; - } - - public void setCachedProfileNbtString(String newNbt) - { - if (newNbt == null || newNbt.isEmpty() || newNbt.isBlank()) newNbt = "{}"; + if (!haveProfile()) + return "{}"; - this.cachedProfileNbtString = newNbt; + var s = NbtUtils.getCompoundString(NbtUtils.toCompoundTag(disguiseWrapper.getSkin())); + return s; } public boolean haveProfile() { - return !cachedProfileNbtString.equals("{}"); + return disguiseWrapper.getSkin() != null; } //endregion ProfileNBT @@ -495,9 +478,6 @@ public void setDisguise(@NotNull String identifier, @NotNull String skillIdentif { if (disguiseWrapper == wrapper) return; - setCachedProfileNbtString(null); - setCachedNbtString(null); - this.entityCustomName = null; this.disguiseWrapper = wrapper; @@ -788,9 +768,6 @@ public DisguiseState createCopy() var state = new DisguiseState(player, this.disguiseIdentifier, this.skillLookupIdentifier, disguise, shouldHandlePose, provider, getDisguisedItems(), this.playerOptions, morphConfiguration); - state.setCachedProfileNbtString(this.cachedProfileNbtString); - state.setCachedNbtString(this.cachedNbtString); - return state; } diff --git a/src/main/java/xiamomc/morph/misc/DisguiseStateGenerator.java b/src/main/java/xiamomc/morph/misc/DisguiseStateGenerator.java index c5ece678..83f53289 100644 --- a/src/main/java/xiamomc/morph/misc/DisguiseStateGenerator.java +++ b/src/main/java/xiamomc/morph/misc/DisguiseStateGenerator.java @@ -7,7 +7,6 @@ import xiamomc.morph.MorphPlugin; import xiamomc.morph.backends.DisguiseBackend; import xiamomc.morph.network.PlayerOptions; -import xiamomc.morph.providers.PlayerDisguiseProvider; import xiamomc.morph.skills.MorphSkillHandler; import xiamomc.morph.skills.SkillType; import xiamomc.morph.storage.offlinestore.OfflineDisguiseState; @@ -80,18 +79,20 @@ public static DisguiseState fromOfflineState(OfflineDisguiseState offlineState, wrapper, true, provider, null, playerOptions, playerMeta); - //设置NBT和皮肤 - state.setCachedProfileNbtString(offlineState.profileString); - state.setCachedNbtString(offlineState.snbt); - try { var profileCompound = NbtUtils.toCompoundTag(offlineState.profileString); if (profileCompound != null) { - var profile = net.minecraft.nbt.NbtUtils.readGameProfile(profileCompound); - wrapper.applySkin(profile); + var rawProfile = net.minecraft.nbt.NbtUtils.readGameProfile(profileCompound); + + if (rawProfile != null) + { + var profile = new MorphGameProfile(rawProfile); + profile.setName(wrapper.getDisguiseName()); + wrapper.applySkin(profile); + } } } catch (Throwable t) diff --git a/src/main/java/xiamomc/morph/misc/skins/PlayerSkinProvider.java b/src/main/java/xiamomc/morph/misc/skins/PlayerSkinProvider.java index a33d0535..8a2478a4 100644 --- a/src/main/java/xiamomc/morph/misc/skins/PlayerSkinProvider.java +++ b/src/main/java/xiamomc/morph/misc/skins/PlayerSkinProvider.java @@ -1,20 +1,31 @@ package xiamomc.morph.misc.skins; +import com.destroystokyo.paper.profile.PaperAuthenticationService; import com.mojang.authlib.GameProfile; +import com.mojang.authlib.GameProfileRepository; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.minecraft.MinecraftSessionService; +import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.Util; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.Services; import net.minecraft.server.players.GameProfileCache; +import net.minecraft.world.entity.player.Player; import org.bukkit.Bukkit; import org.jetbrains.annotations.Nullable; import xiamomc.morph.MorphPlugin; import xiamomc.morph.MorphPluginObject; import xiamomc.morph.misc.NmsRecord; import xiamomc.pluginbase.Annotations.Initializer; -import xiamomc.pluginbase.Annotations.Resolved; +import java.io.File; +import java.net.Proxy; +import java.net.URI; import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; public class PlayerSkinProvider extends MorphPluginObject { @@ -27,14 +38,58 @@ public static PlayerSkinProvider getInstance() return instance; } - private GameProfileCache userCache; + private CompletableFuture> getProfileAsyncV2(String name) + { + var executor = Util.PROFILE_EXECUTOR; + + var task = CompletableFuture.supplyAsync(() -> + { + return this.fetchProfileV2(name); + }, executor).whenCompleteAsync((optional, throwable) -> + { + }, executor); + + return task; + } + + private final SkinStore skinStore = new SkinStore(); + + private Optional fetchProfileV2(String name) + { + if (!Player.isValidUsername(name)) + return Optional.empty(); + + var cached = skinStore.get(name); + if (cached != null) + return Optional.of(cached); - @Initializer - private void load() + var profileRef = new AtomicReference(null); + + var lookupCallback = new ProfileLookupCallback() + { + public void onProfileLookupSucceeded(GameProfile gameprofile) + { + profileRef.set(gameprofile); + } + + public void onProfileLookupFailed(String profileName, Exception exception) + { + logger.info("Failed to lookup '%s': '%s'".formatted(profileName, exception.getMessage())); + exception.printStackTrace(); + } + }; + + GameProfileRepository gameProfileRepository = MinecraftServer.getServer().getProfileRepository(); + gameProfileRepository.findProfilesByNames(new String[]{name}, lookupCallback); + + var profile = profileRef.get(); + return profile == null ? Optional.empty() : Optional.of(profile); + } + + @Nullable + public GameProfile getCachedProfile(String name) { - var server = MinecraftServer.getServer(); - //userCache = new GameProfileCache(server.getProfileRepository(), new File("/dev/null")); - //userCache.setExecutor(server); + return skinStore.get(name); } public CompletableFuture> fetchSkinFromProfile(GameProfile profile) @@ -42,7 +97,7 @@ public CompletableFuture> fetchSkinFromProfile(GameProfile if (profile.getProperties().containsKey("textures")) { var optional = Optional.of(profile); - profileCache.put(profile.getName(), ProfileMeta.of(profile)); + skinStore.cache(profile); return CompletableFuture.completedFuture(optional); } @@ -52,6 +107,10 @@ public CompletableFuture> fetchSkinFromProfile(GameProfile { var sessionService = MinecraftServer.getServer().getSessionService(); var result = sessionService.fetchProfile(profile.getId(), true); + + if (result != null) + skinStore.cache(result.profile()); + return result == null ? Optional.of(profile) : Optional.of(result.profile()); }); } @@ -76,46 +135,17 @@ public static ProfileMeta of(GameProfile profile) } } - private final Map profileCache = new Object2ObjectOpenHashMap<>(); - - @Nullable - public GameProfile getCachedProfile(String profileName) - { - var meta = profileCache.getOrDefault(profileName, null); - if (meta != null) - { - meta.lastAccess = plugin.getCurrentTick(); - return meta.profile; - } - - return null; - } - - private void removeUnusedMeta() - { - var currentTick = plugin.getCurrentTick(); - var mapCopy = new Object2ObjectOpenHashMap<>(profileCache); - mapCopy.forEach((name, meta) -> - { - if (currentTick - meta.lastAccess > 20 * 60 * 30) - profileCache.remove(name); - }); - } - private int performCount; public CompletableFuture> fetchSkin(String profileName) { performCount++; - if (performCount >= 10) - removeUnusedMeta(); - var player = Bukkit.getPlayerExact(profileName); if (player != null) { var profile = NmsRecord.ofPlayer(player).gameProfile; - profileCache.put(profileName, ProfileMeta.of(profile)); + skinStore.remove(profileName); return CompletableFuture.completedFuture(Optional.of(profile)); } @@ -123,26 +153,17 @@ public CompletableFuture> fetchSkin(String profileName) if (cachedSkin != null) return CompletableFuture.completedFuture(Optional.of(cachedSkin)); - var server = MinecraftServer.getServer(); - var userCache = this.userCache == null - ? server.getProfileCache() - : this.userCache; //new GameProfileCache(server.getProfileRepository(), new File("/dev/null")); - - if (userCache == null) return CompletableFuture.completedFuture(Optional.empty()); - else - { - return userCache.getAsync(profileName) - .thenCompose(rawProfileOptional -> + return getProfileAsyncV2(profileName) + .thenCompose(rawProfileOptional -> + { + if (rawProfileOptional.isPresent()) { - if (rawProfileOptional.isPresent()) - { - return fetchSkinFromProfile(rawProfileOptional.get()); - } - else - { - return CompletableFuture.completedFuture(Optional.empty()); - } - }); - } + return fetchSkinFromProfile(rawProfileOptional.get()); + } + else + { + return CompletableFuture.completedFuture(Optional.empty()); + } + }); } } diff --git a/src/main/java/xiamomc/morph/misc/skins/SingleSkin.java b/src/main/java/xiamomc/morph/misc/skins/SingleSkin.java index 9e7cfecb..36b566de 100644 --- a/src/main/java/xiamomc/morph/misc/skins/SingleSkin.java +++ b/src/main/java/xiamomc/morph/misc/skins/SingleSkin.java @@ -1,32 +1,55 @@ package xiamomc.morph.misc.skins; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; import com.mojang.authlib.GameProfile; import net.minecraft.Util; +import net.minecraft.commands.arguments.GameProfileArgument; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtIo; +import org.jetbrains.annotations.Nullable; import xiamomc.morph.misc.MorphGameProfile; +import xiamomc.morph.utilities.DisguiseUtils; +import xiamomc.morph.utilities.NbtUtils; +import xiamomc.morph.utilities.NmsUtils; import java.util.UUID; public class SingleSkin { + @Expose + @SerializedName("name") public String name = "unknown"; - public UUID uuid = Util.NIL_UUID; + @Expose + @SerializedName("profile") + public String snbt = "{}"; - public String texture = ""; + @Expose + @SerializedName("expires_at") + public long expiresAt; - public String signature = ""; - - public int expiresAt = 0; - -/* public static SingleSkin fromProfile(GameProfile profile) { - var wrapped = new MorphGameProfile(profile); var instance = new SingleSkin(); instance.name = profile.getName(); - instance.uuid = profile.getId(); + instance.snbt = NbtUtils.getCompoundString(NbtUtils.toCompoundTag(profile)); + instance.expiresAt = System.currentTimeMillis() + 15 * 24 * 60 * 60 * 1000; + // D H M S MS + + return instance; } - */ + @Nullable + public GameProfile toGameProfile() + { + if (this.snbt == null || this.snbt.equalsIgnoreCase("{}")) + return null; + + var compound = NbtUtils.toCompoundTag(this.snbt); + return compound == null + ? null + : net.minecraft.nbt.NbtUtils.readGameProfile(compound); + } } diff --git a/src/main/java/xiamomc/morph/misc/skins/SkinStore.java b/src/main/java/xiamomc/morph/misc/skins/SkinStore.java index a08d77f7..2ed8f6a6 100644 --- a/src/main/java/xiamomc/morph/misc/skins/SkinStore.java +++ b/src/main/java/xiamomc/morph/misc/skins/SkinStore.java @@ -1,8 +1,15 @@ package xiamomc.morph.misc.skins; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import xiamomc.morph.storage.MorphJsonBasedStorage; +import java.text.DateFormat; +import java.util.Date; +import java.util.concurrent.TimeUnit; + public class SkinStore extends MorphJsonBasedStorage { @Override @@ -22,19 +29,34 @@ public class SkinStore extends MorphJsonBasedStorage { return "Server renderer skin store"; } -/* - @Nullable - public GameProfile getProfile(String skinName) + + public synchronized void cache(GameProfile profile) { - var matchedSkin = storingObject.storedSkins.stream() - .filter(skin -> skin.name.equalsIgnoreCase(skinName)) - .findFirst().orElse(null); + storingObject.storedSkins.add(SingleSkin.fromProfile(profile)); - if (matchedSkin == null) return null; + saveConfiguration(); + } - var profile = new GameProfile(matchedSkin.uuid, matchedSkin.name); - profile.getProperties().put("textures", new Property()) + public void remove(String name) + { + storingObject.storedSkins.removeIf(ss -> ss.name.equalsIgnoreCase(name)); } - */ + @Nullable + public GameProfile get(String name) + { + var single = storingObject.storedSkins.stream().filter(ss -> + ss.name.equalsIgnoreCase(name)).findFirst().orElse(null); + + if (single == null) return null; + + if (System.currentTimeMillis() > single.expiresAt) + { + this.addSchedule(() -> storingObject.storedSkins.remove(single)); + + return null; + } + + return single.toGameProfile(); + } } diff --git a/src/main/java/xiamomc/morph/misc/skins/SkinStoreRoot.java b/src/main/java/xiamomc/morph/misc/skins/SkinStoreRoot.java index f65a02b4..bf97eb67 100644 --- a/src/main/java/xiamomc/morph/misc/skins/SkinStoreRoot.java +++ b/src/main/java/xiamomc/morph/misc/skins/SkinStoreRoot.java @@ -1,10 +1,14 @@ package xiamomc.morph.misc.skins; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import java.util.List; public class SkinStoreRoot { + @Expose + @SerializedName("profiles") public List storedSkins = new ObjectArrayList<>(); } diff --git a/src/main/java/xiamomc/morph/providers/PlayerDisguiseProvider.java b/src/main/java/xiamomc/morph/providers/PlayerDisguiseProvider.java index f279f56d..d2e88fb2 100644 --- a/src/main/java/xiamomc/morph/providers/PlayerDisguiseProvider.java +++ b/src/main/java/xiamomc/morph/providers/PlayerDisguiseProvider.java @@ -99,25 +99,6 @@ public boolean isValid(String rawIdentifier) return DisguiseResult.success(wrapper, result.isCopy()); } - @Override - public void postConstructDisguise(DisguiseState state, @Nullable Entity targetEntity) - { - super.postConstructDisguise(state, targetEntity); - - var profile = state.getDisguiseWrapper().getSkin(); - - if (profile != null) - { - var gameProfile = new MorphGameProfile(profile); - var compound = xiamomc.morph.utilities.NbtUtils.toCompoundTag(state.getCachedNbtString(), false); - - gameProfile.setName(DisguiseTypes.PLAYER.toStrippedId(state.getDisguiseIdentifier())); - - NbtUtils.writeGameProfile(compound, gameProfile); - state.setCachedProfileNbtString(compound.toString()); - } - } - private MorphGameProfile getGameProfile(ItemStack item) { if (item.getType() != Material.PLAYER_HEAD) return null; diff --git a/src/main/java/xiamomc/morph/utilities/NbtUtils.java b/src/main/java/xiamomc/morph/utilities/NbtUtils.java index 8563e3b7..c4ae74fd 100644 --- a/src/main/java/xiamomc/morph/utilities/NbtUtils.java +++ b/src/main/java/xiamomc/morph/utilities/NbtUtils.java @@ -1,5 +1,6 @@ package xiamomc.morph.utilities; +import com.mojang.authlib.GameProfile; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.StringTagVisitor; import net.minecraft.nbt.TagParser; @@ -15,6 +16,12 @@ public class NbtUtils { + public static CompoundTag toCompoundTag(GameProfile profile) + { + var compound = new CompoundTag(); + return net.minecraft.nbt.NbtUtils.writeGameProfile(compound, profile); + } + /** * 获取目标实体的 {@link CompoundTag} * @param entity 目标实体