Added 1.7.10 NPC interaction support (for #10).

This commit is contained in:
Jitse Boonstra
2019-02-13 11:42:12 +01:00
parent ab5f07b976
commit 7101542328
18 changed files with 757 additions and 57 deletions
@@ -6,9 +6,11 @@ package net.jitse.npclib;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.listeners.ChunkListener;
import net.jitse.npclib.listeners.LegacyPacketListener;
import net.jitse.npclib.listeners.PacketListener;
import net.jitse.npclib.listeners.PlayerListener;
import net.jitse.npclib.skin.Skin;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.plugin.PluginManager;
@@ -64,7 +66,12 @@ public class NPCLib {
pluginManager.registerEvents(new PlayerListener(), plugin);
pluginManager.registerEvents(new ChunkListener(), plugin);
new PacketListener().start(plugin);
// Boot the according packet listener.
if (Bukkit.getBukkitVersion().contains("1.7")) {
new LegacyPacketListener().start(plugin);
} else {
new PacketListener().start(plugin);
}
}
/**
@@ -4,9 +4,8 @@
package net.jitse.npclib.api;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.wrapper.GameProfileWrapper;
import net.jitse.npclib.events.NPCDestroyEvent;
import net.jitse.npclib.events.NPCSpawnEvent;
import net.jitse.npclib.events.trigger.TriggerType;
@@ -39,7 +38,7 @@ public abstract class NPC implements PacketHandler, ActionHandler {
protected final List<String> lines;
protected JavaPlugin plugin;
protected GameProfile gameProfile;
protected GameProfileWrapper gameProfile;
protected Location location;
public NPC(JavaPlugin plugin, Skin skin, double autoHideDistance, List<String> 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;
@@ -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;
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 Jitse Boonstra
*/
package net.jitse.npclib.listeners;
import com.comphenix.tinyprotocol.LegacyTinyProtocol;
import com.comphenix.tinyprotocol.Reflection;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.NPCInteractEvent;
import net.jitse.npclib.events.click.ClickType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author Jitse Boonstra
*/
public class LegacyPacketListener {
// Classes:
private final Class<?> packetPlayInUseEntityClazz = Reflection.getMinecraftClass("PacketPlayInUseEntity");
// Fields:
private final Reflection.FieldAccessor entityIdField = Reflection.getField(packetPlayInUseEntityClazz, "a", int.class);
private final Reflection.FieldAccessor actionField = Reflection.getField(packetPlayInUseEntityClazz, "action", Object.class);
// Prevent players from clicking at very high speeds.
private final Set<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);
}
};
}
}
@@ -5,6 +5,14 @@
package net.jitse.npclib.listeners;
import com.comphenix.tinyprotocol.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import io.netty.channel.Channel;
import net.jitse.npclib.NPCManager;
import net.jitse.npclib.api.NPC;
import net.jitse.npclib.events.NPCInteractEvent;
import net.jitse.npclib.events.click.ClickType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashSet;
@@ -27,38 +35,38 @@ public class PacketListener {
private final Set<UUID> delay = new HashSet<>();
public void start(JavaPlugin plugin) {
// new TinyProtocol(plugin) {
//
// @Override
// public Object onPacketInAsync(Player player, Channel channel, Object packet) {
//
// if (packetPlayInUseEntityClazz.isInstance(packet)) {
// NPC npc = NPCManager.getAllNPCs().stream().filter(
// check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
// .findFirst().orElse(null);
//
// if (npc == null) {
// // Default player, not doing magic with the packet.
// return super.onPacketInAsync(player, channel, packet);
// }
//
// if (delay.contains(player.getUniqueId())) {
// return null;
// }
//
// ClickType clickType = actionField.get(packet).toString()
// .equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK;
//
// Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc));
//
// UUID uuid = player.getUniqueId();
// delay.add(uuid);
// Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid));
// return null;
// }
//
// return super.onPacketInAsync(player, channel, packet);
// }
// };
new TinyProtocol(plugin) {
@Override
public Object onPacketInAsync(Player player, Channel channel, Object packet) {
if (packetPlayInUseEntityClazz.isInstance(packet)) {
NPC npc = NPCManager.getAllNPCs().stream().filter(
check -> check.isActuallyShown(player) && check.getEntityId() == (int) entityIdField.get(packet))
.findFirst().orElse(null);
if (npc == null) {
// Default player, not doing magic with the packet.
return super.onPacketInAsync(player, channel, packet);
}
if (delay.contains(player.getUniqueId())) {
return null;
}
ClickType clickType = actionField.get(packet).toString()
.equals("ATTACK") ? ClickType.LEFT_CLICK : ClickType.RIGHT_CLICK;
Bukkit.getPluginManager().callEvent(new NPCInteractEvent(player, clickType, npc));
UUID uuid = player.getUniqueId();
delay.add(uuid);
Bukkit.getScheduler().runTask(plugin, () -> delay.remove(uuid));
return null;
}
return super.onPacketInAsync(player, channel, packet);
}
};
}
}