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> {
|
||||
|
||||
protected Player player;
|
||||
private @Getter VCMDWrapper vanillaCmdListener;
|
||||
private @Getter final VCMDWrapper vanillaCmdListener;
|
||||
|
||||
public DiscordPlayerSender(User user, MessageChannel channel, Player player, MinecraftChatModule module) {
|
||||
super(user, channel);
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.mockito.invocation.InvocationOnMock;
|
|||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
@ -25,6 +26,7 @@ import java.util.UUID;
|
|||
public class PlayerListWatcher {
|
||||
private static Object plist;
|
||||
private static Object mock;
|
||||
private static MethodHandle fHandle; //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16
|
||||
|
||||
/*public PlayerListWatcher(DedicatedServer minecraftserver) {
|
||||
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 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
|
||||
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
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
|
@ -128,24 +125,19 @@ public class PlayerListWatcher {
|
|||
return null;
|
||||
}
|
||||
//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")) {
|
||||
//val fHandle = MethodHandles.lookup().findSpecial(dplc, "f", MethodType.methodType(adpcl, epcl), mock.getClass());
|
||||
System.out.println("Declaring class: " + method.getDeclaringClass());
|
||||
if (nms.contains("1_16") && method.getName().equals("f") && method.getParameterCount() > 0 && method.getParameterTypes()[0].getSimpleName().equals("EntityPlayer")) {
|
||||
method.setAccessible(true);
|
||||
//new ByteBuddy().subclass(dplc).method(ElementMatchers.named("f"))
|
||||
var lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class);
|
||||
lookupConstructor.setAccessible(true); //Create lookup with a given class instead of caller
|
||||
var lookup = lookupConstructor.newInstance(mock.getClass());
|
||||
val fHandle = lookup.unreflectSpecial(method, mock.getClass()); //Special: super.method()
|
||||
if (fHandle == null) {
|
||||
assert lookupConstructor != null;
|
||||
var lookup = lookupConstructor.newInstance(mock.getClass());
|
||||
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 method.invoke(plist, invocation.getArguments());
|
||||
}
|
||||
val args = invocation.getArguments();
|
||||
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) {
|
||||
TBMCCoreAPI.SendException("Found a strange method",
|
||||
new Exception("Found a sendMessage() method without arguments."));
|
||||
|
@ -188,14 +180,7 @@ public class PlayerListWatcher {
|
|||
private void sendAll(Object packet) {
|
||||
try { // Some messages get sent by directly constructing a 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) {
|
||||
//System.out.println("Indeed a chat packet");
|
||||
Field msgf = ppoc.getDeclaredField("a");
|
||||
msgf.setAccessible(true);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*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());
|
||||
plist = currentPL;
|
||||
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.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class VCMDWrapper {
|
||||
@Getter //Needed to mock the player
|
||||
@Nullable
|
||||
private final Object listener;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +41,7 @@ public class VCMDWrapper {
|
|||
ret = new VanillaCommandListener<>(player, bukkitplayer);
|
||||
else if (mcpackage.contains("1_14"))
|
||||
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
|
||||
else
|
||||
ret = null;
|
||||
|
@ -55,4 +58,9 @@ public class VCMDWrapper {
|
|||
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.");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
||||
if (icommandlistener == null)
|
||||
return VCMDWrapper.compatResponse(dsender);
|
||||
String[] args = cmdstr.split(" ");
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
try {
|
||||
|
|
|
@ -87,6 +87,8 @@ public class VanillaCommandListener14<T extends DiscordSenderBase & IMCPlayer<T>
|
|||
|
||||
val world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
|
||||
ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener();
|
||||
if (icommandlistener == null)
|
||||
return VCMDWrapper.compatResponse(dsender);
|
||||
val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0),
|
||||
new Vec2F(0, 0), world, 0, sender.getName(),
|
||||
new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);
|
||||
|
|
|
@ -79,7 +79,7 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
|||
var server = Bukkit.getServer();
|
||||
var cmap = (SimpleCommandMap) server.getClass().getMethod("getCommandMap").invoke(server);
|
||||
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
|
||||
|
||||
if (!(dsender instanceof IMCPlayer))
|
||||
|
@ -94,7 +94,8 @@ public class VanillaCommandListener15<T extends DiscordSenderBase & IMCPlayer<T>
|
|||
var cworld = Bukkit.getWorlds().get(0);
|
||||
val world = cworld.getClass().getMethod("getHandle").invoke(cworld);
|
||||
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 v3dcl = Class.forName(nms + ".Vec3D");
|
||||
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 ecl = Class.forName(nms + ".Entity");
|
||||
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,
|
||||
v3dcl.getConstructor(double.class, double.class, double.class).newInstance(0, 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 ChatComponentText(sender.getName()), world.getMinecraftServer(), null);*/
|
||||
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);
|
||||
String[] args = cmdstr.split(" ");
|
||||
args = Arrays.copyOfRange(args, 1, args.length);
|
||||
|
|
Loading…
Reference in a new issue