diff --git a/nicko-core/dependency-reduced-pom.xml b/nicko-core/dependency-reduced-pom.xml
index dbcbfbf..9070ca3 100644
--- a/nicko-core/dependency-reduced-pom.xml
+++ b/nicko-core/dependency-reduced-pom.xml
@@ -78,6 +78,10 @@
spigot-repo
https://hub.spigotmc.org/nexus/content/groups/public/
+
+ dmulloy2-repo
+ https://repo.dmulloy2.net/repository/public/
+
codemc-snapshots
https://repo.codemc.io/repository/maven-snapshots/
@@ -160,6 +164,12 @@
3.1.0
compile
+
+ com.comphenix.protocol
+ ProtocolLib
+ 5.0.0-SNAPSHOT
+ compile
+
17
diff --git a/nicko-core/pom.xml b/nicko-core/pom.xml
index 0917b60..52570d0 100644
--- a/nicko-core/pom.xml
+++ b/nicko-core/pom.xml
@@ -36,6 +36,10 @@
spigot-repo
https://hub.spigotmc.org/nexus/content/groups/public/
+
+ dmulloy2-repo
+ https://repo.dmulloy2.net/repository/public/
+
codemc-snapshots
https://repo.codemc.io/repository/maven-snapshots/
@@ -111,6 +115,12 @@
yamlconfig
1.1.1
+
+
+ com.comphenix.protocol
+ ProtocolLib
+ 5.0.0-SNAPSHOT
+
diff --git a/nicko-core/src/main/java/net/artelnatif/nicko/NickoBukkit.java b/nicko-core/src/main/java/net/artelnatif/nicko/NickoBukkit.java
index b028a4a..6d1b7ae 100644
--- a/nicko-core/src/main/java/net/artelnatif/nicko/NickoBukkit.java
+++ b/nicko-core/src/main/java/net/artelnatif/nicko/NickoBukkit.java
@@ -1,8 +1,11 @@
package net.artelnatif.nicko;
+import com.comphenix.protocol.ProtocolLibrary;
+import com.comphenix.protocol.ProtocolManager;
import de.studiocode.invui.gui.structure.Structure;
import de.studiocode.invui.item.builder.ItemBuilder;
import de.studiocode.invui.item.impl.SimpleItem;
+import net.artelnatif.nicko.bungee.BungeeCordSupport;
import net.artelnatif.nicko.bungee.NickoBungee;
import net.artelnatif.nicko.command.NickoCommand;
import net.artelnatif.nicko.config.NickoConfiguration;
@@ -17,7 +20,6 @@ import net.artelnatif.nicko.mojang.MojangAPI;
import net.artelnatif.nicko.placeholder.PlaceHolderHook;
import net.artelnatif.nicko.pluginchannel.PluginMessageHandler;
import net.artelnatif.nicko.storage.PlayerDataStore;
-import net.artelnatif.nicko.bungee.BungeeCordSupport;
import org.bukkit.Material;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginDescriptionFile;
@@ -36,6 +38,7 @@ public class NickoBukkit extends JavaPlugin {
private MojangAPI mojangAPI;
private PlayerDataStore dataStore;
private LocaleFileManager localeFileManager;
+ private ProtocolManager protocolManager;
public NickoBukkit() { this.unitTesting = false; }
@@ -94,6 +97,7 @@ public class NickoBukkit extends JavaPlugin {
saveDefaultConfig();
config = new NickoConfiguration(this);
dataStore = new PlayerDataStore(this);
+ protocolManager = ProtocolLibrary.getProtocolManager();
getLogger().info("Loading internals...");
if (getInternals() == null) {
@@ -163,6 +167,10 @@ public class NickoBukkit extends JavaPlugin {
return localeFileManager;
}
+ public ProtocolManager getProtocolManager() {
+ return protocolManager;
+ }
+
public boolean isUnitTesting() {
return unitTesting;
}
diff --git a/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProtocolLib.java b/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProtocolLib.java
new file mode 100644
index 0000000..9697648
--- /dev/null
+++ b/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProtocolLib.java
@@ -0,0 +1,10 @@
+package net.artelnatif.nicko.impl;
+
+import com.comphenix.protocol.ProtocolManager;
+import net.artelnatif.nicko.NickoBukkit;
+
+public interface InternalsProtocolLib extends Internals {
+ default ProtocolManager getProtocolLib() {
+ return NickoBukkit.getInstance().getProtocolManager();
+ }
+}
diff --git a/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java b/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java
index f66428d..8b6a1b0 100644
--- a/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java
+++ b/nicko-core/src/main/java/net/artelnatif/nicko/impl/InternalsProvider.java
@@ -6,12 +6,17 @@ import java.lang.reflect.InvocationTargetException;
public class InternalsProvider {
private static Internals internals;
+ private static boolean protocolLib = true;
static {
try {
final String packageName = Internals.class.getPackage().getName();
final String bukkitVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
- final String fullClassName = packageName + "." + bukkitVersion;
+ String fullClassName = packageName + "." + bukkitVersion;
+ if (protocolLib) {
+ System.out.println("USING PROTOCOLLIB HACK");
+ fullClassName += "_P";
+ }
internals = (Internals) Class.forName(fullClassName).getConstructors()[0].newInstance();
} catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException |
ClassCastException exception) {
diff --git a/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java
index 81391b5..c6669de 100644
--- a/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java
+++ b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2.java
@@ -86,12 +86,12 @@ public class v1_19_R2 implements Internals {
@Override
public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) {
final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName());
- // TODO: 1/20/23 Unable to update the GameProfile name.
- //final String profileName = profile.getName() == null ? player.getName() : profile.getName();
+ final String profileName = profile.getName() == null ? player.getName() : profile.getName();
Optional skin;
final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
final GameProfile gameProfile = serverPlayer.getGameProfile();
+ final GameProfile newGameProfile = new GameProfile(player.getUniqueId(), profileName);
final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId()));
// TODO: 1/20/23 Sets Gamemode to Survival but keeps the flying? Visual effect only?
@@ -104,7 +104,7 @@ public class v1_19_R2 implements Internals {
if (uuid.isPresent()) {
skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get()));
if (skin.isPresent()) {
- final PropertyMap properties = gameProfile.getProperties();
+ final PropertyMap properties = newGameProfile.getProperties();
properties.removeAll("textures");
properties.put("textures", new Property("textures", skin.get().value(), skin.get().signature()));
updateSelf(player);
@@ -121,6 +121,30 @@ public class v1_19_R2 implements Internals {
}
}
+ /*
+ Tried this solution, doesn't work either:
+
+ final FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.buffer());
+ final EnumSet actions = EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER);
+ byteBuf.writeEnumSet(actions, ClientboundPlayerInfoUpdatePacket.Action.class);
+ byteBuf.writeCollection(List.of(new ClientboundPlayerInfoUpdatePacket.Entry(
+ player.getUniqueId(),
+ newGameProfile,
+ true,
+ serverPlayer.latency,
+ serverPlayer.gameMode.getGameModeForPlayer(),
+ Component.literal(profileName),
+ serverPlayer.getChatSession().asData()
+ )), (bb, entry) -> {
+ bb.writeUUID(entry.profileId());
+ Iterator iterator = actions.iterator();
+
+ while (iterator.hasNext()) {
+ bb.writeUtf(entry.profile().getName(), 16);
+ bb.writeGameProfileProperties(newGameProfile.getProperties());
+ }
+ });*/
+
serverPlayer.connection.send(remove);
serverPlayer.connection.send(init);
Bukkit.getOnlinePlayers().forEach(online -> {
diff --git a/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2_P.java b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2_P.java
new file mode 100644
index 0000000..810c8f2
--- /dev/null
+++ b/v1_19_R2/src/main/java/net/artelnatif/nicko/impl/v1_19_R2_P.java
@@ -0,0 +1,147 @@
+package net.artelnatif.nicko.impl;
+
+import com.comphenix.protocol.PacketType;
+import com.comphenix.protocol.events.PacketContainer;
+import com.comphenix.protocol.wrappers.*;
+import com.google.common.collect.Multimap;
+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.minecraft.network.protocol.game.ClientboundAddEntityPacket;
+import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
+import net.minecraft.network.protocol.game.ClientboundRespawnPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
+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.level.Level;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
+import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.bukkit.event.player.PlayerTeleportEvent;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+
+public class v1_19_R2_P implements InternalsProtocolLib {
+ @Override
+ public void updateSelf(Player player) {
+ final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
+ final ServerLevel level = serverPlayer.getLevel();
+ final ResourceKey levelResourceKey = serverPlayer.getLevel().dimension();
+ final CraftWorld world = level.getWorld();
+ // last boolean is: "has death location" attribute, if true, the optional contains the death dimension and position.
+ // with the boolean being false, we don't need to provide a value, and thus we return an empty optional.
+ final ClientboundRespawnPacket respawn = new ClientboundRespawnPacket(serverPlayer.level.dimensionTypeId(),
+ levelResourceKey, world.getSeed(),
+ serverPlayer.gameMode.getPreviousGameModeForPlayer(), serverPlayer.gameMode.getGameModeForPlayer(),
+ 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);
+
+ /*
+ BIT MASKS:
+ 0x01 Cape enabled
+ 0x02 Jacket enabled
+ 0x04 Left sleeve enabled
+ 0x08 Right sleeve enabled
+ 0x10 Left pants leg enabled
+ 0x20 Right pants leg enabled
+ 0x40 Hat enabled
+ */
+ final SynchedEntityData entityData = serverPlayer.getEntityData();
+ final EntityDataAccessor skinPartAccessor = new EntityDataAccessor<>(17, EntityDataSerializers.BYTE);
+ entityData.set(skinPartAccessor, (byte) 0x7f); // 127, all masks combined
+ final ClientboundSetEntityDataPacket entityMetadata = new ClientboundSetEntityDataPacket(serverPlayer.getBukkitEntity().getEntityId(), entityData.getNonDefaultValues());
+
+ Bukkit.getOnlinePlayers().forEach(online -> {
+ 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();
+ Optional skin;
+
+ final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(profileName);
+ if (skinChange || changeOnlyName) {
+ try {
+ final MojangAPI mojang = NickoBukkit.getInstance().getMojangAPI();
+ final Optional uuid = mojang.getUUID(profile.getSkin());
+ if (uuid.isPresent()) {
+ skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get()));
+ if (skin.isPresent()) {
+ final Multimap properties = gameProfile.getProperties();
+ properties.removeAll("textures");
+ properties.put("textures", new WrappedSignedProperty("textures", skin.get().value(), skin.get().signature()));
+ updateSelf(player);
+ } else {
+ return new ActionResult(I18NDict.Error.SKIN_FAIL_MOJANG);
+ }
+ } else {
+ return new ActionResult(I18NDict.Error.NAME_FAIL_MOJANG);
+ }
+ } catch (ExecutionException e) {
+ return new ActionResult(I18NDict.Error.SKIN_FAIL_CACHE);
+ } catch (IOException e) {
+ return new ActionResult(I18NDict.Error.NAME_FAIL_MOJANG);
+ }
+ }
+
+ // Letting ProtocolLib handle the reflection here.
+ final PacketContainer remove = getProtocolLib().createPacket(PacketType.Play.Server.PLAYER_INFO_REMOVE);
+ remove.getUUIDLists().write(0, Collections.singletonList(player.getUniqueId()));
+
+ final EnumSet actions = EnumSet.of(
+ EnumWrappers.PlayerInfoAction.ADD_PLAYER,
+ EnumWrappers.PlayerInfoAction.UPDATE_LATENCY,
+ EnumWrappers.PlayerInfoAction.UPDATE_LISTED);
+ final PacketContainer add = getProtocolLib().createPacket(PacketType.Play.Server.PLAYER_INFO);
+
+ add.getPlayerInfoActions().write(0, actions);
+ add.getPlayerInfoDataLists().write(1, Collections.singletonList(new PlayerInfoData(
+ gameProfile,
+ player.getPing(),
+ EnumWrappers.NativeGameMode.fromBukkit(player.getGameMode()),
+ WrappedChatComponent.fromText(profileName)
+ )));
+
+ Bukkit.getOnlinePlayers().forEach(online -> {
+ getProtocolLib().sendServerPacket(online, remove);
+ getProtocolLib().sendServerPacket(online, add);
+ });
+ updateOthers(player);
+ return new ActionResult();
+ }
+}
\ No newline at end of file