+ * 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() + ")");
+ }
+ }
+}