Cleaned up some code (#10).

This commit is contained in:
Jitse Boonstra 2019-02-15 19:59:38 +01:00
parent 7101542328
commit 06cec6c0bf
5 changed files with 121 additions and 541 deletions

View File

@ -20,11 +20,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
/**
* Represents a very tiny alternative to ProtocolLib.
* <p>
* 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 LegacyTinyProtocol {
private static final AtomicInteger ID = new AtomicInteger(0);
@ -56,7 +52,7 @@ public abstract class LegacyTinyProtocol {
private Listener listener;
// Channels that have already been removed
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
// List of network markers
private List<Object> networkManagers;
@ -70,39 +66,32 @@ public abstract class LegacyTinyProtocol {
// 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.
* <p>
* You can construct multiple instances per plugin.
*
* @param plugin - the plugin.
*/
public LegacyTinyProtocol(final Plugin plugin) {
protected LegacyTinyProtocol(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("Attempting to inject into netty");
System.out.println("[NPCLib] Attempting to inject into netty.");
registerChannelHandler();
registerPlayers(plugin);
} catch (IllegalArgumentException ex) {
// Damn you, late bind
plugin.getLogger().info("Attempting to delay injection.");
plugin.getLogger().info("[NPCLib] Attempting to delay injection.");
new BukkitRunnable() {
@Override
public void run() {
registerChannelHandler();
registerPlayers(plugin);
plugin.getLogger().info("Injection complete.");
plugin.getLogger().info("[NPCLib] Injection complete.");
}
}.runTask(plugin);
}
@ -112,6 +101,7 @@ public abstract class LegacyTinyProtocol {
// Handle connected channels
endInitProtocol = new ChannelInitializer<Channel>() {
@SuppressWarnings("all")
@Override
protected void initChannel(Channel channel) throws Exception {
try {
@ -123,7 +113,7 @@ public abstract class LegacyTinyProtocol {
}
}
} 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);
}
}
@ -132,6 +122,7 @@ public abstract class LegacyTinyProtocol {
// This is executed before Minecraft's channel handler
beginInitProtocol = new ChannelInitializer<Channel>() {
@SuppressWarnings("all")
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(endInitProtocol);
@ -141,6 +132,7 @@ public abstract class LegacyTinyProtocol {
serverChannelHandler = new ChannelInboundHandlerAdapter() {
@SuppressWarnings("all")
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = (Channel) msg;
@ -152,12 +144,10 @@ public abstract class LegacyTinyProtocol {
};
}
/**
* Register bukkit events.
*/
private void registerBukkitEvents() {
listener = new Listener() {
@SuppressWarnings("unused")
@EventHandler(priority = EventPriority.LOWEST)
public final void onPlayerLogin(PlayerJoinEvent e) {
if (closed)
@ -171,6 +161,7 @@ public abstract class LegacyTinyProtocol {
}
}
@SuppressWarnings("unused")
@EventHandler
public final void onPluginDisable(PluginDisableEvent e) {
if (e.getPlugin().equals(plugin)) {
@ -205,9 +196,9 @@ public abstract class LegacyTinyProtocol {
Channel serverChannel = ((ChannelFuture) item).channel();
serverChannels.add(serverChannel);
;
serverChannel.pipeline().addFirst(serverChannelHandler);
System.out.println("Server channel handler injected (" + serverChannel + ")");
System.out.println("[NPCLib] Server channel handler injected (" + serverChannel + ")");
looking = false;
}
}
@ -237,118 +228,14 @@ public abstract class LegacyTinyProtocol {
}
}
/**
* Invoked when the server is starting to send a packet to a player.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* @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);
@ -367,13 +254,7 @@ public abstract class LegacyTinyProtocol {
}
}
/**
* 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
@ -387,37 +268,7 @@ public abstract class LegacyTinyProtocol {
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.
* <p>
* 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) {
private void uninjectChannel(final Channel channel) {
// No need to guard against this if we're closing
if (!closed) {
uninjectedChannels.add(channel);
@ -427,36 +278,13 @@ public abstract class LegacyTinyProtocol {
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);
uninjectChannel(getChannel(player));
}
// Clean up Bukkit
@ -465,11 +293,6 @@ public abstract class LegacyTinyProtocol {
}
}
/**
* 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;
@ -491,25 +314,12 @@ public abstract class LegacyTinyProtocol {
try {
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) {
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);
}
}
}
}

View File

@ -21,11 +21,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
/**
* Represents a very tiny alternative to ProtocolLib.
* <p>
* 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);
@ -53,7 +49,7 @@ public abstract class TinyProtocol {
private Listener listener;
// Channels that have already been removed
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().<Channel, Boolean>makeMap());
private Set<Channel> uninjectedChannels = Collections.newSetFromMap(new MapMaker().weakKeys().makeMap());
// List of network markers
private List<Object> networkManagers;
@ -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.
* <p>
* 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<Channel>() {
@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<Channel>() {
@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<Object> 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,120 +224,14 @@ public abstract class TinyProtocol {
}
}
/**
* Invoked when the server is starting to send a packet to a player.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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.
* @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);
@ -369,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
@ -389,69 +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.
* <p>
* 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(new Runnable() {
@Override
public void run() {
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
@ -460,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;
@ -476,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) {
@ -486,19 +307,6 @@ 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)) {
Object profile = getGameProfile.get(packet);

View File

@ -6,7 +6,6 @@ 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;
@ -67,11 +66,7 @@ public class NPCLib {
pluginManager.registerEvents(new ChunkListener(), plugin);
// Boot the according packet listener.
if (Bukkit.getBukkitVersion().contains("1.7")) {
new LegacyPacketListener().start(plugin);
} else {
new PacketListener().start(plugin);
}
new PacketListener().start(plugin, Bukkit.getBukkitVersion().contains("1.7"));
}
/**

View File

@ -1,71 +0,0 @@
/*
* 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<UUID> 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);
}
};
}
}

View File

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