diff --git a/.travis.yml b/.travis.yml index fb99088..f4a42f3 100755 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,10 @@ cache: - $HOME/.m2/ install: - wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar +- wget http://central.maven.org/maven2/io/netty/netty-all/4.1.33.Final/netty-all-4.1.33.Final.jar +- mvn install:install-file -Dfile=netty-all-4.1.33.Final.jar -DgroupId=io.netty -DartifactId=netty-all -Dversion=4.1.33.Final -Dpackaging=jar +- wget https://github.com/Sarabveer/CraftBukkit-Spigot-Binary/blob/master/spigot-1.7.10-R0.1/spigot-1.7.10-R0.1-1649.jar +- mvn install:install-file -Dfile=spigot-1.7.10-R0.1-1649.jar -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.7.10-R0.1-SNAPSHOT -Dpackaging=jar - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8.3-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8.3 >> /dev/null 2>&1 - ls $HOME/.m2/repository/org/spigotmc/spigot/1.8.8-R0.1-SNAPSHOT >> /dev/null 2>&1 || java -jar BuildTools.jar --rev 1.8.8 >> /dev/null 2>&1 diff --git a/README.md b/README.md index 1098f86..979f825 100755 --- a/README.md +++ b/README.md @@ -3,16 +3,14 @@ NPCLib – Basic non-player character library.
[![Release](https://jitpack.io/v/JitseB/NPCLib.svg)](https://github.com/JitseB/NPCLib/releases) [![Build Status](https://travis-ci.com/JitseB/NPCLib.svg?branch=master)](https://travis-ci.com/JitseB/NPCLib) [![JDK](https://img.shields.io/badge/Using-Java%208-blue.svg)](http://jdk.java.net/8/) -[![Versions](https://img.shields.io/badge/MC-1.8%20--%201.13.2-blue.svg)](https://github.com/JitseB/NPCLib/releases) +[![Versions](https://img.shields.io/badge/MC-1.7.10%20--%201.13.2-blue.svg)](https://github.com/JitseB/NPCLib/releases) +[![Thread](https://img.shields.io/badge/SpigotMC-Resource-orange.svg)](https://www.spigotmc.org/resources/npclib.55884/) +[![Thread](https://img.shields.io/badge/SpigotMC-Thread-orange.svg)](https://www.spigotmc.org/threads/npclib-–-basic-non-player-character-library.314460/) = -This is an API made specifically for spigot servers (Minecraft). Current supported versions: **1.8 - 1.13.2**. Lightweight replacement for Citizens. NPCLib only uses packets instead of registering the entity in the actual Minecraft server. - -**SpigotMC Resource** https://www.spigotmc.org/resources/npclib.55884/
-**SpigotMC Thread** https://www.spigotmc.org/threads/npclib-–-basic-non-player-character-library.314460/ - -**Preview** (click to play video) +This is an API made specifically for spigot servers (Minecraft). Current supported versions: **1.7.10 - 1.13.2**. Lightweight replacement for Citizens. NPCLib only uses packets instead of registering the entity in the actual Minecraft server. +### Preview (click to play video) [![YouTube Video](http://img.youtube.com/vi/LqwdqIxPIvE/0.jpg)](http://www.youtube.com/watch?v=LqwdqIxPIvE "NPCLib – Basic non-player character library (Minecraft).") ## Donate @@ -21,6 +19,21 @@ This is an API made specifically for spigot servers (Minecraft). Current support Alternatively, you can help the project by starring the repository or telling others about NPCLib. :smile: +## Roadmap + +- :heavy_check_mark: Spawn and destroy NPC (version 1.8 - latest). +- :heavy_check_mark: Autohide NPC when out of range. +- :heavy_check_mark: Add support for 1.7.10 (1.7 R4). +- :construction: Option to rotate head to player (when nearby). +- :construction: Add support for animated text (update-able holograms). +- :x: Give NPC armor and items in hand. +- :x: Multi-line text support for 1.7.10 (1.7 R4). + +### Roadmad Legend +:heavy_check_mark: Feature is fully implemented and functional.
+:construction: Feature is still in development (or experimental).
+:x: Development of feature has yet to be started.
+ ## Developers ### Usage @@ -40,6 +53,7 @@ Alternatively, you can put `npclib-plugin-v*.jar` under your `plugins` folder. B ```Java // Creating a new NPC instance. + // MC 1.7.10 (1.7 R4) only supports single-line text. NPC npc = lib.createNPC(skin, lines); // Then let the library generate the necessary packets. diff --git a/api/pom.xml b/api/pom.xml index 27be49f..0404f9c 100755 --- a/api/pom.xml +++ b/api/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib - 1.3 + 1.4 npclib-api @@ -19,6 +19,12 @@ ${project.version} compile + + net.jitse + npclib-nms-v1_7_R4 + ${project.version} + compile + net.jitse npclib-nms-v1_8_R1 diff --git a/commons/pom.xml b/commons/pom.xml index f7e75d9..ee0e809 100755 --- a/commons/pom.xml +++ b/commons/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib - 1.3 + 1.4 npclib-commons @@ -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..5453571 --- /dev/null +++ b/commons/src/main/java/com/comphenix/tinyprotocol/LegacyTinyProtocol.java @@ -0,0 +1,325 @@ +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; + +/** + * Minimized version of TinyProtocol by Kristian suited for NPCLib. + */ +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; + + private volatile boolean closed; + protected Plugin plugin; + + protected LegacyTinyProtocol(final Plugin plugin) { + this.plugin = plugin; + + // Compute handler name + this.handlerName = "tiny-" + plugin.getName() + "-" + ID.incrementAndGet(); + + // Prepare existing players + registerBukkitEvents(); + + try { + System.out.println("[NPCLib] Attempting to inject into netty."); + registerChannelHandler(); + registerPlayers(plugin); + } catch (IllegalArgumentException ex) { + // Damn you, late bind + plugin.getLogger().info("[NPCLib] Attempting to delay injection."); + + new BukkitRunnable() { + @Override + public void run() { + registerChannelHandler(); + registerPlayers(plugin); + plugin.getLogger().info("[NPCLib] Injection complete."); + } + }.runTask(plugin); + } + } + + private void createServerChannelHandler() { + // Handle connected channels + endInitProtocol = new ChannelInitializer() { + + @SuppressWarnings("all") + @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, "[NPCLib] Cannot inject incomming channel " + channel, e); + } + } + + }; + + // This is executed before Minecraft's channel handler + beginInitProtocol = new ChannelInitializer() { + + @SuppressWarnings("all") + @Override + protected void initChannel(Channel channel) throws Exception { + channel.pipeline().addLast(endInitProtocol); + } + + }; + + serverChannelHandler = new ChannelInboundHandlerAdapter() { + + @SuppressWarnings("all") + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + Channel channel = (Channel) msg; + + channel.pipeline().addFirst(beginInitProtocol); + ctx.fireChannelRead(msg); + } + + }; + } + + private void registerBukkitEvents() { + listener = new Listener() { + + @SuppressWarnings("unused") + @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()); + } + } + + @SuppressWarnings("unused") + @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("[NPCLib] 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); + } + } + + public Object onPacketInAsync(Player sender, Object packet) { + return packet; + } + + private void injectPlayer(Player player) { + injectChannelInternal(getChannel(player)).player = player; + } + + 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); + } + } + + private 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; + } + + private 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)); + } + + private void close() { + if (!closed) { + closed = true; + + // Remove our handlers + for (Player player : plugin.getServer().getOnlinePlayers()) { + uninjectChannel(getChannel(player)); + } + + // Clean up Bukkit + HandlerList.unregisterAll(listener); + unregisterChannelHandler(); + } + } + + 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, "[NPCLib] Error in onPacketInAsync().", e); + } + + if (msg != null) { + super.channelRead(ctx, msg); + } + } + } +} \ 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..f2a936b 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; @@ -22,11 +21,7 @@ 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 + * Minimized version of TinyProtocol by Kristian suited for NPCLib. */ public abstract class TinyProtocol { private static final AtomicInteger ID = new AtomicInteger(0); @@ -46,7 +41,8 @@ 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(); @@ -67,38 +63,32 @@ public abstract class TinyProtocol { // Current handler name private String handlerName; - protected volatile boolean closed; + private 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 TinyProtocol(final Plugin plugin) { + protected TinyProtocol(final Plugin plugin) { this.plugin = plugin; // Compute handler name - this.handlerName = getHandlerName(); + this.handlerName = "tiny-" + plugin.getName() + "-" + ID.incrementAndGet(); // Prepare existing players registerBukkitEvents(); try { + System.out.println("[NPCLib] Attempting to inject into netty."); registerChannelHandler(); registerPlayers(plugin); } catch (IllegalArgumentException ex) { // Damn you, late bind - plugin.getLogger().info("[TinyProtocol] Delaying server channel injection due to late bind."); + plugin.getLogger().info("[NPCLib] Attempting to delay injection."); new BukkitRunnable() { @Override public void run() { registerChannelHandler(); registerPlayers(plugin); - plugin.getLogger().info("[TinyProtocol] Late bind injection successful."); + plugin.getLogger().info("[NPCLib] Injection complete."); } }.runTask(plugin); } @@ -108,6 +98,7 @@ public abstract class TinyProtocol { // Handle connected channels endInitProtocol = new ChannelInitializer() { + @SuppressWarnings("all") @Override protected void initChannel(Channel channel) throws Exception { try { @@ -119,7 +110,7 @@ public abstract class TinyProtocol { } } } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Cannot inject incomming channel " + channel, e); + plugin.getLogger().log(Level.SEVERE, "[NPCLib] Cannot inject incomming channel " + channel, e); } } @@ -128,6 +119,7 @@ public abstract class TinyProtocol { // This is executed before Minecraft's channel handler beginInitProtocol = new ChannelInitializer() { + @SuppressWarnings("all") @Override protected void initChannel(Channel channel) throws Exception { channel.pipeline().addLast(endInitProtocol); @@ -137,6 +129,7 @@ public abstract class TinyProtocol { serverChannelHandler = new ChannelInboundHandlerAdapter() { + @SuppressWarnings("all") @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Channel channel = (Channel) msg; @@ -149,12 +142,10 @@ public abstract class TinyProtocol { }; } - /** - * Register bukkit events. - */ private void registerBukkitEvents() { listener = new Listener() { + @SuppressWarnings("unused") @EventHandler(priority = EventPriority.LOWEST) public final void onPlayerLogin(PlayerLoginEvent e) { if (closed) @@ -168,6 +159,7 @@ public abstract class TinyProtocol { } } + @SuppressWarnings("unused") @EventHandler public final void onPluginDisable(PluginDisableEvent e) { if (e.getPlugin().equals(plugin)) { @@ -195,7 +187,7 @@ public abstract class TinyProtocol { List list = Reflection.getField(serverConnection.getClass(), List.class, i).get(serverConnection); for (Object item : list) { - if (!ChannelFuture.class.isInstance(item)) + if (!(item instanceof ChannelFuture)) break; // Channel future that contains the server connection @@ -216,17 +208,12 @@ public abstract class TinyProtocol { final ChannelPipeline pipeline = serverChannel.pipeline(); // Remove channel handler - serverChannel.eventLoop().execute(new Runnable() { - - @Override - public void run() { - try { - pipeline.remove(serverChannelHandler); - } catch (NoSuchElementException e) { - // That's fine - } + serverChannel.eventLoop().execute(() -> { + try { + pipeline.remove(serverChannelHandler); + } catch (NoSuchElementException e) { + // That's fine } - }); } } @@ -237,119 +224,14 @@ public abstract class TinyProtocol { } } - /** - * 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 channel - the channel that received the packet. Never NULL. - * @param packet - the packet being sent. - * @return The packet to send instead, or NULL to cancel the transmission. - */ - public Object onPacketOutAsync(Player receiver, Channel channel, Object packet) { + public Object onPacketInAsync(Player sender, 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 channel - channel that received the packet. Never NULL. - * @param packet - the packet being received. - * @return The packet to recieve instead, or NULL to cancel. - */ - public Object onPacketInAsync(Player sender, Channel channel, Object packet) { - return packet; - } - - /** - * Send a packet to a particular player. - *

- * Note that {@link #onPacketOutAsync(Player, Channel, 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, Channel, 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, Channel, 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, Channel, 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) { + private void injectPlayer(Player player) { injectChannelInternal(getChannel(player)).player = player; } - /** - * Add a custom channel handler to the given channel. - * - * @param channel - the channel to inject. - */ - 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); @@ -368,13 +250,7 @@ public abstract class TinyProtocol { } } - /** - * Retrieve the Netty channel associated with a player. This is cached. - * - * @param player - the player. - * @return The Netty channel. - */ - public Channel getChannel(Player player) { + private Channel getChannel(Player player) { Channel channel = channelLookup.get(player.getName()); // Lookup channel again @@ -388,62 +264,20 @@ public abstract class TinyProtocol { return 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() { + private void close() { if (!closed) { closed = true; // Remove our handlers for (Player player : plugin.getServer().getOnlinePlayers()) { - uninjectPlayer(player); + // No need to guard against this if we're closing + Channel channel = getChannel(player); + if (!closed) { + uninjectedChannels.add(channel); + } + + // See ChannelInjector in ProtocolLib, line 590 + channel.eventLoop().execute(() -> channel.pipeline().remove(handlerName)); } // Clean up Bukkit @@ -452,11 +286,6 @@ public abstract class TinyProtocol { } } - /** - * 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; @@ -468,9 +297,9 @@ public abstract class TinyProtocol { handleLoginStart(channel, msg); try { - msg = onPacketInAsync(player, channel, msg); + msg = onPacketInAsync(player, msg); } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Error in onPacketInAsync().", e); + plugin.getLogger().log(Level.SEVERE, "[NPCLib] Error in onPacketInAsync().", e); } if (msg != null) { @@ -478,23 +307,10 @@ public abstract class TinyProtocol { } } - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - try { - msg = onPacketOutAsync(player, ctx.channel(), msg); - } catch (Exception e) { - plugin.getLogger().log(Level.SEVERE, "Error in onPacketOutAsync().", e); - } - - if (msg != null) { - super.write(ctx, msg, promise); - } - } - 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..d304d0c 100755 --- a/commons/src/main/java/net/jitse/npclib/NPCLib.java +++ b/commons/src/main/java/net/jitse/npclib/NPCLib.java @@ -9,6 +9,7 @@ import net.jitse.npclib.listeners.ChunkListener; 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 +65,8 @@ public class NPCLib { pluginManager.registerEvents(new PlayerListener(), plugin); pluginManager.registerEvents(new ChunkListener(), plugin); - new PacketListener().start(plugin); + // Boot the according packet listener. + new PacketListener().start(plugin, Bukkit.getBukkitVersion().contains("1.7")); } /** 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 7856759..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; @@ -25,11 +24,11 @@ import java.util.*; public abstract class NPC implements PacketHandler, ActionHandler { protected final UUID uuid = UUID.randomUUID(); - protected final String name = uuid.toString().replace("-", "").substring(0, 10); // Below was previously = (int) Math.ceil(Math.random() * 100000) + 100000 (new is experimental). protected final int entityId = Integer.MAX_VALUE - NPCManager.getAllNPCs().size(); protected double cosFOV = Math.cos(Math.toRadians(60)); + protected String name = uuid.toString().replace("-", "").substring(0, 10); private final Set shown = new HashSet<>(); private final Set autoHidden = new HashSet<>(); @@ -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/PacketListener.java b/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java index cb07d28..5e700d0 100755 --- a/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java +++ b/commons/src/main/java/net/jitse/npclib/listeners/PacketListener.java @@ -4,9 +4,9 @@ package net.jitse.npclib.listeners; +import com.comphenix.tinyprotocol.LegacyTinyProtocol; 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; @@ -34,39 +34,77 @@ public class PacketListener { // Prevent players from clicking at very high speeds. private final Set delay = new HashSet<>(); - public void start(JavaPlugin plugin) { - new TinyProtocol(plugin) { + public void start(JavaPlugin plugin, boolean is1_7) { + if (is1_7) { + // 1.7 R4 packet interaction. + new LegacyTinyProtocol(plugin) { - @Override - public Object onPacketInAsync(Player player, Channel channel, Object packet) { + @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 (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 (npc == null) { + // Default player, not doing magic with the packet. + return super.onPacketInAsync(player, packet); + } - if (delay.contains(player.getUniqueId())) { + 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; } - 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); } + }; + } else { + // 1.8 (and above) packet interaction. + new TinyProtocol(plugin) { - return super.onPacketInAsync(player, channel, packet); - } - }; + @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/nms/pom.xml b/nms/pom.xml index 93bb047..7aa51d8 100755 --- a/nms/pom.xml +++ b/nms/pom.xml @@ -7,12 +7,13 @@ net.jitse npclib - 1.3 + 1.4 npclib-nms + v1_7_R4 v1_8_R1 v1_8_R2 v1_8_R3 diff --git a/nms/v1_10_R1/pom.xml b/nms/v1_10_R1/pom.xml index d59f6a9..30ace41 100755 --- a/nms/v1_10_R1/pom.xml +++ b/nms/v1_10_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_10_R1 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/pom.xml b/nms/v1_11_R1/pom.xml index e38a305..98a3ee5 100755 --- a/nms/v1_11_R1/pom.xml +++ b/nms/v1_11_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_11_R1 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/pom.xml b/nms/v1_12_R1/pom.xml index 09b3df4..1203094 100755 --- a/nms/v1_12_R1/pom.xml +++ b/nms/v1_12_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_12_R1 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/pom.xml b/nms/v1_13_R1/pom.xml index 45a1e74..271c83d 100755 --- a/nms/v1_13_R1/pom.xml +++ b/nms/v1_13_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_13_R1 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/pom.xml b/nms/v1_13_R2/pom.xml index c3651fc..f7d67a5 100755 --- a/nms/v1_13_R2/pom.xml +++ b/nms/v1_13_R2/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_13_R2 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_7_R4/pom.xml b/nms/v1_7_R4/pom.xml new file mode 100755 index 0000000..9cbf492 --- /dev/null +++ b/nms/v1_7_R4/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + + net.jitse + npclib-nms + 1.4 + + + npclib-nms-v1_7_R4 + + + + org.spigotmc + spigot + 1.7.10-R0.1-SNAPSHOT + provided + + + com.google.code.gson + gson + 2.8.5 + compile + + + diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java new file mode 100755 index 0000000..bb5cd9d --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/NPC_v1_7_R4.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4; + +import net.jitse.npclib.api.NPC; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutEntityHeadRotationWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutNamedEntitySpawnWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutPlayerInfoWrapper; +import net.jitse.npclib.nms.v1_7_R4.packets.PacketPlayOutScoreboardTeamWrapper; +import net.jitse.npclib.skin.Skin; +import net.minecraft.server.v1_7_R4.*; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import net.minecraft.util.com.mojang.authlib.properties.Property; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.List; +import java.util.UUID; + +/** + * @author Jitse Boonstra + */ +public class NPC_v1_7_R4 extends NPC { + + private PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn; + private PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeamRegister, packetPlayOutScoreboardTeamUnregister; + private PacketPlayOutPlayerInfo packetPlayOutPlayerInfoAdd, packetPlayOutPlayerInfoRemove; + private PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation; + private PacketPlayOutEntityDestroy packetPlayOutEntityDestroy; + + private GameProfile legacyGameProfile; + + public NPC_v1_7_R4(JavaPlugin plugin, Skin skin, double autoHideDistance, List lines) { + super(plugin, skin, autoHideDistance, lines); + // TODO: Add multi-line text support. + this.name = lines.get(0); + } + + @Override + public void createPackets() { + this.legacyGameProfile = generateLegacyGameProfile(uuid, name.length() < 16 ? name : name.substring(0, 15)); + PacketPlayOutPlayerInfoWrapper packetPlayOutPlayerInfoWrapper = new PacketPlayOutPlayerInfoWrapper(); + + // Packets for spawning the NPC: + this.packetPlayOutScoreboardTeamRegister = new PacketPlayOutScoreboardTeamWrapper() + .createRegisterTeam(legacyGameProfile.getId().toString().replace("-", "").substring(0, 10), name); // First packet to send. + + this.packetPlayOutPlayerInfoAdd = packetPlayOutPlayerInfoWrapper + .create(0, legacyGameProfile, name.length() < 16 ? name : name.length() < 16 ? name : name.substring(0, 15)); // Second packet to send. + + this.packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawnWrapper() + .create(legacyGameProfile, location, entityId); // Third packet to send. + + this.packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotationWrapper() + .create(location, entityId); // Fourth packet to send. + + this.packetPlayOutPlayerInfoRemove = packetPlayOutPlayerInfoWrapper + .create(4, legacyGameProfile, name.length() < 16 ? name : name.substring(0, 15)); // Fifth packet to send (delayed). + + // Packet for destroying the NPC: + this.packetPlayOutEntityDestroy = new PacketPlayOutEntityDestroy(entityId); // First packet to send. + + // Second packet to send is "packetPlayOutPlayerInfoRemove". + + this.packetPlayOutScoreboardTeamUnregister = new PacketPlayOutScoreboardTeamWrapper() + .createUnregisterTeam(legacyGameProfile.getId().toString().replace("-", "").substring(0, 10)); // Third packet to send. + } + + @Override + public void sendShowPackets(Player player) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + playerConnection.sendPacket(packetPlayOutScoreboardTeamRegister); + playerConnection.sendPacket(packetPlayOutPlayerInfoAdd); + playerConnection.sendPacket(packetPlayOutNamedEntitySpawn); + playerConnection.sendPacket(packetPlayOutEntityHeadRotation); + + Bukkit.getScheduler().runTaskLater(plugin, () -> + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove), 50); + } + + @Override + public void sendHidePackets(Player player, boolean scheduler) { + PlayerConnection playerConnection = ((CraftPlayer) player).getHandle().playerConnection; + + playerConnection.sendPacket(packetPlayOutEntityDestroy); + playerConnection.sendPacket(packetPlayOutPlayerInfoRemove); + + if (scheduler) { + // Sending this a bit later so the player doesn't see the name (for that split second). + Bukkit.getScheduler().runTaskLater(plugin, () -> + playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister), 5); + } else { + playerConnection.sendPacket(packetPlayOutScoreboardTeamUnregister); + } + } + + private GameProfile generateLegacyGameProfile(UUID uuid, String name) { + GameProfile gameProfile = new GameProfile(uuid, name); + + if (skin != null) { + gameProfile.getProperties().put("textures", new Property("textures", skin.getValue(), skin.getSignature())); + } + + return gameProfile; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java new file mode 100755 index 0000000..a8af781 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutEntityHeadRotationWrapper.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.PacketPlayOutEntityHeadRotation; +import org.bukkit.Location; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutEntityHeadRotationWrapper { + + public PacketPlayOutEntityHeadRotation create(Location location, int entityId) { + PacketPlayOutEntityHeadRotation packetPlayOutEntityHeadRotation = new PacketPlayOutEntityHeadRotation(); + + Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "a", int.class). + set(packetPlayOutEntityHeadRotation, entityId); + Reflection.getField(packetPlayOutEntityHeadRotation.getClass(), "b", byte.class) + .set(packetPlayOutEntityHeadRotation, (byte) ((int) location.getYaw() * 256.0F / 360.0F)); + + return packetPlayOutEntityHeadRotation; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java new file mode 100755 index 0000000..61b47a5 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutNamedEntitySpawnWrapper.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.DataWatcher; +import net.minecraft.server.v1_7_R4.PacketPlayOutNamedEntitySpawn; +import net.minecraft.util.com.mojang.authlib.GameProfile; +import org.bukkit.Location; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutNamedEntitySpawnWrapper { + + public PacketPlayOutNamedEntitySpawn create(GameProfile gameProfile, Location location, int entityId) { + PacketPlayOutNamedEntitySpawn packetPlayOutNamedEntitySpawn = new PacketPlayOutNamedEntitySpawn(); + + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "a", int.class) + .set(packetPlayOutNamedEntitySpawn, entityId); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "b", GameProfile.class) + .set(packetPlayOutNamedEntitySpawn, gameProfile); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "c", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getX() * 32.0D)); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "d", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getY() * 32.0D)); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "e", int.class) + .set(packetPlayOutNamedEntitySpawn, (int) Math.floor(location.getZ() * 32.0D)); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "f", byte.class) + .set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getYaw() * 256.0F / 360.0F))); + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "g", byte.class) + .set(packetPlayOutNamedEntitySpawn, (byte) ((int) (location.getPitch() * 256.0F / 360.0F))); + + DataWatcher dataWatcher = new DataWatcher(null); + dataWatcher.a(10, (byte) 127); + + Reflection.getField(packetPlayOutNamedEntitySpawn.getClass(), "i", DataWatcher.class) + .set(packetPlayOutNamedEntitySpawn, dataWatcher); + + return packetPlayOutNamedEntitySpawn; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java new file mode 100755 index 0000000..3673d16 --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutPlayerInfoWrapper.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.PacketPlayOutPlayerInfo; +import net.minecraft.util.com.mojang.authlib.GameProfile; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutPlayerInfoWrapper { + + public PacketPlayOutPlayerInfo create(int action, GameProfile gameProfile, String name) { + PacketPlayOutPlayerInfo packetPlayOutPlayerInfo = new PacketPlayOutPlayerInfo(); + + // Action values: + // 0 = Add player + // 1 = Update gamemode + // 2 = Update latency + // 3 = Update display name + // 4 = Remove player + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "action", int.class) + .set(packetPlayOutPlayerInfo, action); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "player", GameProfile.class) + .set(packetPlayOutPlayerInfo, gameProfile); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "username", String.class) + .set(packetPlayOutPlayerInfo, name); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "ping", int.class) + .set(packetPlayOutPlayerInfo, 0); + Reflection.getField(packetPlayOutPlayerInfo.getClass(), "gamemode", int.class) + .set(packetPlayOutPlayerInfo, 1); + + return packetPlayOutPlayerInfo; + } +} diff --git a/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java new file mode 100755 index 0000000..2547cdc --- /dev/null +++ b/nms/v1_7_R4/src/main/java/net/jitse/npclib/nms/v1_7_R4/packets/PacketPlayOutScoreboardTeamWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 Jitse Boonstra + */ + +package net.jitse.npclib.nms.v1_7_R4.packets; + +import com.comphenix.tinyprotocol.Reflection; +import net.minecraft.server.v1_7_R4.PacketPlayOutScoreboardTeam; + +import java.util.Collection; + +/** + * @author Jitse Boonstra + */ +public class PacketPlayOutScoreboardTeamWrapper { + + public PacketPlayOutScoreboardTeam createRegisterTeam(String uuidName, String name) { + PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(); + + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", int.class) + .set(packetPlayOutScoreboardTeam, 0); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "b", String.class) + .set(packetPlayOutScoreboardTeam, name.length() < 16 ? name : name.substring(0, 15)); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, uuidName); + if (name.length() > 16) { + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "d", String.class) + .set(packetPlayOutScoreboardTeam, name.substring(15)); + } + if (name.length() > 32) { + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "c", String.class) + .set(packetPlayOutScoreboardTeam, name.substring(31)); + } + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "g", int.class) + .set(packetPlayOutScoreboardTeam, 1); + Reflection.FieldAccessor collectionFieldAccessor = Reflection.getField( + packetPlayOutScoreboardTeam.getClass(), "e", Collection.class); + Collection collection = collectionFieldAccessor.get(packetPlayOutScoreboardTeam); + collection.add(name.length() < 16 ? name : name.substring(0, 15)); + collectionFieldAccessor.set(packetPlayOutScoreboardTeam, collection); + + return packetPlayOutScoreboardTeam; + } + + public PacketPlayOutScoreboardTeam createUnregisterTeam(String uuidName) { + PacketPlayOutScoreboardTeam packetPlayOutScoreboardTeam = new PacketPlayOutScoreboardTeam(); + + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "f", int.class) + .set(packetPlayOutScoreboardTeam, 1); + Reflection.getField(packetPlayOutScoreboardTeam.getClass(), "a", String.class) + .set(packetPlayOutScoreboardTeam, uuidName); + + return packetPlayOutScoreboardTeam; + } +} diff --git a/nms/v1_8_R1/pom.xml b/nms/v1_8_R1/pom.xml index 25d62d1..7adb9ec 100755 --- a/nms/v1_8_R1/pom.xml +++ b/nms/v1_8_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_8_R1 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/pom.xml b/nms/v1_8_R2/pom.xml index 4936191..feda596 100755 --- a/nms/v1_8_R2/pom.xml +++ b/nms/v1_8_R2/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_8_R2 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/pom.xml b/nms/v1_8_R3/pom.xml index 3445740..cfdf521 100755 --- a/nms/v1_8_R3/pom.xml +++ b/nms/v1_8_R3/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_8_R3 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/pom.xml b/nms/v1_9_R1/pom.xml index 1e450c9..550baf2 100755 --- a/nms/v1_9_R1/pom.xml +++ b/nms/v1_9_R1/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_9_R1 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/pom.xml b/nms/v1_9_R2/pom.xml index 62d69bc..e5aaeb3 100755 --- a/nms/v1_9_R2/pom.xml +++ b/nms/v1_9_R2/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib-nms - 1.3 + 1.4 npclib-nms-v1_9_R2 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); diff --git a/plugin/pom.xml b/plugin/pom.xml index f9f3393..8e2e1fc 100755 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -7,7 +7,7 @@ net.jitse npclib - 1.3 + 1.4 npclib-plugin diff --git a/pom.xml b/pom.xml index 2987d62..5e9bb07 100755 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ net.jitse npclib - 1.3 + 1.4 NPCLib https://github.com/JitseB/NPCLib