diff --git a/commons/pom.xml b/commons/pom.xml index f7e75d9..952033b 100755 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -16,8 +16,16 @@ org.spigotmc spigot - 1.13.2-R0.1-SNAPSHOT + 1.7.10-R0.1-SNAPSHOT provided + + + io.netty + netty-all + 4.1.33.Final + provided + + diff --git a/commons/src/main/java/com/comphenix/tinyprotocol/LegacyTinyProtocol.java b/commons/src/main/java/com/comphenix/tinyprotocol/LegacyTinyProtocol.java new file mode 100755 index 0000000..105a00d --- /dev/null +++ b/commons/src/main/java/com/comphenix/tinyprotocol/LegacyTinyProtocol.java @@ -0,0 +1,515 @@ +package com.comphenix.tinyprotocol; + +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.io.netty.channel.*; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + +/** + * Represents a very tiny alternative to ProtocolLib. + *

+ * It now supports intercepting packets during login and status ping (such as OUT_SERVER_PING)! + * + * @author Kristian + */ +public abstract class LegacyTinyProtocol { + private static final AtomicInteger ID = new AtomicInteger(0); + + // Used in order to lookup a channel + private static final Reflection.MethodInvoker getPlayerHandle = Reflection.getMethod("{obc}.entity.CraftPlayer", "getHandle"); + private static final Reflection.FieldAccessor getConnection = Reflection.getField("{nms}.EntityPlayer", "playerConnection", Object.class); + private static final Reflection.FieldAccessor getManager = Reflection.getField("{nms}.PlayerConnection", "networkManager", Object.class); + private static final Reflection.FieldAccessor getChannel = Reflection.getField("{nms}.NetworkManager", Channel.class, 0); + + // Looking up ServerConnection + private static final Class minecraftServerClass = Reflection.getUntypedClass("{nms}.MinecraftServer"); + private static final Class serverConnectionClass = Reflection.getUntypedClass("{nms}.ServerConnection"); + private static final Reflection.FieldAccessor getMinecraftServer = Reflection.getField("{obc}.CraftServer", minecraftServerClass, 0); + private static final Reflection.FieldAccessor getServerConnection = Reflection.getField(minecraftServerClass, serverConnectionClass, 0); + private static final Reflection.MethodInvoker getNetworkMarkers = Reflection.getTypedMethod(serverConnectionClass, null, List.class, serverConnectionClass); + + // Packets we have to intercept + private static final Class PACKET_SET_PROTOCOL = Reflection.getMinecraftClass("PacketHandshakingInSetProtocol"); + private static final Class PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart"); + private static final Reflection.FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0); + private static final Reflection.FieldAccessor protocolId = Reflection.getField(PACKET_SET_PROTOCOL, int.class, 0); + private static final Reflection.FieldAccessor protocolType = Reflection.getField(PACKET_SET_PROTOCOL, Enum.class, 0); + + + // Speedup channel lookup + private Map channelLookup = new MapMaker().weakValues().makeMap(); + private Map protocolLookup = new MapMaker().weakKeys().makeMap(); + private Listener listener; + + // Channels that have already been removed + private Set uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + + // List of network markers + private List networkManagers; + + // Injected channel handlers + private List serverChannels = Lists.newArrayList(); + private ChannelInboundHandlerAdapter serverChannelHandler; + private ChannelInitializer beginInitProtocol; + private ChannelInitializer endInitProtocol; + + // Current handler name + private String handlerName; + + protected volatile boolean closed; + protected Plugin plugin; + + /** + * Construct a new instance of TinyProtocol, and start intercepting packets for all connected clients and future clients. + *

+ * You can construct multiple instances per plugin. + * + * @param plugin - the plugin. + */ + public LegacyTinyProtocol(final Plugin plugin) { + this.plugin = plugin; + + // Compute handler name + this.handlerName = getHandlerName(); + + // Prepare existing players + registerBukkitEvents(); + + try { + System.out.println("Attempting to inject into netty"); + registerChannelHandler(); + registerPlayers(plugin); + } catch (IllegalArgumentException ex) { + // Damn you, late bind + plugin.getLogger().info("Attempting to delay injection."); + + new BukkitRunnable() { + @Override + public void run() { + registerChannelHandler(); + registerPlayers(plugin); + plugin.getLogger().info("Injection complete."); + } + }.runTask(plugin); + } + } + + private void createServerChannelHandler() { + // Handle connected channels + endInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + try { + // This can take a while, so we need to stop the main thread from interfering + synchronized (networkManagers) { + // Stop injecting channels + if (!closed) { + channel.eventLoop().submit(() -> injectChannelInternal(channel)); + } + } + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e); + } + } + + }; + + // This is executed before Minecraft's channel handler + beginInitProtocol = new ChannelInitializer() { + + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(endInitProtocol); + } + + }; + + serverChannelHandler = new ChannelInboundHandlerAdapter() { + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel channel = (Channel) msg; + + channel.pipeline().addFirst(beginInitProtocol); + ctx.fireChannelRead(msg); + } + + }; + } + + /** + * Register bukkit events. + */ + private void registerBukkitEvents() { + listener = new Listener() { + + @EventHandler(priority = EventPriority.LOWEST) + public final void onPlayerLogin(PlayerJoinEvent e) { + if (closed) + return; + + Channel channel = getChannel(e.getPlayer()); + + // Don't inject players that have been explicitly uninjected + if (!uninjectedChannels.contains(channel)) { + injectPlayer(e.getPlayer()); + } + } + + @EventHandler + public final void onPluginDisable(PluginDisableEvent e) { + if (e.getPlugin().equals(plugin)) { + close(); + } + } + + }; + + plugin.getServer().getPluginManager().registerEvents(listener, plugin); + } + + @SuppressWarnings("unchecked") + private void registerChannelHandler() { + Object mcServer = getMinecraftServer.get(Bukkit.getServer()); + Object serverConnection = getServerConnection.get(mcServer); + boolean looking = true; + + // We need to synchronize against this list + networkManagers = (List) getNetworkMarkers.invoke(null, serverConnection); + createServerChannelHandler(); + + // Find the correct list, or implicitly throw an exception + for (int i = 0; looking; i++) { + List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); + + for (Object item : list) { + //if (!ChannelFuture.class.isInstance(item)) + // break; + + // Channel future that contains the server connection + Channel serverChannel = ((ChannelFuture) item).channel(); + + serverChannels.add(serverChannel); + ; + serverChannel.pipeline().addFirst(serverChannelHandler); + System.out.println("Server channel handler injected (" + serverChannel + ")"); + looking = false; + } + } + } + + private void unregisterChannelHandler() { + if (serverChannelHandler == null) + return; + + for (Channel serverChannel : serverChannels) { + final ChannelPipeline pipeline = serverChannel.pipeline(); + + // Remove channel handler + serverChannel.eventLoop().execute(() -> { + try { + pipeline.remove(serverChannelHandler); + } catch (NoSuchElementException e) { + // That's fine + } + }); + } + } + + private void registerPlayers(Plugin plugin) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + injectPlayer(player); + } + } + + /** + * Invoked when the server is starting to send a packet to a player. + *

+ * Note that this is not executed on the main thread. + * + * @param receiver - the receiving player, NULL for early login/status packets. + * @param packet - the packet being sent. + * @return The packet to send instead, or NULL to cancel the transmission. + */ + public Object onPacketOutAsync(Player receiver, Object packet) { + return packet; + } + + /** + * Invoked when the server has received a packet from a given player. + *

+ * Use {@link Channel#remoteAddress()} to get the remote address of the client. + * + * @param sender - the player that sent the packet, NULL for early login/status packets. + * @param packet - the packet being received. + * @return The packet to recieve instead, or NULL to cancel. + */ + public Object onPacketInAsync(Player sender, Object packet) { + return packet; + } + + /** + * Send a packet to a particular player. + *

+ * Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet. + * + * @param player - the destination player. + * @param packet - the packet to send. + */ + public void sendPacket(Player player, Object packet) { + sendPacket(getChannel(player), packet); + } + + /** + * Send a packet to a particular client. + *

+ * Note that {@link #onPacketOutAsync(Player, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet to send. + */ + public void sendPacket(Channel channel, Object packet) { + channel.pipeline().writeAndFlush(packet); + } + + /** + * Pretend that a given packet has been received from a player. + *

+ * Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet. + * + * @param player - the player that sent the packet. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Player player, Object packet) { + receivePacket(getChannel(player), packet); + } + + /** + * Pretend that a given packet has been received from a given client. + *

+ * Note that {@link #onPacketInAsync(Player, Object)} will be invoked with this packet. + * + * @param channel - client identified by a channel. + * @param packet - the packet that will be received by the server. + */ + public void receivePacket(Channel channel, Object packet) { + channel.pipeline().context("encoder").fireChannelRead(packet); + } + + /** + * Retrieve the name of the channel injector, default implementation is "tiny-" + plugin name + "-" + a unique ID. + *

+ * Note that this method will only be invoked once. It is no longer necessary to override this to support multiple instances. + * + * @return A unique channel handler name. + */ + protected String getHandlerName() { + return "tiny-" + plugin.getName() + "-" + ID.incrementAndGet(); + } + + /** + * Add a custom channel handler to the given player's channel pipeline, allowing us to intercept sent and received packets. + *

+ * This will automatically be called when a player has logged in. + * + * @param player - the player to inject. + */ + public void injectPlayer(Player player) { + injectChannelInternal(getChannel(player)).player = player; + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The intercepted channel, or NULL if it has already been injected. + */ + public void injectChannel(Channel channel) { + injectChannelInternal(channel); + } + + /** + * Add a custom channel handler to the given channel. + * + * @param channel - the channel to inject. + * @return The packet interceptor. + */ + private PacketInterceptor injectChannelInternal(Channel channel) { + try { + PacketInterceptor interceptor = (PacketInterceptor) channel.pipeline().get(handlerName); + + // Inject our packet interceptor + if (interceptor == null) { + interceptor = new PacketInterceptor(); + channel.pipeline().addBefore("packet_handler", handlerName, interceptor); + uninjectedChannels.remove(channel); + } + + return interceptor; + } catch (IllegalArgumentException e) { + // Try again + return (PacketInterceptor) channel.pipeline().get(handlerName); + } + } + + /** + * Retrieve the Netty channel associated with a player. This is cached. + * + * @param player - the player. + * @return The Netty channel. + */ + public Channel getChannel(Player player) { + Channel channel = channelLookup.get(player.getName()); + + // Lookup channel again + if (channel == null) { + Object connection = getConnection.get(getPlayerHandle.invoke(player)); + Object manager = getManager.get(connection); + + channelLookup.put(player.getName(), channel = getChannel.get(manager)); + } + + return channel; + } + + public int getProtocolVersion(Player player) { + Channel channel = channelLookup.get(player.getName()); + + // Lookup channel again + if (channel == null) { + Object connection = getConnection.get(getPlayerHandle.invoke(player)); + Object manager = getManager.get(connection); + + channelLookup.put(player.getName(), channel = getChannel.get(manager)); + } + + return protocolLookup.get(channel); + } + + /** + * Uninject a specific player. + * + * @param player - the injected player. + */ + public void uninjectPlayer(Player player) { + uninjectChannel(getChannel(player)); + } + + /** + * Uninject a specific channel. + *

+ * This will also disable the automatic channel injection that occurs when a player has properly logged in. + * + * @param channel - the injected channel. + */ + public void uninjectChannel(final Channel channel) { + // No need to guard against this if we're closing + if (!closed) { + uninjectedChannels.add(channel); + } + + // See ChannelInjector in ProtocolLib, line 590 + channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName)); + } + + /** + * Determine if the given player has been injected by TinyProtocol. + * + * @param player - the player. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Player player) { + return hasInjected(getChannel(player)); + } + + /** + * Determine if the given channel has been injected by TinyProtocol. + * + * @param channel - the channel. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean hasInjected(Channel channel) { + return channel.pipeline().get(handlerName) != null; + } + + /** + * Cease listening for packets. This is called automatically when your plugin is disabled. + */ + public final void close() { + if (!closed) { + closed = true; + + // Remove our handlers + for (Player player : plugin.getServer().getOnlinePlayers()) { + uninjectPlayer(player); + } + + // Clean up Bukkit + HandlerList.unregisterAll(listener); + unregisterChannelHandler(); + } + } + + /** + * Channel handler that is inserted into the player's channel pipeline, allowing us to intercept sent and received packets. + * + * @author Kristian + */ + private final class PacketInterceptor extends ChannelDuplexHandler { + // Updated by the login event + public volatile Player player; + + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // Intercept channel + final Channel channel = ctx.channel(); + if (PACKET_LOGIN_IN_START.isInstance(msg)) { + GameProfile profile = getGameProfile.get(msg); + channelLookup.put(profile.getName(), channel); + } else if (PACKET_SET_PROTOCOL.isInstance(msg)) { + String protocol = protocolType.get(msg).name(); + if (protocol.equalsIgnoreCase("LOGIN")) { + protocolLookup.put(channel, protocolId.get(msg)); + } + } + + try { + msg = onPacketInAsync(player, msg); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e); + } + + if (msg != null) { + super.channelRead(ctx, msg); + } + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + try { + msg = onPacketOutAsync(player, msg); + } catch (Exception e) { + plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); + } + + if (msg != null) { + super.write(ctx, msg, promise); + } + } + } +} \ No newline at end of file diff --git a/commons/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java b/commons/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java index 5623c08..2198573 100755 --- a/commons/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/commons/src/main/java/com/comphenix/tinyprotocol/TinyProtocol.java @@ -4,7 +4,6 @@ import com.comphenix.tinyprotocol.Reflection.FieldAccessor; import com.comphenix.tinyprotocol.Reflection.MethodInvoker; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; -import com.mojang.authlib.GameProfile; import io.netty.channel.*; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -46,14 +45,15 @@ public abstract class TinyProtocol { // Packets we have to intercept private static final Class PACKET_LOGIN_IN_START = Reflection.getMinecraftClass("PacketLoginInStart"); - private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, GameProfile.class, 0); + private static final FieldAccessor getGameProfile = Reflection.getField(PACKET_LOGIN_IN_START, + Reflection.getClass("com.mojang.authlib.GameProfile"), 0); // Speedup channel lookup private Map channelLookup = new MapMaker().weakValues().makeMap(); private Listener listener; // Channels that have already been removed - private Set uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); + private Set uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap()); // List of network markers private List networkManagers; @@ -339,6 +339,7 @@ public abstract class TinyProtocol { * Add a custom channel handler to the given channel. * * @param channel - the channel to inject. + * @return The intercepted channel, or NULL if it has already been injected. */ public void injectChannel(Channel channel) { injectChannelInternal(channel); @@ -411,7 +412,14 @@ public abstract class TinyProtocol { } // See ChannelInjector in ProtocolLib, line 590 - channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName)); + channel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + channel.pipeline().remove(handlerName); + } + + }); } /** @@ -493,8 +501,8 @@ public abstract class TinyProtocol { private void handleLoginStart(Channel channel, Object packet) { if (PACKET_LOGIN_IN_START.isInstance(packet)) { - GameProfile profile = getGameProfile.get(packet); - channelLookup.put(profile.getName(), channel); + Object profile = getGameProfile.get(packet); + channelLookup.put((String) Reflection.getMethod(profile.getClass(), "getName").invoke(profile), channel); } } } diff --git a/commons/src/main/java/net/jitse/npclib/NPCLib.java b/commons/src/main/java/net/jitse/npclib/NPCLib.java index 2ebfa9e..43b541a 100755 --- a/commons/src/main/java/net/jitse/npclib/NPCLib.java +++ b/commons/src/main/java/net/jitse/npclib/NPCLib.java @@ -6,9 +6,11 @@ package net.jitse.npclib; import net.jitse.npclib.api.NPC; import net.jitse.npclib.listeners.ChunkListener; +import net.jitse.npclib.listeners.LegacyPacketListener; import net.jitse.npclib.listeners.PacketListener; import net.jitse.npclib.listeners.PlayerListener; import net.jitse.npclib.skin.Skin; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Server; import org.bukkit.plugin.PluginManager; @@ -64,7 +66,12 @@ public class NPCLib { pluginManager.registerEvents(new PlayerListener(), plugin); pluginManager.registerEvents(new ChunkListener(), plugin); - new PacketListener().start(plugin); + // Boot the according packet listener. + if (Bukkit.getBukkitVersion().contains("1.7")) { + new LegacyPacketListener().start(plugin); + } else { + new PacketListener().start(plugin); + } } /** diff --git a/commons/src/main/java/net/jitse/npclib/api/NPC.java b/commons/src/main/java/net/jitse/npclib/api/NPC.java index af7d786..5d3d59d 100755 --- a/commons/src/main/java/net/jitse/npclib/api/NPC.java +++ b/commons/src/main/java/net/jitse/npclib/api/NPC.java @@ -4,9 +4,8 @@ package net.jitse.npclib.api; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.properties.Property; import net.jitse.npclib.NPCManager; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.jitse.npclib.events.NPCDestroyEvent; import net.jitse.npclib.events.NPCSpawnEvent; import net.jitse.npclib.events.trigger.TriggerType; @@ -39,7 +38,7 @@ public abstract class NPC implements PacketHandler, ActionHandler { protected final List lines; protected JavaPlugin plugin; - protected GameProfile gameProfile; + protected GameProfileWrapper gameProfile; protected Location location; public NPC(JavaPlugin plugin, Skin skin, double autoHideDistance, List lines) { @@ -51,11 +50,11 @@ public abstract class NPC implements PacketHandler, ActionHandler { NPCManager.add(this); } - protected GameProfile generateGameProfile(UUID uuid, String name) { - GameProfile gameProfile = new GameProfile(uuid, name); + protected GameProfileWrapper generateGameProfile(UUID uuid, String name) { + GameProfileWrapper gameProfile = new GameProfileWrapper(uuid, name); if (skin != null) { - gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + gameProfile.addSkin(skin); } return gameProfile; diff --git a/commons/src/main/java/net/jitse/npclib/api/wrapper/GameProfileWrapper.java b/commons/src/main/java/net/jitse/npclib/api/wrapper/GameProfileWrapper.java new file mode 100644 index 0000000..5443ac1 --- /dev/null +++ b/commons/src/main/java/net/jitse/npclib/api/wrapper/GameProfileWrapper.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.api.wrapper; + +import com.comphenix.tinyprotocol.Reflection; +import com.google.common.collect.ForwardingMultimap; +import net.jitse.npclib.skin.Skin; +import org.bukkit.Bukkit; + +import java.util.UUID; + +public class GameProfileWrapper { + + // Written because of issue#10 (https://github.com/JitseB/NPCLib/issues/10). + // This class acts as an NMS reflection wrapper for the GameProfileWrapper class. + + // TODO: Add this class to the v1_7_R4 module of NPCLib. + + private final boolean is1_7 = Bukkit.getBukkitVersion().contains("1.7"); + private final Class gameProfileClazz = Reflection.getClass((is1_7 ? "net.minecraft.util." : "") + "com.mojang.authlib.GameProfile"); + + Object gameProfile; + + public GameProfileWrapper(UUID uuid, String name) { + // Only need to check if the version is 1.7, as NPCLib doesn't support any version below this version. + this.gameProfile = Reflection.getConstructor(gameProfileClazz, UUID.class, String.class).invoke(uuid, name); + } + + public void addSkin(Skin skin) { + // Create a new property with the skin data. + Class propertyClazz = Reflection.getClass((is1_7 ? "net.minecraft.util." : "") + "com.mojang.authlib.properties.Property"); + Object property = Reflection.getConstructor(propertyClazz, + String.class, String.class, String.class).invoke("textures", skin.getValue(), skin.getSignature()); + + // Get the property map from the GameProfileWrapper object. + Class propertyMapClazz = Reflection.getClass((is1_7 ? "net.minecraft.util." : "") + "com.mojang.authlib.properties.PropertyMap"); + Reflection.FieldAccessor propertyMapGetter = Reflection.getField(gameProfileClazz, "properties", + propertyMapClazz); + Object propertyMap = propertyMapGetter.get(gameProfile); + + // Add our new property to the property map. + Reflection.getMethod(ForwardingMultimap.class, "put", Object.class, Object.class) + .invoke(propertyMap, "textures", property); + + // Finally set the property map back in the GameProfileWrapper object. + propertyMapGetter.set(gameProfile, propertyMap); + } + + public Object getGameProfile() { + return gameProfile; + } +} diff --git a/commons/src/main/java/net/jitse/npclib/listeners/LegacyPacketListener.java b/commons/src/main/java/net/jitse/npclib/listeners/LegacyPacketListener.java new file mode 100755 index 0000000..fcbdc81 --- /dev/null +++ b/commons/src/main/java/net/jitse/npclib/listeners/LegacyPacketListener.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.listeners; + +import com.comphenix.tinyprotocol.LegacyTinyProtocol; +import com.comphenix.tinyprotocol.Reflection; +import net.jitse.npclib.NPCManager; +import net.jitse.npclib.api.NPC; +import net.jitse.npclib.events.NPCInteractEvent; +import net.jitse.npclib.events.click.ClickType; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +/** + * @author Jitse Boonstra + */ +public class LegacyPacketListener { + + // Classes: + private final Class packetPlayInUseEntityClazz = Reflection.getMinecraftClass("PacketPlayInUseEntity"); + + // Fields: + private final Reflection.FieldAccessor entityIdField = Reflection.getField(packetPlayInUseEntityClazz, "a", int.class); + private final Reflection.FieldAccessor actionField = Reflection.getField(packetPlayInUseEntityClazz, "action", Object.class); + + // Prevent players from clicking at very high speeds. + private final Set delay = new HashSet<>(); + + public void start(JavaPlugin plugin) { + new LegacyTinyProtocol(plugin) { + + @Override + public Object onPacketInAsync(Player player, Object packet) { + + if (packetPlayInUseEntityClazz.isInstance(packet)) { + NPC npc = NPCManager.getAllNPCs().stream().filter( + check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet)) + .findFirst().orElse(null); + + if (npc == null) { + // Default player, not doing magic with the packet. + return super.onPacketInAsync(player, packet); + } + + if (delay.contains(player.getUniqueId())) { + return null; + } + + ClickType clickType = actionField.get(packet).toString() + .equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK; + + Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc)); + + UUID uuid = player.getUniqueId(); + delay.add(uuid); + Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid)); + return null; + } + + return super.onPacketInAsync(player, packet); + } + }; + } +} diff --git a/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java b/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java index e3e3857..cb07d28 100755 --- a/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java +++ b/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java @@ -5,6 +5,14 @@ package net.jitse.npclib.listeners; import com.comphenix.tinyprotocol.Reflection; +import com.comphenix.tinyprotocol.TinyProtocol; +import io.netty.channel.Channel; +import net.jitse.npclib.NPCManager; +import net.jitse.npclib.api.NPC; +import net.jitse.npclib.events.NPCInteractEvent; +import net.jitse.npclib.events.click.ClickType; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import java.util.HashSet; @@ -27,38 +35,38 @@ public class PacketListener { private final Set delay = new HashSet<>(); public void start(JavaPlugin plugin) { -// new TinyProtocol(plugin) { -// -// @Override -// public Object onPacketInAsync(Player player, Channel channel, Object packet) { -// -// if (packetPlayInUseEntityClazz.isInstance(packet)) { -// NPC npc = NPCManager.getAllNPCs().stream().filter( -// check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet)) -// .findFirst().orElse(null); -// -// if (npc == null) { -// // Default player, not doing magic with the packet. -// return super.onPacketInAsync(player, channel, packet); -// } -// -// if (delay.contains(player.getUniqueId())) { -// return null; -// } -// -// ClickType clickType = actionField.get(packet).toString() -// .equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK; -// -// Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc)); -// -// UUID uuid = player.getUniqueId(); -// delay.add(uuid); -// Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid)); -// return null; -// } -// -// return super.onPacketInAsync(player, channel, packet); -// } -// }; + new TinyProtocol(plugin) { + + @Override + public Object onPacketInAsync(Player player, Channel channel, Object packet) { + + if (packetPlayInUseEntityClazz.isInstance(packet)) { + NPC npc = NPCManager.getAllNPCs().stream().filter( + check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet)) + .findFirst().orElse(null); + + if (npc == null) { + // Default player, not doing magic with the packet. + return super.onPacketInAsync(player, channel, packet); + } + + if (delay.contains(player.getUniqueId())) { + return null; + } + + ClickType clickType = actionField.get(packet).toString() + .equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK; + + Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc)); + + UUID uuid = player.getUniqueId(); + delay.add(uuid); + Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid)); + return null; + } + + return super.onPacketInAsync(player, channel, packet); + } + }; } } diff --git a/nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/packets/PacketPlayOutPlayerInfoWrapper.java index d07e54a..d5a77df 100755 --- a/nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_10_R1/src/main/java/net/jitse/npclib/nms/v1_10_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_10_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_10_R1.EnumGamemode; import net.minecraft.server.v1_10_R1.IChatBaseComponent; import net.minecraft.server.v1_10_R1.PacketPlayOutPlayerInfo; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/packets/PacketPlayOutPlayerInfoWrapper.java index 52413e5..cb2a3de 100755 --- a/nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_11_R1/src/main/java/net/jitse/npclib/nms/v1_11_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_11_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_11_R1.EnumGamemode; import net.minecraft.server.v1_11_R1.IChatBaseComponent; import net.minecraft.server.v1_11_R1.PacketPlayOutPlayerInfo; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/packets/PacketPlayOutPlayerInfoWrapper.java index f13a0a4..e24f311 100755 --- a/nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_12_R1/src/main/java/net/jitse/npclib/nms/v1_12_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_12_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_12_R1.EnumGamemode; import net.minecraft.server.v1_12_R1.IChatBaseComponent; import net.minecraft.server.v1_12_R1.PacketPlayOutPlayerInfo; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/packets/PacketPlayOutPlayerInfoWrapper.java index 578efb0..24b0d6a 100755 --- a/nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_13_R1/src/main/java/net/jitse/npclib/nms/v1_13_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_13_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_13_R1.EnumGamemode; import net.minecraft.server.v1_13_R1.IChatBaseComponent; import net.minecraft.server.v1_13_R1.PacketPlayOutPlayerInfo; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/packets/PacketPlayOutPlayerInfoWrapper.java index 79e1fbb..bd8103d 100755 --- a/nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_13_R2/src/main/java/net/jitse/npclib/nms/v1_13_R2/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_13_R2.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_13_R2.EnumGamemode; import net.minecraft.server.v1_13_R2.IChatBaseComponent; import net.minecraft.server.v1_13_R2.PacketPlayOutPlayerInfo; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/packets/PacketPlayOutPlayerInfoWrapper.java index 283b46e..bab9bfb 100755 --- a/nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_8_R1/src/main/java/net/jitse/npclib/nms/v1_8_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_8_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_8_R1.*; import java.util.List; @@ -15,7 +16,9 @@ import java.util.List; */ public class PacketPlayOutPlayerInfoWrapper { - public PacketPlayOutPlayerInfo create(EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/packets/PacketPlayOutPlayerInfoWrapper.java index bca9688..9afaaca 100755 --- a/nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_8_R2/src/main/java/net/jitse/npclib/nms/v1_8_R2/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_8_R2.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_8_R2.IChatBaseComponent; import net.minecraft.server.v1_8_R2.PacketPlayOutPlayerInfo; import net.minecraft.server.v1_8_R2.WorldSettings; @@ -17,7 +18,9 @@ import java.util.List; */ public class PacketPlayOutPlayerInfoWrapper { - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/packets/PacketPlayOutPlayerInfoWrapper.java index 3c411df..24a7cc0 100755 --- a/nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_8_R3/src/main/java/net/jitse/npclib/nms/v1_8_R3/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_8_R3.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_8_R3.IChatBaseComponent; import net.minecraft.server.v1_8_R3.PacketPlayOutPlayerInfo; import net.minecraft.server.v1_8_R3.WorldSettings; @@ -17,7 +18,9 @@ import java.util.List; */ public class PacketPlayOutPlayerInfoWrapper { - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/packets/PacketPlayOutPlayerInfoWrapper.java index 43695c7..0f9a478 100755 --- a/nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_9_R1/src/main/java/net/jitse/npclib/nms/v1_9_R1/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_9_R1.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_9_R1.IChatBaseComponent; import net.minecraft.server.v1_9_R1.PacketPlayOutPlayerInfo; import net.minecraft.server.v1_9_R1.WorldSettings; @@ -18,7 +19,9 @@ import java.util.List; */ public class PacketPlayOutPlayerInfoWrapper { - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action); diff --git a/nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/packets/PacketPlayOutPlayerInfoWrapper.java index 67afd64..6ba09e6 100755 --- a/nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/packets/PacketPlayOutPlayerInfoWrapper.java +++ b/nms/v1_9_R2/src/main/java/net/jitse/npclib/nms/v1_9_R2/packets/PacketPlayOutPlayerInfoWrapper.java @@ -6,6 +6,7 @@ package net.jitse.npclib.nms.v1_9_R2.packets; import com.comphenix.tinyprotocol.Reflection; import com.mojang.authlib.GameProfile; +import net.jitse.npclib.api.wrapper.GameProfileWrapper; import net.minecraft.server.v1_9_R2.IChatBaseComponent; import net.minecraft.server.v1_9_R2.PacketPlayOutPlayerInfo; import net.minecraft.server.v1_9_R2.WorldSettings; @@ -23,7 +24,9 @@ public class PacketPlayOutPlayerInfoWrapper { private final Reflection.ConstructorInvoker playerInfoDataConstructor = Reflection.getConstructor(playerInfoDataClazz, packetPlayOutPlayerInfoClazz, GameProfile.class, int.class, WorldSettings.EnumGamemode.class, IChatBaseComponent.class); - public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfile gameProfile, String name) { + public PacketPlayOutPlayerInfo create(PacketPlayOutPlayerInfo.EnumPlayerInfoAction action, GameProfileWrapper gameProfileWrapper, String name) { + GameProfile gameProfile = (GameProfile) gameProfileWrapper.getGameProfile(); + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); Reflection.getField(packetPlayOutPlayerInfo.getClass(), "a", PacketPlayOutPlayerInfo.EnumPlayerInfoAction.class) .set(packetPlayOutPlayerInfo, action);