diff --git a/pom.xml b/pom.xml index 9859e28..0b752c5 100755 --- a/pom.xml +++ b/pom.xml @@ -180,7 +180,7 @@ com.discord4j discord4j-core - 3.0.14 + 3.0.15 diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index 8ba5d77..86c3375 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -248,7 +248,7 @@ public class DiscordPlugin extends ButtonPlugin { .sanitizeString(Bukkit.getOnlinePlayers().stream() .map(Player::getDisplayName).collect(Collectors.joining(", "))) + (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ") - + "kicked the hell out.") //TODO: Make configurable + + "thrown out") //TODO: Make configurable : ""); //If 'restart' is disabled then this isn't shown even if joinleave is enabled })).subscribe(), ChannelconBroadcast.RESTART, false); timings.printElapsed("Updating player list"); diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index 3130092..18e70ff 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -3,15 +3,23 @@ package buttondevteam.discordplugin.broadcaster; import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.lib.TBMCCoreAPI; import lombok.val; +import net.bytebuddy.ByteBuddy; +import net.bytebuddy.matcher.ElementMatchers; import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.io.File; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Map; import java.util.UUID; public class PlayerListWatcher { @@ -98,6 +106,18 @@ 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 mock = Mockito.mock(dplc, Mockito.withSettings().defaultAnswer(new Answer<>() { // Cannot call super constructor @Override public Object answer(InvocationOnMock invocation) throws Throwable { @@ -107,10 +127,25 @@ public class PlayerListWatcher { sendAll(invocation.getArgument(0)); 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()); + 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() + 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.")); @@ -142,8 +177,6 @@ public class PlayerListWatcher { var comp = fixComponent.invoke(null, chatComponent); var packet = ppocC.getParameterCount() == 3 ? ppocC.newInstance(comp, chatmessagetype, sysb.get(null)) - - : ppocC.newInstance(comp, chatmessagetype); this.sendAll(packet); // CraftBukkit end @@ -155,7 +188,14 @@ 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)))); @@ -164,6 +204,31 @@ 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 map = (Map) 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 diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 3afc038..313e4e8 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.listeners; import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.commands.ConnectCommand; +import buttondevteam.lib.TBMCCommandPreprocessEvent; import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; import buttondevteam.lib.player.TBMCPlayerJoinEvent; import discord4j.core.object.entity.Member; @@ -52,7 +53,7 @@ public class MCListener implements Listener { } @EventHandler - public void onServerCommand(ServerCommandEvent e) { - DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped + public void onCommandPreprocess(TBMCCommandPreprocessEvent e){ + DiscordPlugin.Restart = !e.getMessage().equalsIgnoreCase("/stop"); // The variable is always true except if stopped } }