From d9ca07b195caedb1dc35377d8bf302154cd4a917 Mon Sep 17 00:00:00 2001 From: Kneesnap Date: Fri, 20 Sep 2019 14:34:16 -0700 Subject: [PATCH] Optimize important code, add distance interaction check. --- .../npclib/listeners/PacketListener.java | 60 +++++++++++++++---- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/api/src/main/java/net/jitse/npclib/listeners/PacketListener.java b/api/src/main/java/net/jitse/npclib/listeners/PacketListener.java index 36a6431..a02b737 100755 --- a/api/src/main/java/net/jitse/npclib/listeners/PacketListener.java +++ b/api/src/main/java/net/jitse/npclib/listeners/PacketListener.java @@ -11,12 +11,11 @@ import net.jitse.npclib.api.events.NPCInteractEvent; import net.jitse.npclib.internal.NPCManager; import net.jitse.npclib.internal.SimpleNPC; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import java.util.*; /** * @author Jitse Boonstra @@ -49,31 +48,66 @@ public class PacketListener { private boolean handleInteractPacket(Player player, Object packet) { if (packetPlayInUseEntityClazz.isInstance(packet)) { - SimpleNPC npc = NPCManager.getAllNPCs().stream().filter( - check -> check.isShown(player) && check.getEntityId() == (int) entityIdField.get(packet)) - .findFirst().orElse(null); + SimpleNPC npc = null; + int packetEntityId = (int) entityIdField.get(packet); + + + // Not using streams here is an intentional choice. + // Packet listeners is one of the few places where it is important to write optimized code. + // Lambdas (And the stream api) create a massive amount of objects, especially if it isn't a static lambda. + // So, we're avoiding them here. + // ~ Kneesnap, 9 / 20 / 2019. + + for (SimpleNPC testNPC : NPCManager.getAllNPCs()) { + if (testNPC.isShown(player) && testNPC.getEntityId() == packetEntityId) { + npc = testNPC; + break; + } + } if (npc == null) { // Default player, not doing magic with the packet. return true; } - if (delay.contains(player.getUniqueId())) { + if (delay.contains(player.getUniqueId())) { // There is an active delay. return false; } NPCInteractEvent.ClickType clickType = actionField.get(packet).toString().equals("ATTACK") ? NPCInteractEvent.ClickType.LEFT_CLICK : NPCInteractEvent.ClickType.RIGHT_CLICK; - Bukkit.getScheduler().runTask(plugin, () -> - Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc))); - - UUID uuid = player.getUniqueId(); - delay.add(uuid); - Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid)); + delay.add(player.getUniqueId()); + Bukkit.getScheduler().runTask(plugin, new TaskCallNpcInteractEvent(new NPCInteractEvent(player, clickType, npc), this)); return false; } return true; } + + // This would be a non-static lambda, and its usage matters, so we'll make it a full class. + private static final class TaskCallNpcInteractEvent implements Runnable { + private NPCInteractEvent eventToCall; + private PacketListener listener; + + private static Location playerLocation = new Location(null, 0, 0, 0); + + TaskCallNpcInteractEvent(NPCInteractEvent eventToCall, PacketListener listener) { + this.eventToCall = eventToCall; + this.listener = listener; + } + + @Override + public void run() { + Player player = eventToCall.getWhoClicked(); + this.listener.delay.remove(player.getUniqueId()); // Remove the NPC from the interact cooldown. + + if (!Objects.equals(playerLocation.getWorld(), eventToCall.getNPC().getLocation().getWorld())) + return; // If the NPC and player are not in the same world, abort! + + double distance = player.getLocation(playerLocation).distanceSquared(eventToCall.getNPC().getLocation()); + if (distance <= 64) // Only handle the interaction if the player is within interaction range. This way, hacked clients can't interact with NPCs that they shouldn't be able to interact with. + Bukkit.getPluginManager().callEvent(this.eventToCall); + } + } }