Fixes, fix vanilla command handling on 1.16
And on unsupported versions too
This commit is contained in:
parent
6b60135867
commit
6bf91afab9
6 changed files with 33 additions and 59 deletions
|
@ -13,7 +13,7 @@ import java.lang.reflect.Modifier;
|
||||||
public abstract class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {
|
public abstract class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {
|
||||||
|
|
||||||
protected Player player;
|
protected Player player;
|
||||||
private @Getter VCMDWrapper vanillaCmdListener;
|
private @Getter final VCMDWrapper vanillaCmdListener;
|
||||||
|
|
||||||
public DiscordPlayerSender(User user, MessageChannel channel, Player player, MinecraftChatModule module) {
|
public DiscordPlayerSender(User user, MessageChannel channel, Player player, MinecraftChatModule module) {
|
||||||
super(user, channel);
|
super(user, channel);
|
||||||
|
|
|
@ -12,6 +12,7 @@ import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
@ -25,6 +26,7 @@ import java.util.UUID;
|
||||||
public class PlayerListWatcher {
|
public class PlayerListWatcher {
|
||||||
private static Object plist;
|
private static Object plist;
|
||||||
private static Object mock;
|
private static Object mock;
|
||||||
|
private static MethodHandle fHandle; //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16
|
||||||
|
|
||||||
/*public PlayerListWatcher(DedicatedServer minecraftserver) {
|
/*public PlayerListWatcher(DedicatedServer minecraftserver) {
|
||||||
super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis
|
super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis
|
||||||
|
@ -107,17 +109,12 @@ public class PlayerListWatcher {
|
||||||
val toPlainText = tpt;
|
val toPlainText = tpt;
|
||||||
val sysb = Class.forName(nms + ".SystemUtils").getField("b");
|
val sysb = Class.forName(nms + ".SystemUtils").getField("b");
|
||||||
|
|
||||||
//TODO: 1.16 only
|
|
||||||
//String cbs = nms.replace("net.minecraft.server", "org.bukkit.craftbukkit");
|
|
||||||
val epcl = Class.forName(nms + ".EntityPlayer");
|
|
||||||
/*val getUUID = epcl.getMethod("getUniqueID");
|
|
||||||
val getPlayer = Class.forName(cbs + ".CraftPlayer").getConstructors()[0];
|
|
||||||
val getDataFixer = server.getClass().getMethod("getDataFixer");
|
|
||||||
val getAdvancementData = server.getClass().getMethod("getAdvancementData");
|
|
||||||
val adpcl = Class.forName(nms + ".AdvancementDataPlayer").getConstructors()[0];
|
|
||||||
val setEPlayer = getAdvancementData.getReturnType().getMethod("a", epcl);*/
|
|
||||||
val adpcl = Class.forName(nms + ".AdvancementDataPlayer");
|
|
||||||
//Find the original method without overrides
|
//Find the original method without overrides
|
||||||
|
Constructor<MethodHandles.Lookup> lookupConstructor;
|
||||||
|
if (nms.contains("1_16")) {
|
||||||
|
lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
|
||||||
|
lookupConstructor.setAccessible(true); //Create lookup with a given class instead of caller
|
||||||
|
} else lookupConstructor = null;
|
||||||
mock = Mockito.mock(dplc, Mockito.withSettings().defaultAnswer(new Answer<>() { // Cannot call super constructor
|
mock = Mockito.mock(dplc, Mockito.withSettings().defaultAnswer(new Answer<>() { // Cannot call super constructor
|
||||||
@Override
|
@Override
|
||||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||||
|
@ -128,24 +125,19 @@ public class PlayerListWatcher {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//In 1.16 it passes a reference to the player list to advancement data for each player
|
//In 1.16 it passes a reference to the player list to advancement data for each player
|
||||||
if (method.getName().equals("f") && method.getParameterCount() > 0 && method.getParameterTypes()[0].getSimpleName().equals("EntityPlayer")) {
|
if (nms.contains("1_16") && method.getName().equals("f") && method.getParameterCount() > 0 && method.getParameterTypes()[0].getSimpleName().equals("EntityPlayer")) {
|
||||||
//val fHandle = MethodHandles.lookup().findSpecial(dplc, "f", MethodType.methodType(adpcl, epcl), mock.getClass());
|
|
||||||
System.out.println("Declaring class: " + method.getDeclaringClass());
|
|
||||||
method.setAccessible(true);
|
method.setAccessible(true);
|
||||||
//new ByteBuddy().subclass(dplc).method(ElementMatchers.named("f"))
|
if (fHandle == null) {
|
||||||
var lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
|
assert lookupConstructor != null;
|
||||||
lookupConstructor.setAccessible(true); //Create lookup with a given class instead of caller
|
|
||||||
var lookup = lookupConstructor.newInstance(mock.getClass());
|
var lookup = lookupConstructor.newInstance(mock.getClass());
|
||||||
val fHandle = lookup.unreflectSpecial(method, mock.getClass()); //Special: super.method()
|
fHandle = lookup.unreflectSpecial(method, mock.getClass()); //Special: super.method()
|
||||||
|
}
|
||||||
return fHandle.invoke(mock, invocation.getArgument(0)); //Invoke with our instance, so it passes that to advancement data, we have the fields as well
|
return fHandle.invoke(mock, invocation.getArgument(0)); //Invoke with our instance, so it passes that to advancement data, we have the fields as well
|
||||||
}
|
}
|
||||||
return method.invoke(plist, invocation.getArguments());
|
return method.invoke(plist, invocation.getArguments());
|
||||||
}
|
}
|
||||||
val args = invocation.getArguments();
|
val args = invocation.getArguments();
|
||||||
val params = method.getParameterTypes();
|
val params = method.getParameterTypes();
|
||||||
System.out.println("Method: " + method.getName());
|
|
||||||
System.out.println("args: " + Arrays.toString(args));
|
|
||||||
System.out.println("params: " + Arrays.toString(params));
|
|
||||||
if (params.length == 0) {
|
if (params.length == 0) {
|
||||||
TBMCCoreAPI.SendException("Found a strange method",
|
TBMCCoreAPI.SendException("Found a strange method",
|
||||||
new Exception("Found a sendMessage() method without arguments."));
|
new Exception("Found a sendMessage() method without arguments."));
|
||||||
|
@ -188,14 +180,7 @@ public class PlayerListWatcher {
|
||||||
private void sendAll(Object packet) {
|
private void sendAll(Object packet) {
|
||||||
try { // Some messages get sent by directly constructing a packet
|
try { // Some messages get sent by directly constructing a packet
|
||||||
sendAll.invoke(plist, packet);
|
sendAll.invoke(plist, packet);
|
||||||
/*if(packet.getClass().getName().contains("Chat"))
|
|
||||||
System.out.println("Chat packet: "+packet);
|
|
||||||
if(packet.getClass().getName().contains("Advancement"))
|
|
||||||
System.out.println("Advancement packet: "+packet);*/
|
|
||||||
if (!packet.getClass().getName().contains("KeepAlive"))
|
|
||||||
System.out.println("Packet: " + packet);
|
|
||||||
if (packet.getClass() == ppoc) {
|
if (packet.getClass() == ppoc) {
|
||||||
//System.out.println("Indeed a chat packet");
|
|
||||||
Field msgf = ppoc.getDeclaredField("a");
|
Field msgf = ppoc.getDeclaredField("a");
|
||||||
msgf.setAccessible(true);
|
msgf.setAccessible(true);
|
||||||
MCChatUtils.forAllMCChat(MCChatUtils.send((String) toPlainText.invoke(msgf.get(packet))));
|
MCChatUtils.forAllMCChat(MCChatUtils.send((String) toPlainText.invoke(msgf.get(packet))));
|
||||||
|
@ -204,31 +189,6 @@ public class PlayerListWatcher {
|
||||||
TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e);
|
TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*public Object f(Object entityplayer) { //Returns advancement data
|
|
||||||
try {
|
|
||||||
UUID uuid = (UUID) getUUID.invoke(entityplayer);
|
|
||||||
@SuppressWarnings("unchecked") Map<UUID, Object> map = (Map<UUID, Object>) dplc.getField("p").get(plist);
|
|
||||||
var advancementdataplayer = map.get(uuid);
|
|
||||||
|
|
||||||
if (advancementdataplayer == null) {
|
|
||||||
var player = (Player) getPlayer.newInstance(Bukkit.getServer(), entityplayer);
|
|
||||||
var file = new File(player.getWorld().getWorldFolder(), "advancements");
|
|
||||||
File file1 = new File(file, uuid + ".json");
|
|
||||||
|
|
||||||
var dataFixer = getDataFixer.invoke(server);
|
|
||||||
var advData = getAdvancementData.invoke(server);
|
|
||||||
advancementdataplayer = adpcl.newInstance(dataFixer, this, advData, file1, entityplayer);
|
|
||||||
//advancementdataplayer = new AdvancementDataPlayer(this.server.getDataFixer(), this, this.server.getAdvancementData(), file1, entityplayer);
|
|
||||||
map.put(uuid, advancementdataplayer);
|
|
||||||
}
|
|
||||||
|
|
||||||
setEPlayer.invoke(advancementdataplayer, entityplayer);
|
|
||||||
return advancementdataplayer;
|
|
||||||
} catch (Exception e) {
|
|
||||||
TBMCCoreAPI.SendException("An error occurred while getting advancement data!", e);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}).stubOnly());
|
}).stubOnly());
|
||||||
plist = currentPL;
|
plist = currentPL;
|
||||||
for (var plc = dplc; plc != null; plc = plc.getSuperclass()) { //Set all fields
|
for (var plc = dplc; plc != null; plc = plc.getSuperclass()) { //Set all fields
|
||||||
|
|
|
@ -9,9 +9,12 @@ import lombok.RequiredArgsConstructor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class VCMDWrapper {
|
public class VCMDWrapper {
|
||||||
@Getter //Needed to mock the player
|
@Getter //Needed to mock the player
|
||||||
|
@Nullable
|
||||||
private final Object listener;
|
private final Object listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +41,7 @@ public class VCMDWrapper {
|
||||||
ret = new VanillaCommandListener<>(player, bukkitplayer);
|
ret = new VanillaCommandListener<>(player, bukkitplayer);
|
||||||
else if (mcpackage.contains("1_14"))
|
else if (mcpackage.contains("1_14"))
|
||||||
ret = new VanillaCommandListener14<>(player, bukkitplayer);
|
ret = new VanillaCommandListener14<>(player, bukkitplayer);
|
||||||
else if (mcpackage.contains("1_15") || mcpackage.contains("1.16"))
|
else if (mcpackage.contains("1_15") || mcpackage.contains("1_16"))
|
||||||
ret = VanillaCommandListener15.create(player, bukkitplayer); //bukkitplayer may be null but that's fine
|
ret = VanillaCommandListener15.create(player, bukkitplayer); //bukkitplayer may be null but that's fine
|
||||||
else
|
else
|
||||||
ret = null;
|
ret = null;
|
||||||
|
@ -55,4 +58,9 @@ public class VCMDWrapper {
|
||||||
private static void compatWarning(MinecraftChatModule module) {
|
private static void compatWarning(MinecraftChatModule module) {
|
||||||
module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message.");
|
module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean compatResponse(DiscordSenderBase dsender) {
|
||||||
|
dsender.sendMessage("Vanilla commands are not supported on this Minecraft version.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,8 @@ public class VanillaCommandListener<T extends DiscordSenderBase & IMCPlayer<T>>
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
||||||
|
if (icommandlistener == null)
|
||||||
|
return VCMDWrapper.compatResponse(dsender);
|
||||||
String[] args = cmdstr.split(" ");
|
String[] args = cmdstr.split(" ");
|
||||||
args = Arrays.copyOfRange(args, 1, args.length);
|
args = Arrays.copyOfRange(args, 1, args.length);
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -87,6 +87,8 @@ public class VanillaCommandListener14<T extends DiscordSenderBase & IMCPlayer<T>
|
||||||
|
|
||||||
val world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
|
val world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
|
||||||
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
||||||
|
if (icommandlistener == null)
|
||||||
|
return VCMDWrapper.compatResponse(dsender);
|
||||||
val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0),
|
val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0),
|
||||||
new Vec2F(0, 0), world, 0, sender.getName(),
|
new Vec2F(0, 0), world, 0, sender.getName(),
|
||||||
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);
|
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
||||||
var server = Bukkit.getServer();
|
var server = Bukkit.getServer();
|
||||||
var cmap = (SimpleCommandMap) server.getClass().getMethod("getCommandMap").invoke(server);
|
var cmap = (SimpleCommandMap) server.getClass().getMethod("getCommandMap").invoke(server);
|
||||||
val cmd = cmap.getCommand(cmdstr.split(" ")[0].toLowerCase());
|
val cmd = cmap.getCommand(cmdstr.split(" ")[0].toLowerCase());
|
||||||
if (!(dsender instanceof Player) || !vcwcl.isAssignableFrom(dsender.getClass()))
|
if (!(dsender instanceof Player) || !vcwcl.isAssignableFrom(cmd.getClass()))
|
||||||
return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds
|
return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds
|
||||||
|
|
||||||
if (!(dsender instanceof IMCPlayer))
|
if (!(dsender instanceof IMCPlayer))
|
||||||
|
@ -94,7 +94,8 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
||||||
var cworld = Bukkit.getWorlds().get(0);
|
var cworld = Bukkit.getWorlds().get(0);
|
||||||
val world = cworld.getClass().getMethod("getHandle").invoke(cworld);
|
val world = cworld.getClass().getMethod("getHandle").invoke(cworld);
|
||||||
var icommandlistener = sender.getVanillaCmdListener().getListener();
|
var icommandlistener = sender.getVanillaCmdListener().getListener();
|
||||||
var nms = icommandlistener.getClass().getPackage().getName();
|
if (icommandlistener == null)
|
||||||
|
return VCMDWrapper.compatResponse(dsender);
|
||||||
var clwcl = Class.forName(nms + ".CommandListenerWrapper");
|
var clwcl = Class.forName(nms + ".CommandListenerWrapper");
|
||||||
var v3dcl = Class.forName(nms + ".Vec3D");
|
var v3dcl = Class.forName(nms + ".Vec3D");
|
||||||
var v2fcl = Class.forName(nms + ".Vec2F");
|
var v2fcl = Class.forName(nms + ".Vec2F");
|
||||||
|
@ -102,7 +103,8 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
||||||
var mcscl = Class.forName(nms + ".MinecraftServer");
|
var mcscl = Class.forName(nms + ".MinecraftServer");
|
||||||
var ecl = Class.forName(nms + ".Entity");
|
var ecl = Class.forName(nms + ".Entity");
|
||||||
var cctcl = Class.forName(nms + ".ChatComponentText");
|
var cctcl = Class.forName(nms + ".ChatComponentText");
|
||||||
Object wrapper = clwcl.getConstructor(icommandlistener.getClass(), v3dcl, v2fcl, world.getClass(), int.class, String.class, icbcl, mcscl, ecl)
|
var iclcl = Class.forName(nms + ".ICommandListener");
|
||||||
|
Object wrapper = clwcl.getConstructor(iclcl, v3dcl, v2fcl, world.getClass(), int.class, String.class, icbcl, mcscl, ecl)
|
||||||
.newInstance(icommandlistener,
|
.newInstance(icommandlistener,
|
||||||
v3dcl.getConstructor(double.class, double.class, double.class).newInstance(0, 0, 0),
|
v3dcl.getConstructor(double.class, double.class, double.class).newInstance(0, 0, 0),
|
||||||
v2fcl.getConstructor(float.class, float.class).newInstance(0, 0),
|
v2fcl.getConstructor(float.class, float.class).newInstance(0, 0),
|
||||||
|
@ -112,7 +114,7 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
||||||
new Vec2F(0, 0), world, 0, sender.getName(),
|
new Vec2F(0, 0), world, 0, sender.getName(),
|
||||||
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);*/
|
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);*/
|
||||||
var pncscl = Class.forName(vcwcl.getPackage().getName() + ".ProxiedNativeCommandSender");
|
var pncscl = Class.forName(vcwcl.getPackage().getName() + ".ProxiedNativeCommandSender");
|
||||||
Object pncs = pncscl.getConstructor(clwcl, sender.getClass(), sender.getClass())
|
Object pncs = pncscl.getConstructor(clwcl, CommandSender.class, CommandSender.class)
|
||||||
.newInstance(wrapper, sender, sender);
|
.newInstance(wrapper, sender, sender);
|
||||||
String[] args = cmdstr.split(" ");
|
String[] args = cmdstr.split(" ");
|
||||||
args = Arrays.copyOfRange(args, 1, args.length);
|
args = Arrays.copyOfRange(args, 1, args.length);
|
||||||
|
|
Loading…
Reference in a new issue