From 7f3ecddc5198c97652ed5ad98eff81fb724dca3a Mon Sep 17 00:00:00 2001 From: ineanto Date: Tue, 25 Apr 2023 18:34:49 +0200 Subject: [PATCH] feat: protocollib, java 8 --- .gitignore | 3 +- dependency-reduced-pom.xml | 16 +- pom.xml | 20 ++- .../net/artelnatif/nicko/NickoBukkit.java | 6 + .../artelnatif/nicko/anvil/AnvilManager.java | 2 +- .../nicko/command/sub/NickoCheckSubCmd.java | 2 +- .../nicko/command/sub/NickoDebugSubCmd.java | 2 +- .../AppearanceManager.java | 75 +++------- .../nicko/event/PlayerJoinListener.java | 2 +- .../nicko/gui/items/main/ResetAppearance.java | 2 +- .../artelnatif/nicko/mojang/MojangAPI.java | 2 +- .../nicko/wrapper/AbstractPacket.java | 88 +++++++++++ .../wrapper/WrapperPlayServerRespawn.java | 37 +++++ src/main/resources/plugin.yml | 1 - v1_19_R3/pom.xml | 68 +++++++++ .../net/artelnatif/nicko/impl/v1_19_R3.java | 140 ++++++++++++++++++ 16 files changed, 387 insertions(+), 79 deletions(-) rename src/main/java/net/artelnatif/nicko/{appearance => disguise}/AppearanceManager.java (57%) create mode 100644 src/main/java/net/artelnatif/nicko/wrapper/AbstractPacket.java create mode 100644 src/main/java/net/artelnatif/nicko/wrapper/WrapperPlayServerRespawn.java create mode 100644 v1_19_R3/pom.xml create mode 100644 v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java diff --git a/.gitignore b/.gitignore index d7a802e..67f8558 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,4 @@ v1_19_R1/target target # Maven Dependency Reduced Pom -dist/dependency-reduced-pom.xml -core/dependency-reduced-pom.xml \ No newline at end of file +dependency-reduced-pom.xml \ No newline at end of file diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index f502d6a..5f3a8ad 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -87,13 +87,17 @@ placeholderapi https://repo.extendedclip.com/content/repositories/placeholderapi/ + + dmulloy2-repo + https://repo.dmulloy2.net/repository/public/ + - com.github.retrooper.packetevents - spigot - 2.0.0-SNAPSHOT - provided + com.comphenix.protocol + ProtocolLib + 5.0.0-SNAPSHOT + compile me.clip @@ -157,8 +161,8 @@ - 17 - 17 + 8 + 8 UTF-8 diff --git a/pom.xml b/pom.xml index d975ad7..be4c70f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,8 +10,8 @@ Nicko - 17 - 17 + 8 + 8 UTF-8 @@ -32,15 +32,19 @@ placeholderapi https://repo.extendedclip.com/content/repositories/placeholderapi/ + + dmulloy2-repo + https://repo.dmulloy2.net/repository/public/ + - - com.github.retrooper.packetevents - spigot - 2.0.0-SNAPSHOT - provided - + + com.comphenix.protocol + ProtocolLib + 5.0.0-SNAPSHOT + + me.clip diff --git a/src/main/java/net/artelnatif/nicko/NickoBukkit.java b/src/main/java/net/artelnatif/nicko/NickoBukkit.java index c8a798b..8310406 100644 --- a/src/main/java/net/artelnatif/nicko/NickoBukkit.java +++ b/src/main/java/net/artelnatif/nicko/NickoBukkit.java @@ -1,5 +1,7 @@ package net.artelnatif.nicko; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; import net.artelnatif.nicko.command.NickoCommand; import net.artelnatif.nicko.config.Configuration; import net.artelnatif.nicko.config.ConfigurationManager; @@ -37,6 +39,7 @@ public class NickoBukkit extends JavaPlugin { private Configuration configuration; private LocaleFileManager localeFileManager; private PlayerNameStore nameStore; + private ProtocolManager protocolManager; public NickoBukkit() { this.unitTesting = false; } @@ -76,6 +79,7 @@ public class NickoBukkit extends JavaPlugin { } if (!unitTesting) { + protocolManager = ProtocolLibrary.getProtocolManager(); localeFileManager = new LocaleFileManager(); if (configuration.isCustomLocale()) { if (localeFileManager.dumpFromLocale(Locale.ENGLISH)) { @@ -150,4 +154,6 @@ public class NickoBukkit extends JavaPlugin { public LocaleFileManager getLocaleFileManager() { return localeFileManager; } + + public ProtocolManager getProtocolManager() { return protocolManager; } } diff --git a/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java b/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java index b82d18d..6e53506 100644 --- a/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java +++ b/src/main/java/net/artelnatif/nicko/anvil/AnvilManager.java @@ -1,7 +1,7 @@ package net.artelnatif.nicko.anvil; import net.artelnatif.nicko.NickoBukkit; -import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.AppearanceManager; import net.artelnatif.nicko.disguise.ActionResult; import net.artelnatif.nicko.i18n.I18N; import net.artelnatif.nicko.i18n.I18NDict; diff --git a/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java b/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java index 848a87b..56a4614 100644 --- a/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java +++ b/src/main/java/net/artelnatif/nicko/command/sub/NickoCheckSubCmd.java @@ -1,7 +1,7 @@ package net.artelnatif.nicko.command.sub; import net.artelnatif.nicko.NickoBukkit; -import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.AppearanceManager; import net.artelnatif.nicko.i18n.I18N; import net.artelnatif.nicko.i18n.I18NDict; import net.artelnatif.nicko.mojang.MojangUtils; diff --git a/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java b/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java index b691de9..e7490ba 100644 --- a/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java +++ b/src/main/java/net/artelnatif/nicko/command/sub/NickoDebugSubCmd.java @@ -1,7 +1,7 @@ package net.artelnatif.nicko.command.sub; import net.artelnatif.nicko.NickoBukkit; -import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.AppearanceManager; import net.artelnatif.nicko.mojang.MojangUtils; import org.bukkit.Bukkit; import org.bukkit.Sound; diff --git a/src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java b/src/main/java/net/artelnatif/nicko/disguise/AppearanceManager.java similarity index 57% rename from src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java rename to src/main/java/net/artelnatif/nicko/disguise/AppearanceManager.java index e182d64..2cde36d 100644 --- a/src/main/java/net/artelnatif/nicko/appearance/AppearanceManager.java +++ b/src/main/java/net/artelnatif/nicko/disguise/AppearanceManager.java @@ -1,29 +1,20 @@ -package net.artelnatif.nicko.appearance; +package net.artelnatif.nicko.disguise; -import com.github.retrooper.packetevents.PacketEvents; -import com.github.retrooper.packetevents.protocol.player.TextureProperty; -import com.github.retrooper.packetevents.protocol.player.UserProfile; -import com.github.retrooper.packetevents.protocol.world.Difficulty; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoRemove; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate; -import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn; -import io.github.retrooper.packetevents.util.SpigotConversionUtil; +import com.comphenix.protocol.wrappers.WrappedGameProfile; +import com.comphenix.protocol.wrappers.WrappedSignedProperty; import net.artelnatif.nicko.NickoBukkit; -import net.artelnatif.nicko.disguise.ActionResult; -import net.artelnatif.nicko.disguise.NickoProfile; import net.artelnatif.nicko.i18n.I18NDict; import net.artelnatif.nicko.mojang.MojangAPI; import net.artelnatif.nicko.mojang.MojangSkin; import net.artelnatif.nicko.storage.PlayerDataStore; import net.artelnatif.nicko.storage.name.PlayerNameStore; -import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; import java.io.IOException; -import java.util.EnumSet; -import java.util.List; +import java.util.Collection; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutionException; @@ -105,16 +96,17 @@ public class AppearanceManager { public ActionResult updatePlayer(boolean skinChange) { final String displayName = profile.getName() == null ? player.getName() : profile.getName(); Bukkit.broadcastMessage("Building UserProfile"); - final UserProfile userProfile = new UserProfile(player.getUniqueId(), displayName); + final WrappedGameProfile gameProfile = new WrappedGameProfile(player.getUniqueId(), displayName); - final ActionResult result = updateGameProfileSkin(userProfile, skinChange); + final ActionResult result = updateGameProfileSkin(gameProfile, skinChange); if (!result.isError()) { - updateTabList(userProfile, displayName); + updateTabList(gameProfile, displayName); + respawnPlayer(); } return new ActionResult<>(); } - private ActionResult updateGameProfileSkin(UserProfile userProfile, boolean skinChange) { + private ActionResult updateGameProfileSkin(WrappedGameProfile gameProfile, boolean skinChange) { final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); if (skinChange || changeOnlyName) { @@ -126,14 +118,12 @@ public class AppearanceManager { skin = mojang.getSkin(uuid.get()); if (skin.isPresent()) { final MojangSkin skinResult = skin.get(); - final List properties = userProfile.getTextureProperties(); + final Collection properties = gameProfile.getProperties().values(); properties.clear(); - properties.add(new TextureProperty("textures", skinResult.getValue(), skinResult.getSignature())); + properties.add(new WrappedSignedProperty("textures", skinResult.getValue(), skinResult.getSignature())); Bukkit.broadcastMessage("Modified properties"); } } - Bukkit.broadcastMessage("Respawning player"); - respawnPlayer(); return new ActionResult<>(); } catch (ExecutionException e) { return new ActionResult<>(I18NDict.Error.SKIN_FAIL_CACHE); @@ -145,43 +135,16 @@ public class AppearanceManager { } private void respawnPlayer() { + Bukkit.broadcastMessage("Respawning player"); final World world = player.getWorld(); - final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn( - SpigotConversionUtil.fromBukkitWorld(world), - world.getName(), - Difficulty.getById(world.getDifficulty().ordinal()), - world.getSeed(), - SpigotConversionUtil.fromBukkitGameMode(player.getGameMode()), - null, - false, - false, - false, - null, - null - ); - PacketEvents.getAPI().getPlayerManager().sendPacket(player, respawn); + // TODO (Ineanto, 4/23/23): Respawn Packet + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); } - private void updateTabList(UserProfile userProfile, String displayName) { - final WrapperPlayServerPlayerInfoUpdate infoAdd = new WrapperPlayServerPlayerInfoUpdate(EnumSet.of( - WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER, - WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_GAME_MODE, - WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_DISPLAY_NAME, - WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LISTED, - WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_LATENCY - ), new WrapperPlayServerPlayerInfoUpdate.PlayerInfo( - userProfile, - true, - 0, - SpigotConversionUtil.fromBukkitGameMode(player.getGameMode()), - Component.text(displayName), - null - )); - - final WrapperPlayServerPlayerInfoRemove infoRemove = new WrapperPlayServerPlayerInfoRemove(player.getUniqueId()); + private void updateTabList(WrappedGameProfile gameProfile, String displayName) { + // TODO (Ineanto, 4/23/23): Update player info packet + // TODO (Ineanto, 4/23/23): Remove player info packet Bukkit.broadcastMessage("Updating tablist"); - PacketEvents.getAPI().getPlayerManager().sendPacket(player, infoRemove); - PacketEvents.getAPI().getPlayerManager().sendPacket(player, infoAdd); - + // TODO (Ineanto, 4/23/23): Send packets } } diff --git a/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java b/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java index fc1a3c4..d78ff18 100644 --- a/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java +++ b/src/main/java/net/artelnatif/nicko/event/PlayerJoinListener.java @@ -1,7 +1,7 @@ package net.artelnatif.nicko.event; import net.artelnatif.nicko.NickoBukkit; -import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.AppearanceManager; import net.artelnatif.nicko.disguise.ActionResult; import net.artelnatif.nicko.disguise.NickoProfile; import net.artelnatif.nicko.i18n.I18N; diff --git a/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java b/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java index 48fa0d6..3c1e8c1 100644 --- a/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java +++ b/src/main/java/net/artelnatif/nicko/gui/items/main/ResetAppearance.java @@ -1,6 +1,6 @@ package net.artelnatif.nicko.gui.items.main; -import net.artelnatif.nicko.appearance.AppearanceManager; +import net.artelnatif.nicko.disguise.AppearanceManager; import net.artelnatif.nicko.i18n.I18N; import net.artelnatif.nicko.i18n.I18NDict; import org.bukkit.Material; diff --git a/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java b/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java index 7c1f852..d8b3bf2 100644 --- a/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java +++ b/src/main/java/net/artelnatif/nicko/mojang/MojangAPI.java @@ -25,7 +25,7 @@ public class MojangAPI { private final Logger logger = Logger.getLogger("MojangAPI"); - private final CacheLoader> loader = new CacheLoader<>() { + private final CacheLoader> loader = new CacheLoader>() { @Nonnull public Optional load(@Nonnull String uuid) throws Exception { return getSkinFromMojang(uuid); diff --git a/src/main/java/net/artelnatif/nicko/wrapper/AbstractPacket.java b/src/main/java/net/artelnatif/nicko/wrapper/AbstractPacket.java new file mode 100644 index 0000000..f636bf3 --- /dev/null +++ b/src/main/java/net/artelnatif/nicko/wrapper/AbstractPacket.java @@ -0,0 +1,88 @@ +/** + * PacketWrapper - ProtocolLib wrappers for Minecraft packets + * Copyright (C) dmulloy2 + * Copyright (C) Kristian S. Strangeland + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.artelnatif.nicko.wrapper; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; +import com.google.common.base.Objects; +import org.bukkit.entity.Player; + +public abstract class AbstractPacket { + protected PacketContainer handle; + + /** + * Constructs a new strongly typed wrapper for the given packet. + * + * @param handle - handle to the raw packet data. + * @param type - the packet type. + */ + protected AbstractPacket(PacketContainer handle, PacketType type) { + // Make sure we're given a valid packet + if (handle == null) + throw new IllegalArgumentException("Packet handle cannot be NULL."); + if (!Objects.equal(handle.getType(), type)) + throw new IllegalArgumentException(handle.getHandle() + + " is not a packet of type " + type); + + this.handle = handle; + } + + /** + * Retrieve a handle to the raw packet data. + * + * @return Raw packet data. + */ + public PacketContainer getHandle() { + return handle; + } + + /** + * Send the current packet to the given receiver. + * + * @param receiver - the receiver. + * @throws RuntimeException If the packet cannot be sent. + */ + public void sendPacket(Player receiver) { + ProtocolLibrary.getProtocolManager().sendServerPacket(receiver, + getHandle()); + } + + /** + * Send the current packet to all online players. + */ + public void broadcastPacket() { + ProtocolLibrary.getProtocolManager().broadcastServerPacket(getHandle()); + } + + /** + * Simulate receiving the current packet from the given sender. + * + * @param sender - the sender. + * @throws RuntimeException if the packet cannot be received. + */ + public void receivePacket(Player sender) { + try { + ProtocolLibrary.getProtocolManager().receiveClientPacket(sender, + getHandle()); + } catch (Exception e) { + throw new RuntimeException("Cannot receive packet.", e); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/artelnatif/nicko/wrapper/WrapperPlayServerRespawn.java b/src/main/java/net/artelnatif/nicko/wrapper/WrapperPlayServerRespawn.java new file mode 100644 index 0000000..c0cbf97 --- /dev/null +++ b/src/main/java/net/artelnatif/nicko/wrapper/WrapperPlayServerRespawn.java @@ -0,0 +1,37 @@ +package net.artelnatif.nicko.wrapper; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import org.bukkit.World; + +import java.util.Optional; + +/** + * 1.19.4 compliant version of the WrapperPlayServerRespawn. + * + * @author ineanto, based on work from dmulloy2 and Kristian S. Strangeland + */ +public class WrapperPlayServerRespawn extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.RESPAWN; + + public WrapperPlayServerRespawn() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public Optional getDimension() { + return handle.getDimensionTypes().optionRead(0); + } + + public void setDimension(World value) { + handle.getDimensionTypes().write(0, value); + } + + public Optional getSeed() { + return handle.getLongs().optionRead(0); + } + + public void setSeed(long value) { + handle.getLongs().write(0, value); + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 9599f7a..26ba4b9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -5,7 +5,6 @@ author: Ineanto api-version: 1.13 softdepend: [ PlaceholderAPI ] load: POSTWORLD -depend: [ packetevents ] commands: nicko: description: "Opens Nicko's GUI." diff --git a/v1_19_R3/pom.xml b/v1_19_R3/pom.xml new file mode 100644 index 0000000..eefd19d --- /dev/null +++ b/v1_19_R3/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + + net.artelnatif + nicko-parent + 1.0-SNAPSHOT + + + v1_19_R3 + 1.0-SNAPSHOT + + + + + net.md-5 + specialsource-maven-plugin + 1.2.4 + + + package + + remap + + remap-obf + + org.spigotmc:minecraft-server:1.19.4-R0.1-SNAPSHOT:txt:maps-mojang + true + org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT:jar:remapped-mojang + true + remapped-obf + + + + package + + remap + + remap-spigot + + ${project.build.directory}/${project.artifactId}-${project.version}-remapped-obf.jar + org.spigotmc:minecraft-server:1.19.4-R0.1-SNAPSHOT:csrg:maps-spigot + org.spigotmc:spigot:1.19.4-R0.1-SNAPSHOT:jar:remapped-obf + + + + + + + + + + org.spigotmc + spigot + 1.19.4-R0.1-SNAPSHOT + remapped-mojang + provided + + + net.artelnatif + core + 1.0-SNAPSHOT + + + \ No newline at end of file diff --git a/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java b/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java new file mode 100644 index 0000000..d0ef0eb --- /dev/null +++ b/v1_19_R3/src/main/java/net/artelnatif/nicko/impl/v1_19_R3.java @@ -0,0 +1,140 @@ +package net.artelnatif.nicko.impl; + +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import com.mojang.authlib.properties.PropertyMap; +import net.artelnatif.nicko.NickoBukkit; +import net.artelnatif.nicko.disguise.ActionResult; +import net.artelnatif.nicko.disguise.NickoProfile; +import net.artelnatif.nicko.mojang.MojangSkin; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.RemoteChatSession; +import net.minecraft.network.protocol.game.*; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.ProfilePublicKey; +import net.minecraft.world.level.Level; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_19_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent; + +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; +import java.util.logging.Logger; + +public class v1_19_R3 implements Internals { + final Logger logger = Bukkit.getLogger(); + + @Override + public void updateSelf(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final ServerLevel level = serverPlayer.getLevel(); + final ResourceKey levelResourceKey = serverPlayer.getLevel().dimension(); + final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.level.dimensionTypeId(), + levelResourceKey, level.getWorld().getSeed(), + serverPlayer.gameMode.getGameModeForPlayer(), serverPlayer.gameMode.getPreviousGameModeForPlayer(), + level.isDebug(), + level.isFlat(), + (byte) 0x00, + Optional.empty()); + + final boolean wasFlying = player.isFlying(); + serverPlayer.connection.send(respawn); + player.setFlying(wasFlying); + player.teleport(player.getLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.updateInventory(); + } + + @Override + public void updateOthers(Player player) { + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + + final ClientboundRemoveEntitiesPacket remove = new ClientboundRemoveEntitiesPacket(serverPlayer.getBukkitEntity().getEntityId()); + final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket(serverPlayer); + + final SynchedEntityData entityData = serverPlayer.getEntityData(); + final EntityDataAccessor skinPartAccessor = new EntityDataAccessor<>(17, EntityDataSerializers.BYTE); + entityData.set(skinPartAccessor, (byte) 0x7f); + final ClientboundSetEntityDataPacket entityMetadata = new ClientboundSetEntityDataPacket(serverPlayer.getBukkitEntity().getEntityId(), entityData.getNonDefaultValues()); + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlineServerPlayer = ((CraftPlayer) online).getHandle(); + if (onlineServerPlayer.getBukkitEntity().getUniqueId() != player.getUniqueId()) { + onlineServerPlayer.connection.send(remove); + onlineServerPlayer.connection.send(add); + } + onlineServerPlayer.connection.send(entityMetadata); + }); + } + + @Override + public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { + final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); + final String profileName = profile.getName() == null ? player.getName() : profile.getName(); + + final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); + final GameProfile gameProfile = new GameProfile(player.getUniqueId(), profileName); + + if (skinChange || changeOnlyName) { + final ActionResult skinFetch = fetchSkinTextures(profile, reset); + if (!skinFetch.isError()) { + final MojangSkin skin = skinFetch.getResult(); + final PropertyMap properties = gameProfile.getProperties(); + properties.removeAll("textures"); + properties.put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + updateSelf(player); + } + } + + final ClientboundPlayerInfoUpdatePacket init = ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(Collections.singletonList(serverPlayer)); + final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(Collections.singletonList(player.getUniqueId())); + + RemoteChatSession chatSession; + if (serverPlayer.getChatSession() == null) { + NickoBukkit.getInstance().getLogger().warning("Chat Session of " + serverPlayer.displayName + " is null!"); + NickoBukkit.getInstance().getLogger().warning("If your server is in offline mode/under BungeeCord you can safely ignore this message."); + chatSession = null; + } else { + final UUID uuid = serverPlayer.getChatSession().sessionId(); + final ProfilePublicKey ppk = serverPlayer.getChatSession().profilePublicKey(); + chatSession = new RemoteChatSession(uuid, ppk); + } + + spoofPlayerInfoPacket(init, Collections.singletonList(new ClientboundPlayerInfoUpdatePacket.Entry( + player.getUniqueId(), + gameProfile, + true, + serverPlayer.latency, + serverPlayer.gameMode.getGameModeForPlayer(), + Component.literal(profileName), + chatSession == null ? null : chatSession.asData() + ))); + + + Bukkit.getOnlinePlayers().forEach(online -> { + final ServerPlayer onlinePlayer = ((CraftPlayer) online).getHandle(); + onlinePlayer.connection.send(remove); + onlinePlayer.connection.send(init); + }); + // TODO: 3/17/23 Update signature to avoid duplicate for loop + updateOthers(player); + return new ActionResult<>(); + } + + private void spoofPlayerInfoPacket(Object object, Object newValue) { + try { + final Field field = object.getClass().getDeclaredField("b"); + field.setAccessible(true); + field.set(object, newValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + NickoBukkit.getInstance().getLogger().warning("Unable to spoof packet, that's bad! (" + e.getMessage() + ")"); + } + } +}