feat: full 1.19.3 support

ok this is SO weird. protocollib appears to have absolutly NO trouble modifing (via reflection) a IMMUTABLE list??
the code is so messy right now but i'll clean it up later, let's say it works for now.
this commit also breaks all the other versions due to the way i get the internals soooooo.....
This commit is contained in:
aro 2023-01-21 12:11:11 +01:00
parent be83496494
commit 4c2135ac32
7 changed files with 219 additions and 5 deletions

View file

@ -78,6 +78,10 @@
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url> <url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository> </repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository> <repository>
<id>codemc-snapshots</id> <id>codemc-snapshots</id>
<url>https://repo.codemc.io/repository/maven-snapshots/</url> <url>https://repo.codemc.io/repository/maven-snapshots/</url>
@ -160,6 +164,12 @@
<version>3.1.0</version> <version>3.1.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.0.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<properties> <properties>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>

View file

@ -36,6 +36,10 @@
<id>spigot-repo</id> <id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url> <url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository> </repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository> <repository>
<id>codemc-snapshots</id> <id>codemc-snapshots</id>
<url>https://repo.codemc.io/repository/maven-snapshots/</url> <url>https://repo.codemc.io/repository/maven-snapshots/</url>
@ -111,6 +115,12 @@
<artifactId>yamlconfig</artifactId> <artifactId>yamlconfig</artifactId>
<version>1.1.1</version> <version>1.1.1</version>
</dependency> </dependency>
<!-- ProtocolLib -->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -1,8 +1,11 @@
package net.artelnatif.nicko; 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.gui.structure.Structure;
import de.studiocode.invui.item.builder.ItemBuilder; import de.studiocode.invui.item.builder.ItemBuilder;
import de.studiocode.invui.item.impl.SimpleItem; import de.studiocode.invui.item.impl.SimpleItem;
import net.artelnatif.nicko.bungee.BungeeCordSupport;
import net.artelnatif.nicko.bungee.NickoBungee; import net.artelnatif.nicko.bungee.NickoBungee;
import net.artelnatif.nicko.command.NickoCommand; import net.artelnatif.nicko.command.NickoCommand;
import net.artelnatif.nicko.config.NickoConfiguration; 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.placeholder.PlaceHolderHook;
import net.artelnatif.nicko.pluginchannel.PluginMessageHandler; import net.artelnatif.nicko.pluginchannel.PluginMessageHandler;
import net.artelnatif.nicko.storage.PlayerDataStore; import net.artelnatif.nicko.storage.PlayerDataStore;
import net.artelnatif.nicko.bungee.BungeeCordSupport;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.command.PluginCommand; import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginDescriptionFile;
@ -36,6 +38,7 @@ public class NickoBukkit extends JavaPlugin {
private MojangAPI mojangAPI; private MojangAPI mojangAPI;
private PlayerDataStore dataStore; private PlayerDataStore dataStore;
private LocaleFileManager localeFileManager; private LocaleFileManager localeFileManager;
private ProtocolManager protocolManager;
public NickoBukkit() { this.unitTesting = false; } public NickoBukkit() { this.unitTesting = false; }
@ -94,6 +97,7 @@ public class NickoBukkit extends JavaPlugin {
saveDefaultConfig(); saveDefaultConfig();
config = new NickoConfiguration(this); config = new NickoConfiguration(this);
dataStore = new PlayerDataStore(this); dataStore = new PlayerDataStore(this);
protocolManager = ProtocolLibrary.getProtocolManager();
getLogger().info("Loading internals..."); getLogger().info("Loading internals...");
if (getInternals() == null) { if (getInternals() == null) {
@ -163,6 +167,10 @@ public class NickoBukkit extends JavaPlugin {
return localeFileManager; return localeFileManager;
} }
public ProtocolManager getProtocolManager() {
return protocolManager;
}
public boolean isUnitTesting() { public boolean isUnitTesting() {
return unitTesting; return unitTesting;
} }

View file

@ -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();
}
}

View file

@ -6,12 +6,17 @@ import java.lang.reflect.InvocationTargetException;
public class InternalsProvider { public class InternalsProvider {
private static Internals internals; private static Internals internals;
private static boolean protocolLib = true;
static { static {
try { try {
final String packageName = Internals.class.getPackage().getName(); final String packageName = Internals.class.getPackage().getName();
final String bukkitVersion = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3]; 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(); internals = (Internals) Class.forName(fullClassName).getConstructors()[0].newInstance();
} catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException | } catch (InvocationTargetException | ClassNotFoundException | InstantiationException | IllegalAccessException |
ClassCastException exception) { ClassCastException exception) {

View file

@ -86,12 +86,12 @@ public class v1_19_R2 implements Internals {
@Override @Override
public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) { public ActionResult updateProfile(Player player, NickoProfile profile, boolean skinChange, boolean reset) {
final boolean changeOnlyName = profile.getSkin() != null && !profile.getSkin().equalsIgnoreCase(player.getName()); 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<MojangSkin> skin; Optional<MojangSkin> skin;
final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle(); final ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
final GameProfile gameProfile = serverPlayer.getGameProfile(); final GameProfile gameProfile = serverPlayer.getGameProfile();
final GameProfile newGameProfile = new GameProfile(player.getUniqueId(), profileName);
final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId())); final ClientboundPlayerInfoRemovePacket remove = new ClientboundPlayerInfoRemovePacket(List.of(player.getUniqueId()));
// TODO: 1/20/23 Sets Gamemode to Survival but keeps the flying? Visual effect only? // 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()) { if (uuid.isPresent()) {
skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get())); skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get()));
if (skin.isPresent()) { if (skin.isPresent()) {
final PropertyMap properties = gameProfile.getProperties(); final PropertyMap properties = newGameProfile.getProperties();
properties.removeAll("textures"); properties.removeAll("textures");
properties.put("textures", new Property("textures", skin.get().value(), skin.get().signature())); properties.put("textures", new Property("textures", skin.get().value(), skin.get().signature()));
updateSelf(player); 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<ClientboundPlayerInfoUpdatePacket.Action> 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<ClientboundPlayerInfoUpdatePacket.Action> iterator = actions.iterator();
while (iterator.hasNext()) {
bb.writeUtf(entry.profile().getName(), 16);
bb.writeGameProfileProperties(newGameProfile.getProperties());
}
});*/
serverPlayer.connection.send(remove); serverPlayer.connection.send(remove);
serverPlayer.connection.send(init); serverPlayer.connection.send(init);
Bukkit.getOnlinePlayers().forEach(online -> { Bukkit.getOnlinePlayers().forEach(online -> {

View file

@ -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<Level> 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<Byte> 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<MojangSkin> skin;
final WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player).withName(profileName);
if (skinChange || changeOnlyName) {
try {
final MojangAPI mojang = NickoBukkit.getInstance().getMojangAPI();
final Optional<String> uuid = mojang.getUUID(profile.getSkin());
if (uuid.isPresent()) {
skin = (reset ? mojang.getSkinWithoutCaching(uuid.get()) : mojang.getSkin(uuid.get()));
if (skin.isPresent()) {
final Multimap<String, WrappedSignedProperty> 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<EnumWrappers.PlayerInfoAction> 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();
}
}