diff --git a/build.gradle.kts b/build.gradle.kts index ce01c8a..d266835 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,6 @@ plugins { id("java") id("com.gradleup.shadow") version "8.3.2" id("xyz.jpenilla.run-paper") version "2.3.0" - id("io.papermc.paperweight.userdev") version "2.0.0-beta.17" } group = "xyz.ineanto" @@ -31,14 +30,14 @@ repositories { } dependencies { - paperweight.paperDevBundle("1.21.5-R0.1-SNAPSHOT") + compileOnly("io.papermc.paper:paper-api:1.21.6-R0.1-SNAPSHOT") compileOnly("me.clip:placeholderapi:2.11.5") compileOnly("net.kyori:adventure-api:4.21.0") compileOnly("xyz.xenondevs.invui:invui-core:$invuiVersion") compileOnly("net.wesjd:anvilgui:1.10.4-SNAPSHOT") - compileOnly("com.github.retrooper:packetevents-spigot:2.8.0") + implementation("com.github.retrooper:packetevents-spigot:2.8.0") implementation("de.rapha149.signgui:signgui:2.5.0") implementation("com.github.jsixface:yamlconfig:1.2") diff --git a/src/main/java/xyz/ineanto/nicko/Nicko.java b/src/main/java/xyz/ineanto/nicko/Nicko.java index fbdd2ee..b5260aa 100644 --- a/src/main/java/xyz/ineanto/nicko/Nicko.java +++ b/src/main/java/xyz/ineanto/nicko/Nicko.java @@ -2,6 +2,7 @@ package xyz.ineanto.nicko; import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.manager.server.ServerVersion; +import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.plugin.java.JavaPlugin; @@ -39,20 +40,29 @@ public class Nicko extends JavaPlugin { private PlayerNameStore nameStore; private RandomNameFetcher nameFetcher; + @Override + public void onLoad() { + PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); + PacketEvents.getAPI().load(); + } + @Override public void onEnable() { plugin = this; + PacketEvents.getAPI().init(); + PacketEvents.getAPI().getSettings().checkForUpdates(false).kickOnPacketException(true); + + configurationManager = new ConfigurationManager(getDataFolder()); configurationManager.saveDefaultConfig(); dataStore = new PlayerDataStore(mojangAPI, getNickoConfig()); if (PacketEvents.getAPI().getServerManager().getVersion().isOlderThan(ServerVersion.V_1_20)) { - getLogger().severe("This version (" + PacketEvents.getAPI().getServerManager().getVersion() + ") is not supported by Nicko!"); - getLogger().severe("As of version 1.2.0, Nicko only supports the two latest major Minecraft versions. (Currently 1.20 to 1.21.5)"); - dataStore.getStorage().setError(true); - Bukkit.getPluginManager().disablePlugin(this); + getLogger().severe("This version (" + PacketEvents.getAPI().getServerManager().getVersion() + ") is not officially supported by Nicko!"); + getLogger().severe("As of version 1.3.0, Nicko only supports the two latest major Minecraft versions. (Currently 1.20 to 1.21.5)"); + getLogger().severe("Do NOT complain about it not working, you've been warned!"); } if (!Bukkit.getOnlineMode()) { diff --git a/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java b/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java index 7de7d31..394169f 100644 --- a/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java +++ b/src/main/java/xyz/ineanto/nicko/appearance/AppearanceManager.java @@ -5,8 +5,8 @@ import org.bukkit.entity.Player; import xyz.ineanto.nicko.Nicko; import xyz.ineanto.nicko.event.custom.PlayerDisguiseEvent; import xyz.ineanto.nicko.event.custom.PlayerResetDisguiseEvent; +import xyz.ineanto.nicko.packet.PacketEventsPacketSender; import xyz.ineanto.nicko.packet.PacketSender; -import xyz.ineanto.nicko.packet.PaperPacketSender; import xyz.ineanto.nicko.profile.NickoProfile; import xyz.ineanto.nicko.storage.PlayerDataStore; import xyz.ineanto.nicko.storage.name.PlayerNameStore; @@ -23,7 +23,7 @@ public class AppearanceManager { public AppearanceManager(Player player) { this.player = player; - this.packetSender = new PaperPacketSender(player, getNickoProfile()); + this.packetSender = new PacketEventsPacketSender(player, getNickoProfile()); } public ActionResult reset() { diff --git a/src/main/java/xyz/ineanto/nicko/packet/PacketEventsPacketSender.java b/src/main/java/xyz/ineanto/nicko/packet/PacketEventsPacketSender.java index 4373d36..5ce7c7e 100644 --- a/src/main/java/xyz/ineanto/nicko/packet/PacketEventsPacketSender.java +++ b/src/main/java/xyz/ineanto/nicko/packet/PacketEventsPacketSender.java @@ -1,10 +1,17 @@ package xyz.ineanto.nicko.packet; -import com.destroystokyo.paper.profile.CraftPlayerProfile; import com.destroystokyo.paper.profile.PlayerProfile; import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes; +import com.github.retrooper.packetevents.protocol.world.Difficulty; +import com.github.retrooper.packetevents.util.Vector3d; import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerRespawn; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity; +import io.github.retrooper.packetevents.util.SpigotConversionUtil; import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.entity.Player; import xyz.ineanto.nicko.Nicko; import xyz.ineanto.nicko.appearance.ActionResult; @@ -15,6 +22,7 @@ import xyz.ineanto.nicko.profile.NickoProfile; import java.io.IOException; import java.util.Optional; +import java.util.Random; import java.util.concurrent.ExecutionException; public class PacketEventsPacketSender implements PacketSender { @@ -30,27 +38,39 @@ public class PacketEventsPacketSender implements PacketSender { public void sendEntityRespawn() { if (!profile.hasData()) return; - // TODO (Ineanto, 27/06/2025): Create/Delete packets here + final WrapperPlayServerDestroyEntities destroy = new WrapperPlayServerDestroyEntities(player.getEntityId()); + final WrapperPlayServerSpawnEntity spawn = new WrapperPlayServerSpawnEntity( + new Random().nextInt(9999), + Optional.of(player.getUniqueId()), + EntityTypes.PLAYER, + new Vector3d(player.getX(), player.getY(), player.getZ()), + player.getPitch(), + player.getYaw(), + player.getBodyYaw(), + 0, + Optional.empty() + ); Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> { - // TODO (Ineanto, 27/06/2025): Send packets - //sendPacket(destroy, player); - //sendPacket(create, player); + sendPacket(destroy, player); + sendPacket(spawn, player); }); } @Override public ActionResult updatePlayerProfile(String name) { - final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name); + final PlayerProfile previousProfile = player.getPlayerProfile(); + final PlayerProfile newProfile = Bukkit.getServer().createProfile(player.getUniqueId(), name); + // Copy previous properties to preserve skin - playerProfile.setProperties(playerProfile.getProperties()); - player.setPlayerProfile(playerProfile); + newProfile.setProperties(previousProfile.getProperties()); + player.setPlayerProfile(newProfile); return ActionResult.ok(); } @Override public ActionResult updatePlayerProfileProperties() { - // TODO (Ineanto, 27/06/2025): Player profile + final PlayerProfile playerProfile = player.getPlayerProfile(); try { final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI(); @@ -66,8 +86,8 @@ public class PacketEventsPacketSender implements PacketSender { } final MojangSkin skinResult = skin.get(); - //playerProfile.setProperties(skinResult.asProfileProperties()); - //player.setPlayerProfile(playerProfile); + playerProfile.setProperties(skinResult.asProfileProperties()); + player.setPlayerProfile(playerProfile); return ActionResult.ok(); } catch (ExecutionException | IOException e) { return ActionResult.error(LanguageKey.Error.CACHE); @@ -82,8 +102,24 @@ public class PacketEventsPacketSender implements PacketSender { @Override public void sendPlayerRespawn() { - // TODO (Ineanto, 27/06/2025): Respawn packet - //sendPacket(respawn, player); + final World world = player.getWorld(); + + final WrapperPlayServerRespawn respawn = new WrapperPlayServerRespawn( + SpigotConversionUtil.typeFromBukkitWorld(world), + world.getName(), + Difficulty.getById(world.getDifficulty().ordinal()), + world.getSeed(), + SpigotConversionUtil.fromBukkitGameMode(player.getGameMode()), + SpigotConversionUtil.fromBukkitGameMode(player.getPreviousGameMode()), + false, + false, + true, + null, + null, + null + ); + + sendPacket(respawn, player); } @Override diff --git a/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java b/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java index ded8f5e..29fa82c 100644 --- a/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java +++ b/src/main/java/xyz/ineanto/nicko/packet/PaperPacketSender.java @@ -1,167 +1,167 @@ -package xyz.ineanto.nicko.packet; - -import com.destroystokyo.paper.profile.CraftPlayerProfile; -import com.destroystokyo.paper.profile.PlayerProfile; -import it.unimi.dsi.fastutil.ints.IntList; -import net.minecraft.Optionull; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.RemoteChatSession; -import net.minecraft.network.chat.contents.PlainTextContents; -import net.minecraft.network.protocol.Packet; -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.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.phys.Vec3; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.entity.CraftPlayer; -import org.bukkit.entity.Player; -import xyz.ineanto.nicko.Nicko; -import xyz.ineanto.nicko.appearance.ActionResult; -import xyz.ineanto.nicko.language.LanguageKey; -import xyz.ineanto.nicko.mojang.MojangAPI; -import xyz.ineanto.nicko.mojang.MojangSkin; -import xyz.ineanto.nicko.profile.NickoProfile; - -import java.io.IOException; -import java.util.EnumSet; -import java.util.List; -import java.util.Optional; -import java.util.Random; -import java.util.concurrent.ExecutionException; - -/** - * Look at this Mojang. - * I want you to really stare at this code. - * You made me do this. - */ -public class PaperPacketSender implements PacketSender { - private final Player player; - private final NickoProfile profile; - - public PaperPacketSender(Player player, NickoProfile profile) { - this.player = player; - this.profile = profile; - } - - @Override - public void sendEntityRespawn() { - if (!profile.hasData()) return; - - final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId())); - final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket( - new Random().nextInt(9999), - player.getUniqueId(), - player.getX(), - player.getY(), - player.getZ(), - player.getPitch(), - player.getYaw(), - EntityType.PLAYER, - 0, - Vec3.ZERO, - player.getBodyYaw() - ); - - Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> { - sendPacket(destroy, player); - sendPacket(add, player); - }); - } - - @Override - public ActionResult updatePlayerProfile(String name) { - final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name); - // Copy previous properties to preserve skin - playerProfile.setProperties(playerProfile.getProperties()); - player.setPlayerProfile(playerProfile); - return ActionResult.ok(); - } - - @Override - public ActionResult updatePlayerProfileProperties() { - final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), profile.getName() == null ? player.getName() : profile.getName()); - - try { - final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI(); - - final Optional uuid = mojangAPI.getUUID(profile.getSkin()); - if (uuid.isEmpty()) { - return ActionResult.error(LanguageKey.Error.MOJANG); - } - - final Optional skin = mojangAPI.getSkin(uuid.get()); - if (skin.isEmpty()) { - return ActionResult.error(LanguageKey.Error.MOJANG); - } - - final MojangSkin skinResult = skin.get(); - playerProfile.setProperties(skinResult.asProfileProperties()); - player.setPlayerProfile(playerProfile); - return ActionResult.ok(); - } catch (ExecutionException | IOException e) { - return ActionResult.error(LanguageKey.Error.CACHE); - } - } - - @Override - public void sendEntityMetadataUpdate() { - final SynchedEntityData.DataValue dataValueComponent = - new SynchedEntityData.DataItem<>( - new EntityDataAccessor<>(17, EntityDataSerializers.BYTE), - (byte) 0x7f - ).value(); - - final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent)); - sendPacket(data, player); - } - - @Override - public void sendPlayerRespawn() { - final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); - final ServerLevel level = serverPlayer.serverLevel(); - - final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(level), (byte) 0x03); - sendPacket(respawn, player); - } - - @Override - public void sendTabListUpdate(String displayName) { - final ServerPlayer serverPlayer = (((CraftPlayer) player)).getHandle(); - - final EnumSet actions = EnumSet.of( - ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, - ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, - ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY); - - final List entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry( - serverPlayer.getUUID(), - serverPlayer.gameProfile, - true, - serverPlayer.connection.latency(), - serverPlayer.gameMode.getGameModeForPlayer(), - MutableComponent.create(new PlainTextContents.LiteralContents(displayName)), - true, - serverPlayer.getTabListOrder(), - Optionull.map(serverPlayer.getChatSession(), RemoteChatSession::asData) - )); - - final ClientboundPlayerInfoUpdatePacket update = new ClientboundPlayerInfoUpdatePacket(actions, entries); - final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId())); - - Bukkit.getOnlinePlayers().forEach(onlinePlayer -> { - sendPacket(remove, onlinePlayer); - sendPacket(update, onlinePlayer); - }); - } - - private void sendPacket(Packet packet, Player player) { - (((CraftPlayer) player).getHandle()).connection.send(packet); - } -} \ No newline at end of file +//package xyz.ineanto.nicko.packet; +// +//import com.destroystokyo.paper.profile.CraftPlayerProfile; +//import com.destroystokyo.paper.profile.PlayerProfile; +//import it.unimi.dsi.fastutil.ints.IntList; +//import net.minecraft.Optionull; +//import net.minecraft.network.chat.MutableComponent; +//import net.minecraft.network.chat.RemoteChatSession; +//import net.minecraft.network.chat.contents.PlainTextContents; +//import net.minecraft.network.protocol.Packet; +//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.server.level.ServerLevel; +//import net.minecraft.server.level.ServerPlayer; +//import net.minecraft.world.entity.EntityType; +//import net.minecraft.world.phys.Vec3; +//import org.bukkit.Bukkit; +//import org.bukkit.craftbukkit.entity.CraftPlayer; +//import org.bukkit.entity.Player; +//import xyz.ineanto.nicko.Nicko; +//import xyz.ineanto.nicko.appearance.ActionResult; +//import xyz.ineanto.nicko.language.LanguageKey; +//import xyz.ineanto.nicko.mojang.MojangAPI; +//import xyz.ineanto.nicko.mojang.MojangSkin; +//import xyz.ineanto.nicko.profile.NickoProfile; +// +//import java.io.IOException; +//import java.util.EnumSet; +//import java.util.List; +//import java.util.Optional; +//import java.util.Random; +//import java.util.concurrent.ExecutionException; +// +///** +// * Look at this Mojang. +// * I want you to really stare at this code. +// * You made me do this. +// */ +//public class PaperPacketSender implements PacketSender { +// private final Player player; +// private final NickoProfile profile; +// +// public PaperPacketSender(Player player, NickoProfile profile) { +// this.player = player; +// this.profile = profile; +// } +// +// @Override +// public void sendEntityRespawn() { +// if (!profile.hasData()) return; +// +// final ClientboundRemoveEntitiesPacket destroy = new ClientboundRemoveEntitiesPacket(IntList.of(player.getEntityId())); +// final ClientboundAddEntityPacket add = new ClientboundAddEntityPacket( +// new Random().nextInt(9999), +// player.getUniqueId(), +// player.getX(), +// player.getY(), +// player.getZ(), +// player.getPitch(), +// player.getYaw(), +// EntityType.PLAYER, +// 0, +// Vec3.ZERO, +// player.getBodyYaw() +// ); +// +// Bukkit.getOnlinePlayers().stream().filter(receiver -> receiver.getUniqueId() != player.getUniqueId()).forEach(receiver -> { +// sendPacket(destroy, player); +// sendPacket(add, player); +// }); +// } +// +// @Override +// public ActionResult updatePlayerProfile(String name) { +// final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), name); +// // Copy previous properties to preserve skin +// playerProfile.setProperties(playerProfile.getProperties()); +// player.setPlayerProfile(playerProfile); +// return ActionResult.ok(); +// } +// +// @Override +// public ActionResult updatePlayerProfileProperties() { +// final PlayerProfile playerProfile = new CraftPlayerProfile(player.getUniqueId(), profile.getName() == null ? player.getName() : profile.getName()); +// +// try { +// final MojangAPI mojangAPI = Nicko.getInstance().getMojangAPI(); +// +// final Optional uuid = mojangAPI.getUUID(profile.getSkin()); +// if (uuid.isEmpty()) { +// return ActionResult.error(LanguageKey.Error.MOJANG); +// } +// +// final Optional skin = mojangAPI.getSkin(uuid.get()); +// if (skin.isEmpty()) { +// return ActionResult.error(LanguageKey.Error.MOJANG); +// } +// +// final MojangSkin skinResult = skin.get(); +// playerProfile.setProperties(skinResult.asProfileProperties()); +// player.setPlayerProfile(playerProfile); +// return ActionResult.ok(); +// } catch (ExecutionException | IOException e) { +// return ActionResult.error(LanguageKey.Error.CACHE); +// } +// } +// +// @Override +// public void sendEntityMetadataUpdate() { +// final SynchedEntityData.DataValue dataValueComponent = +// new SynchedEntityData.DataItem<>( +// new EntityDataAccessor<>(17, EntityDataSerializers.BYTE), +// (byte) 0x7f +// ).value(); +// +// final ClientboundSetEntityDataPacket data = new ClientboundSetEntityDataPacket(player.getEntityId(), List.of(dataValueComponent)); +// sendPacket(data, player); +// } +// +// @Override +// public void sendPlayerRespawn() { +// final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); +// final ServerLevel level = serverPlayer.serverLevel(); +// +// final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.createCommonSpawnInfo(level), (byte) 0x03); +// sendPacket(respawn, player); +// } +// +// @Override +// public void sendTabListUpdate(String displayName) { +// final ServerPlayer serverPlayer = (((CraftPlayer) player)).getHandle(); +// +// final EnumSet actions = EnumSet.of( +// ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, +// ClientboundPlayerInfoUpdatePacket.Action.INITIALIZE_CHAT, +// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LISTED, +// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_DISPLAY_NAME, +// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE, +// ClientboundPlayerInfoUpdatePacket.Action.UPDATE_LATENCY); +// +// final List entries = List.of(new ClientboundPlayerInfoUpdatePacket.Entry( +// serverPlayer.getUUID(), +// serverPlayer.gameProfile, +// true, +// serverPlayer.connection.latency(), +// serverPlayer.gameMode.getGameModeForPlayer(), +// MutableComponent.create(new PlainTextContents.LiteralContents(displayName)), +// true, +// serverPlayer.getTabListOrder(), +// Optionull.map(serverPlayer.getChatSession(), RemoteChatSession::asData) +// )); +// +// final ClientboundPlayerInfoUpdatePacket update = new ClientboundPlayerInfoUpdatePacket(actions, entries); +// final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId())); +// +// Bukkit.getOnlinePlayers().forEach(onlinePlayer -> { +// sendPacket(remove, onlinePlayer); +// sendPacket(update, onlinePlayer); +// }); +// } +// +// private void sendPacket(Packet packet, Player player) { +// (((CraftPlayer) player).getHandle()).connection.send(packet); +// } +//} \ No newline at end of file