From 0e24344efdffab26479465a501371046cbf241dc Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 9 Jun 2019 23:33:23 +0200 Subject: [PATCH 01/21] PLW error handling --- .../broadcaster/GeneralEventBroadcasterModule.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java index 9b57812..7aafd27 100644 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java @@ -15,20 +15,21 @@ public class GeneralEventBroadcasterModule extends Component { PlayerListWatcher.hookUp(); DPUtils.getLogger().info("Finished hooking into the player list"); hooked = true; - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while hacking the player list!", e); + } catch (Exception | NoClassDefFoundError e) { + TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e); } } @Override protected void disable() { try { + if (!hooked) return; if (PlayerListWatcher.hookDown()) DPUtils.getLogger().info("Finished unhooking the player list!"); else DPUtils.getLogger().info("Didn't have the player list hooked."); hooked = false; - } catch (Exception e) { + } catch (Exception | NoClassDefFoundError e) { TBMCCoreAPI.SendException("Error while hacking the player list!", e); } } From 4082c2abbf2bd2489535d9603d7d9aa169f7dae6 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 30 Jun 2019 22:05:33 +0200 Subject: [PATCH 02/21] Fixes, error handling Sync events Vanilla command error handling (Older changes ^) Fixed exception Coder pinging --- .../discordplugin/DiscordConnectedPlayer.java | 6 ++- .../discordplugin/DiscordPlayerSender.java | 6 ++- .../exceptions/ExceptionListenerModule.java | 44 +++++++++---------- .../discordplugin/mcchat/MCChatListener.java | 9 +++- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index 6e30a53..843484c 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -19,7 +19,11 @@ public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlay public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { super(user, channel, nextEntityId++, uuid, mcname, module); - vanillaCmdListener = new VanillaCommandListener<>(this); + try { + vanillaCmdListener = new VanillaCommandListener<>(this); + } catch (NoClassDefFoundError e) { + DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); + } } } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java index 807abd4..41bb696 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java @@ -41,7 +41,11 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer< public DiscordPlayerSender(User user, MessageChannel channel, Player player) { super(user, channel); this.player = player; - vanillaCmdListener = new VanillaCommandListener(this); + try { + vanillaCmdListener = new VanillaCommandListener(this); + } catch (NoClassDefFoundError e) { + DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); + } } @Override diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java index d57c313..17d029a 100755 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java @@ -50,28 +50,28 @@ public class ExceptionListenerModule extends Component implements private static void SendException(Throwable e, String sourcemessage) { if (instance == null) return; try { - Mono channel = getChannel(); - assert channel != null; - Mono coderRole; - if (channel instanceof GuildChannel) - coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get(); - else - coderRole = Mono.empty(); - coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder() - : new StringBuilder(role.getMention()).append("\n")) - .defaultIfEmpty(new StringBuilder()) - .flatMap(sb -> { - sb.append(sourcemessage).append("\n"); - sb.append("```").append("\n"); - String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")) - .filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam.")) - .collect(Collectors.joining("\n")); - if (sb.length() + stackTrace.length() >= 1980) - stackTrace = stackTrace.substring(0, 1980 - sb.length()); - sb.append(stackTrace).append("\n"); - sb.append("```"); - return channel.flatMap(ch -> ch.createMessage(sb.toString())); - }).subscribe(); + getChannel().flatMap(channel -> { + Mono coderRole; + if (channel instanceof GuildChannel) + coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get(); + else + coderRole = Mono.empty(); + return coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder() + : new StringBuilder(role.getMention()).append("\n")) + .defaultIfEmpty(new StringBuilder()) + .flatMap(sb -> { + sb.append(sourcemessage).append("\n"); + sb.append("```").append("\n"); + String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")) + .filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam.")) + .collect(Collectors.joining("\n")); + if (sb.length() + stackTrace.length() >= 1980) + stackTrace = stackTrace.substring(0, 1980 - sb.length()); + sb.append(stackTrace).append("\n"); + sb.append("```"); + return channel.createMessage(sb.toString()); + }); + }).subscribe(); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index aa7e278..790edfb 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -321,7 +321,8 @@ public class MCChatListener implements Listener { return; } val ev = new TBMCCommandPreprocessEvent(dsender, dmessage); - Bukkit.getPluginManager().callEvent(ev); + Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> + Bukkit.getPluginManager().callEvent(ev)); if (ev.isCancelled()) return; int spi = cmdlowercased.indexOf(' '); @@ -338,7 +339,11 @@ public class MCChatListener implements Listener { if (clmd != null) { channel.set(clmd.mcchannel); //Hack to send command in the channel } //TODO: Permcheck isn't implemented for commands - VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); + try { + VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); + } catch (NoClassDefFoundError e) { + Bukkit.dispatchCommand(dsender, cmd); + } Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased); if (clmd != null) channel.set(chtmp); From 5a5f653b865646b406f2f4f40c5d555630c7699a Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 8 Jul 2019 02:00:08 +0200 Subject: [PATCH 03/21] Added DC user mention tabcomplete... But only for commands, because that's how it works now apparently #16 Also might have made it 1.14 ready, though I switched the dependency back to 1.12 Oh it's 1.12.2... --- pom.xml | 4 +-- .../discordplugin/DiscordPlayerSender.java | 22 ++++++++-------- .../discordplugin/mcchat/MCListener.java | 25 +++++++++++++++++++ .../playerfaker/DiscordFakePlayer.java | 10 ++++++++ .../playerfaker/DiscordLivingEntity.java | 10 -------- 5 files changed, 49 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index f0c321a..6d68328 100755 --- a/pom.xml +++ b/pom.xml @@ -148,7 +148,7 @@ --> Essentials - http://repo.ess3.net/content/repositories/essrel/ + https://ci.ender.zone/plugin/repository/everything/ projectlombok.org @@ -174,7 +174,7 @@ org.spigotmc spigot-api - 1.12-R0.1-SNAPSHOT + 1.12.2-R0.1-SNAPSHOT provided diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java index 41bb696..680e886 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java @@ -302,10 +302,6 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer< return player.addAttachment(plugin); } - public Block getTargetBlock(HashSet transparent, int maxDistance) { - return player.getTargetBlock(transparent, maxDistance); - } - public World getWorld() { return player.getWorld(); } @@ -354,10 +350,6 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer< player.setCompassTarget(loc); } - public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance) { - return player.getLastTwoTargetBlocks(transparent, maxDistance); - } - public Location getCompassTarget() { return player.getCompassTarget(); } @@ -1096,11 +1088,21 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer< } public void hidePlayer(Player player) { - player.hidePlayer(player); + this.player.hidePlayer(player); + } + + @Override + public void hidePlayer(Plugin plugin, Player player) { + this.player.hidePlayer(plugin, player); } public void showPlayer(Player player) { - player.showPlayer(player); + this.player.showPlayer(player); + } + + @Override + public void showPlayer(Plugin plugin, Player player) { + this.player.showPlayer(plugin, player); } public boolean canSee(Player player) { diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java index 7c44627..231bca8 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -18,10 +18,13 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerCommandSendEvent; import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent.Result; import org.bukkit.event.server.BroadcastMessageEvent; +import org.bukkit.event.server.TabCompleteEvent; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Objects; @@ -154,4 +157,26 @@ class MCListener implements Listener { public void onNickChange(NickChangeEvent event) { MCChatUtils.updatePlayerList(); } + + @EventHandler + public void onTabComplete(TabCompleteEvent event) { + int i = event.getBuffer().lastIndexOf(' '); + String t = event.getBuffer().substring(i + 1); //0 if not found + //System.out.println("Last token: " + t); + if (!t.startsWith("@")) + return; + String token = t.substring(1); + //System.out.println("Token: " + token); + val x = DiscordPlugin.mainServer.getMembers() + .flatMap(m -> Flux.just(m.getUsername(), m.getNickname().orElse(""))) + .filter(s -> s.startsWith(token)) + .map(s -> "@" + s) + .doOnNext(event.getCompletions()::add).blockLast(); + //System.out.println("Finished - last: " + x); + } + + @EventHandler + public void onCommandSend(PlayerCommandSendEvent event) { + event.getCommands().add("g"); + } } diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java index 6879155..1902516 100755 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java @@ -552,10 +552,20 @@ public class DiscordFakePlayer extends DiscordHumanEntity implements Player { public void hidePlayer(Player player) { } + @Override + public void hidePlayer(Plugin plugin, Player player) { + + } + @Override public void showPlayer(Player player) { } + @Override + public void showPlayer(Plugin plugin, Player player) { + + } + @Override public boolean canSee(Player player) { // Nobody can see them return false; diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java index c561fbf..9b0c60c 100755 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java @@ -126,21 +126,11 @@ public abstract class DiscordLivingEntity extends DiscordEntity implements Livin return Arrays.asList(); } - @Override - public Block getTargetBlock(HashSet transparent, int maxDistance) { - return null; - } - @Override public Block getTargetBlock(Set transparent, int maxDistance) { return null; } - @Override - public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance) { - return Arrays.asList(); - } - @Override public List getLastTwoTargetBlocks(Set transparent, int maxDistance) { return Arrays.asList(); From 7db3b17090a19d51339c8462dcde8f8e32add289 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 22 Jul 2019 22:35:15 +0200 Subject: [PATCH 04/21] Made DCP version-independent & msg reset Some things may not be implemented yet Also added a wrapper around the vanilla cmd listener so Mockito doesn't complain about the missing class Msg reset fixed (#101) --- .../discordplugin/DiscordConnectedPlayer.java | 140 +++++++++++++++++- .../discordplugin/DiscordPlayerSender.java | 5 +- .../discordplugin/IMCPlayer.java | 4 +- .../GeneralEventBroadcasterModule.java | 8 +- .../mcchat/ChannelconCommand.java | 2 +- .../discordplugin/mcchat/MCChatPrivate.java | 2 +- .../discordplugin/mcchat/MCChatUtils.java | 5 +- .../mcchat/MinecraftChatModule.java | 2 +- .../playerfaker/VCMDWrapper.java | 12 ++ .../playerfaker/VanillaCommandListener.java | 2 +- 10 files changed, 164 insertions(+), 18 deletions(-) mode change 100755 => 100644 src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java create mode 100644 src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java old mode 100755 new mode 100644 index 843484c..da5ccd4 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -1,29 +1,155 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.mcchat.MinecraftChatModule; -import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer; +import buttondevteam.discordplugin.playerfaker.VCMDWrapper; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; import lombok.Getter; import lombok.Setter; +import lombok.experimental.Delegate; +import org.bukkit.*; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.AsyncPlayerChatEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.permissions.PermissibleBase; +import org.bukkit.permissions.ServerOperator; +import org.mockito.Answers; +import org.mockito.Mockito; +import java.util.HashSet; import java.util.UUID; -public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer { - private static int nextEntityId = 10000; - private @Getter VanillaCommandListener vanillaCmdListener; +public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer { + private @Getter VCMDWrapper vanillaCmdListener; @Getter @Setter private boolean loggedIn = false; - public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { - super(user, channel, nextEntityId++, uuid, mcname, module); + @Delegate(excludes = ServerOperator.class) + private PermissibleBase origPerm; + + private @Getter String name; + + private @Getter OfflinePlayer basePlayer; + + @Getter + @Setter + private PermissibleBase perm; + + private Location location = Bukkit.getWorlds().get(0).getSpawnLocation(); + + private final MinecraftChatModule module; + + @Getter + private final UUID uniqueId; + + /** + * The parameters must match with {@link #create(User, MessageChannel, UUID, String, MinecraftChatModule)} + */ + protected DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, + MinecraftChatModule module) { + super(user, channel); + origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid)); + name = mcname; + this.module = module; + uniqueId = uuid; + displayName = mcname; try { - vanillaCmdListener = new VanillaCommandListener<>(this); + vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener<>(this)); } catch (NoClassDefFoundError e) { DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } } + public void setOp(boolean value) { //CraftPlayer-compatible implementation + this.origPerm.setOp(value); + this.perm.recalculatePermissions(); + } + + public boolean isOp() { return this.origPerm.isOp(); } + + @Override + public boolean teleport(Location location) { + if (module.allowFakePlayerTeleports().get()) + this.location = location; + return true; + } + + @Override + public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) { + if (module.allowFakePlayerTeleports().get()) + this.location = location; + return true; + } + + @Override + public boolean teleport(Entity destination) { + if (module.allowFakePlayerTeleports().get()) + this.location = destination.getLocation(); + return true; + } + + @Override + public boolean teleport(Entity destination, PlayerTeleportEvent.TeleportCause cause) { + if (module.allowFakePlayerTeleports().get()) + this.location = destination.getLocation(); + return true; + } + + @Override + public Location getLocation(Location loc) { + if (loc != null) { + loc.setWorld(getWorld()); + loc.setX(location.getX()); + loc.setY(location.getY()); + loc.setZ(location.getZ()); + loc.setYaw(location.getYaw()); + loc.setPitch(location.getPitch()); + } + + return loc; + } + + @Override + public Server getServer() { + return Bukkit.getServer(); + } + + @Override + public void sendRawMessage(String message) { + sendMessage(message); + } + + @Override + public void chat(String msg) { + Bukkit.getPluginManager() + .callEvent(new AsyncPlayerChatEvent(true, this, msg, new HashSet<>(Bukkit.getOnlinePlayers()))); + } + + @Override + public World getWorld() { + return Bukkit.getWorlds().get(0); + } + + @Override + public boolean isOnline() { + return true; + } + + @Override + public Location getLocation() { + return new Location(getWorld(), location.getX(), location.getY(), location.getZ(), + location.getYaw(), location.getPitch()); + } + + @Getter + @Setter + private String displayName; + + public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname, + MinecraftChatModule module) { + return Mockito.mock(DiscordConnectedPlayer.class, Mockito.withSettings() + .defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(user, channel, uuid, mcname, module)); + } } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java index 680e886..62949c5 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java @@ -1,5 +1,6 @@ package buttondevteam.discordplugin; +import buttondevteam.discordplugin.playerfaker.VCMDWrapper; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; @@ -36,13 +37,13 @@ import java.util.*; public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer { protected Player player; - private @Getter VanillaCommandListener vanillaCmdListener; + private @Getter VCMDWrapper vanillaCmdListener; public DiscordPlayerSender(User user, MessageChannel channel, Player player) { super(user, channel); this.player = player; try { - vanillaCmdListener = new VanillaCommandListener(this); + vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener(this, player)); } catch (NoClassDefFoundError e) { DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } diff --git a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java b/src/main/java/buttondevteam/discordplugin/IMCPlayer.java index 854db2b..7943f21 100755 --- a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/IMCPlayer.java @@ -1,8 +1,8 @@ package buttondevteam.discordplugin; -import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; +import buttondevteam.discordplugin.playerfaker.VCMDWrapper; import org.bukkit.entity.Player; public interface IMCPlayer> extends Player { - VanillaCommandListener getVanillaCmdListener(); + VCMDWrapper getVanillaCmdListener(); } diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java index 7aafd27..804e79e 100644 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java @@ -15,9 +15,12 @@ public class GeneralEventBroadcasterModule extends Component { PlayerListWatcher.hookUp(); DPUtils.getLogger().info("Finished hooking into the player list"); hooked = true; - } catch (Exception | NoClassDefFoundError e) { + } catch (Exception e) { TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e); + } catch (NoClassDefFoundError e) { + DPUtils.getLogger().warning("Error while hacking the player list! Disable this module if you're on an incompatible version."); } + } @Override @@ -29,8 +32,9 @@ public class GeneralEventBroadcasterModule extends Component { else DPUtils.getLogger().info("Didn't have the player list hooked."); hooked = false; - } catch (Exception | NoClassDefFoundError e) { + } catch (Exception e) { TBMCCoreAPI.SendException("Error while hacking the player list!", e); + } catch (NoClassDefFoundError ignored) { } } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java index 41599b7..bc1cf20 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java @@ -107,7 +107,7 @@ public class ChannelconCommand extends ICommand2DC { return true; } val channel = message.getChannel().block(); - DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module); + DiscordConnectedPlayer dcp = DiscordConnectedPlayer.create(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module); //Using a fake player with no login/logout, should be fine for this event String groupid = chan.get().getGroupID(dcp); if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java index 344ecf5..06c3ce1 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java @@ -26,7 +26,7 @@ public class MCChatPrivate { val op = Bukkit.getOfflinePlayer(mcp.getUUID()); val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class); if (start) { - val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName(), mcm); + val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID(), op.getName(), mcm); MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender); if (p == null)// Player is offline - If the player is online, that takes precedence MCChatUtils.callLoginEvents(sender); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index 92af87d..5f709d2 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -180,7 +180,10 @@ public class MCChatUtils { } public static Consumer> send(String message) { - return ch -> ch.flatMap(mc -> mc.createMessage(DPUtils.sanitizeString(message))).subscribe(); + return ch -> ch.flatMap(mc -> { + resetLastMessage(mc); + return mc.createMessage(DPUtils.sanitizeString(message)); + }).subscribe(); } public static void forAllowedMCChat(Consumer> action, TBMCSystemChatEvent event) { diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index 28e1e72..1534ee9 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -104,7 +104,7 @@ public class MinecraftChatModule extends Component { if (!mcch.isPresent() || ch == null || user == null || groupid == null) continue; Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase) - val dcp = new DiscordConnectedPlayer(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this); + val dcp = DiscordConnectedPlayer.create(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this); MCChatCustom.addCustomChat((MessageChannel) ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet())); }); } diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java new file mode 100644 index 0000000..95a7811 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java @@ -0,0 +1,12 @@ +package buttondevteam.discordplugin.playerfaker; + +import buttondevteam.discordplugin.DiscordSenderBase; +import buttondevteam.discordplugin.IMCPlayer; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class VCMDWrapper> { + @Getter //Needed to mock the player + private final VanillaCommandListener listener; +} diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java index 29f3a13..9f2db22 100755 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java @@ -87,7 +87,7 @@ public class VanillaCommandListener> if (!vcmd.testPermission(sender)) return true; - ICommandListener icommandlistener = sender.getVanillaCmdListener(); + ICommandListener icommandlistener = sender.getVanillaCmdListener().getListener(); String[] args = cmdstr.split(" "); args = Arrays.copyOfRange(args, 1, args.length); try { From 480032a3d6f94550a083e1c5a92bd77cd47d335f Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 8 Aug 2019 13:45:40 +0200 Subject: [PATCH 05/21] Fix of getInfo and role adding/removing Fixed getInfo if the player isn't on the DC server Fixed message after adding or removing a game role --- .../discordplugin/listeners/MCListener.java | 16 ++++++++++------ .../discordplugin/mcchat/MCListener.java | 13 +++++++------ .../discordplugin/role/RoleCommand.java | 7 +++++-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 6062711..6c9acd6 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -12,6 +12,7 @@ import lombok.val; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.server.ServerCommandEvent; +import reactor.core.publisher.Mono; public class MCListener implements Listener { @EventHandler @@ -33,13 +34,16 @@ public class MCListener implements Listener { DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class); if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals("")) return; - User user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block(); - if (user == null) return; + val userOpt = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).onErrorResume(t -> Mono.empty()).blockOptional(); + if (!userOpt.isPresent()) return; + User user = userOpt.get(); e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator()); - Member member = user.asMember(DiscordPlugin.mainServer.getId()).block(); - if (member == null) return; - val pr = member.getPresence().block(); - if (pr == null) return; + val memberOpt = user.asMember(DiscordPlugin.mainServer.getId()).blockOptional(); + if (!memberOpt.isPresent()) return; + Member member = memberOpt.get(); + val prOpt = member.getPresence().blockOptional(); + if (!prOpt.isPresent()) return; + val pr = prOpt.get(); e.addInfo(pr.getStatus().toString()); if (pr.getActivity().isPresent()) { val activity = pr.getActivity().get(); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java index 231bca8..0880bd9 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -27,7 +27,6 @@ import org.bukkit.event.server.TabCompleteEvent; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.Objects; import java.util.Optional; @RequiredArgsConstructor @@ -53,11 +52,13 @@ class MCListener implements Listener { final Player p = e.getPlayer(); DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class); if (dp != null) { - val user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block(); - MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), - new DiscordPlayerSender(user, Objects.requireNonNull(user).getPrivateChannel().block(), p)); //TODO: Don't block - MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), - new DiscordPlayerSender(user, module.chatChannelMono().block(), p)); //Stored per-channel + DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).flatMap(user -> user.getPrivateChannel().flatMap(chan -> module.chatChannelMono().flatMap(cc -> { + MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), + new DiscordPlayerSender(user, chan, p)); + MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(), + new DiscordPlayerSender(user, cc, p)); //Stored per-channel + return Mono.empty(); + }))).subscribe(); } final String message = e.GetPlayer().PlayerName().get() + " joined the game"; MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true); diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java index eb93eb6..d47007b 100755 --- a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java +++ b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java @@ -8,6 +8,7 @@ import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; import discord4j.core.object.entity.Role; import lombok.val; +import reactor.core.publisher.Mono; import java.util.List; import java.util.stream.Collectors; @@ -31,7 +32,8 @@ public class RoleCommand extends ICommand2DC { return true; try { sender.getMessage().getAuthorAsMember() - .subscribe(m -> m.addRole(role.getId()).subscribe(r -> sender.sendMessage("added role."))); + .flatMap(m -> m.addRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("added role.")))) + .subscribe(); } catch (Exception e) { TBMCCoreAPI.SendException("Error while adding role!", e); sender.sendMessage("an error occured while adding the role."); @@ -49,7 +51,8 @@ public class RoleCommand extends ICommand2DC { return true; try { sender.getMessage().getAuthorAsMember() - .subscribe(m -> m.removeRole(role.getId()).subscribe(r -> sender.sendMessage("removed role."))); + .flatMap(m -> m.removeRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("removed role.")))) + .subscribe(); } catch (Exception e) { TBMCCoreAPI.SendException("Error while removing role!", e); sender.sendMessage("an error occured while removing the role."); From 7a9e7de138276c965d9643151663544a1ef6fdc7 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 8 Aug 2019 14:31:01 +0200 Subject: [PATCH 06/21] Made player list, custom and private chat toggleable The last parts of #51 --- .../mcchat/ChannelconCommand.java | 4 +++ .../discordplugin/mcchat/MCChatCommand.java | 8 +++++ .../discordplugin/mcchat/MCChatUtils.java | 3 +- .../mcchat/MinecraftChatModule.java | 29 ++++++++++++++++++- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java index bc1cf20..d51a4b0 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java @@ -90,6 +90,10 @@ public class ChannelconCommand extends ICommand2DC { @Command2.Subcommand public boolean def(Command2DCSender sender, String channelID) { val message = sender.getMessage(); + if (!module.allowCustomChat().get()) { + sender.sendMessage("channel connection is not allowed on this Minecraft server."); + return true; + } if (checkPerms(message)) return true; if (MCChatCustom.hasCustomChat(message.getChannelId())) return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it."); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java index 63b2fb5..f068a12 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java @@ -9,6 +9,7 @@ import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; import discord4j.core.object.entity.PrivateChannel; +import lombok.RequiredArgsConstructor; import lombok.val; @CommandClass(helpText = { @@ -17,10 +18,17 @@ import lombok.val; "It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", // "You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." // }) +@RequiredArgsConstructor public class MCChatCommand extends ICommand2DC { + private final MinecraftChatModule module; + @Command2.Subcommand public boolean def(Command2DCSender sender) { + if (!module.allowPrivateChat().get()) { + sender.sendMessage("using the private chat is not allowed on this Minecraft server."); + return true; + } val message = sender.getMessage(); val channel = message.getChannel().block(); @SuppressWarnings("OptionalGetWithoutIsPresent") val author = message.getAuthor().get(); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index 5f709d2..5e4c51c 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -53,7 +53,8 @@ public class MCChatUtils { private static HashMap, HashSet> staticExcludedPlugins = new HashMap<>(); public static void updatePlayerList() { - if (notEnabled()) return; + val mod = getModule(); + if (mod == null || !mod.showPlayerListOnDC().get()) return; if (lastmsgdata != null) updatePL(lastmsgdata); MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index 1534ee9..a44986a 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -77,6 +77,33 @@ public class MinecraftChatModule extends Component { return getConfig().getData("allowFakePlayerTeleports", false); } + /** + * If this is on, each chat channel will have a player list in their description. + * It only gets added if there's no description yet or there are (at least) two lines of "----" following each other. + * Note that it will replace everything between the first and last "----" but it will only detect exactly four dashes. + * So if you want to use dashes for something else in the description, make sure it's either less or more dashes in one line. + */ + public ConfigData showPlayerListOnDC() { + return getConfig().getData("showPlayerListOnDC", true); + } + + /** + * This setting controls whether custom chat connections can be created (existing connections will always work). + * Custom chat connections can be created using the channelcon command and they allow players to display town chat in a Discord channel for example. + * See the channelcon command for more details. + */ + public ConfigData allowCustomChat() { + return getConfig().getData("allowCustomChat", true); + } + + /** + * This setting allows you to control if players can DM the bot to log on the server from Discord. + * This allows them to both chat and perform any command they can in-game. + */ + public ConfigData allowPrivateChat() { + return getConfig().getData("allowPrivateChat", true); + } + @Override protected void enable() { if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono())) @@ -84,7 +111,7 @@ public class MinecraftChatModule extends Component { listener = new MCChatListener(this); TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled - getPlugin().getManager().registerCommand(new MCChatCommand()); + getPlugin().getManager().registerCommand(new MCChatCommand(this)); getPlugin().getManager().registerCommand(new ChannelconCommand(this)); val chcons = getConfig().getConfig().getConfigurationSection("chcons"); From e88684a5647497b61f93caf349c9201ac82fa194 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 14 Aug 2019 00:53:51 +0200 Subject: [PATCH 07/21] Error handling, 1.14 vanilla command support Error handling (not today) Added support for vanilla commands on 1.14 --- pom.xml | 6 + .../buttondevteam/discordplugin/DPUtils.java | 4 + .../discordplugin/DiscordConnectedPlayer.java | 7 +- .../discordplugin/DiscordPlayerSender.java | 7 +- .../discordplugin/DiscordSender.java | 4 +- .../discordplugin/IMCPlayer.java | 4 +- .../discordplugin/commands/DebugCommand.java | 2 +- .../discordplugin/listeners/MCListener.java | 2 +- .../discordplugin/mcchat/MCChatListener.java | 16 ++- .../discordplugin/mcchat/MCListener.java | 4 +- .../playerfaker/DiscordFakePlayer.java | 5 +- .../playerfaker/VCMDWrapper.java | 31 ++++- .../playerfaker/VanillaCommandListener.java | 2 +- .../playerfaker/VanillaCommandListener14.java | 106 ++++++++++++++++++ 14 files changed, 178 insertions(+), 22 deletions(-) create mode 100644 src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener14.java diff --git a/pom.xml b/pom.xml index 6d68328..c9d1eb8 100755 --- a/pom.xml +++ b/pom.xml @@ -183,6 +183,12 @@ 1.12.2-R0.1-SNAPSHOT provided + + org.spigotmc. + spigot + 1.14.4-R0.1-SNAPSHOT + provided + com.discord4j diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java index 616b750..84d2ced 100755 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.java +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -163,4 +163,8 @@ public final class DPUtils { return getMessageChannel(config.getPath(), config.get()); } + public static Mono ignoreError(Mono mono) { + return mono.onErrorResume(t -> Mono.empty()); + } + } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index da5ccd4..b5d7da4 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -2,7 +2,6 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.mcchat.MinecraftChatModule; import buttondevteam.discordplugin.playerfaker.VCMDWrapper; -import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; import lombok.Getter; @@ -21,7 +20,7 @@ import java.util.HashSet; import java.util.UUID; public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer { - private @Getter VCMDWrapper vanillaCmdListener; + private @Getter VCMDWrapper vanillaCmdListener; @Getter @Setter private boolean loggedIn = false; @@ -56,7 +55,9 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement uniqueId = uuid; displayName = mcname; try { - vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener<>(this)); + vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this)); + if (vanillaCmdListener.getListener() == null) + DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } catch (NoClassDefFoundError e) { DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java index 62949c5..1e94b14 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java @@ -1,7 +1,6 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.playerfaker.VCMDWrapper; -import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; import lombok.Getter; @@ -37,13 +36,15 @@ import java.util.*; public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer { protected Player player; - private @Getter VCMDWrapper vanillaCmdListener; + private @Getter VCMDWrapper vanillaCmdListener; public DiscordPlayerSender(User user, MessageChannel channel, Player player) { super(user, channel); this.player = player; try { - vanillaCmdListener = new VCMDWrapper<>(new VanillaCommandListener(this, player)); + vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, player)); + if (vanillaCmdListener.getListener() == null) + DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } catch (NoClassDefFoundError e) { DPUtils.getLogger().warning("Vanilla commands won't be available from Discord due to a compatibility error."); } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.java b/src/main/java/buttondevteam/discordplugin/DiscordSender.java index 9eda278..0381e66 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordSender.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordSender.java @@ -12,6 +12,7 @@ import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionAttachmentInfo; import org.bukkit.plugin.Plugin; +import reactor.core.publisher.Mono; import java.util.Set; @@ -23,7 +24,8 @@ public class DiscordSender extends DiscordSenderBase implements CommandSender { public DiscordSender(User user, MessageChannel channel) { super(user, channel); val def = "Discord user"; - name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId()).blockOptional().map(Member::getDisplayName).orElse(def); + name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId()) + .onErrorResume(t -> Mono.empty()).blockOptional().map(Member::getDisplayName).orElse(def); } public DiscordSender(User user, MessageChannel channel, String name) { diff --git a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java b/src/main/java/buttondevteam/discordplugin/IMCPlayer.java index 7943f21..c2ee28e 100755 --- a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/IMCPlayer.java @@ -3,6 +3,6 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.playerfaker.VCMDWrapper; import org.bukkit.entity.Player; -public interface IMCPlayer> extends Player { - VCMDWrapper getVanillaCmdListener(); +public interface IMCPlayer extends Player { + VCMDWrapper getVanillaCmdListener(); } diff --git a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java index e1c0686..65e0663 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java @@ -19,7 +19,7 @@ public class DebugCommand extends ICommand2DC { .flatMap(m -> DiscordPlugin.plugin.modRole().get() .map(mr -> m.getRoleIds().stream().anyMatch(r -> r.equals(mr.getId()))) .switchIfEmpty(Mono.fromSupplier(() -> DiscordPlugin.mainServer.getOwnerId().asLong() == m.getId().asLong()))) //Role not found - .subscribe(success -> { + .onErrorReturn(false).subscribe(success -> { if (success) sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled")); else diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 6c9acd6..3afc038 100755 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -38,7 +38,7 @@ public class MCListener implements Listener { if (!userOpt.isPresent()) return; User user = userOpt.get(); e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator()); - val memberOpt = user.asMember(DiscordPlugin.mainServer.getId()).blockOptional(); + val memberOpt = user.asMember(DiscordPlugin.mainServer.getId()).onErrorResume(t -> Mono.empty()).blockOptional(); if (!memberOpt.isPresent()) return; Member member = memberOpt.get(); val prOpt = member.getPresence().blockOptional(); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 790edfb..4ff36cb 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -10,6 +10,7 @@ import buttondevteam.discordplugin.DiscordSenderBase; import buttondevteam.discordplugin.listeners.CommandListener; import buttondevteam.discordplugin.listeners.CommonListeners; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; +import buttondevteam.discordplugin.playerfaker.VanillaCommandListener14; import buttondevteam.discordplugin.util.Timings; import buttondevteam.lib.*; import buttondevteam.lib.chat.ChatMessage; @@ -277,10 +278,11 @@ public class MCChatListener implements Listener { for (User u : event.getMessage().getUserMentions().toIterable()) { //TODO: Role mentions dmessage = dmessage.replace(u.getMention(), "@" + u.getUsername()); // TODO: IG Formatting - val m = u.asMember(DiscordPlugin.mainServer.getId()).block(); - if (m != null) { - final String nick = m.getDisplayName(); - dmessage = dmessage.replace(m.getNicknameMention(), "@" + nick); + val m = u.asMember(DiscordPlugin.mainServer.getId()).onErrorResume(t -> Mono.empty()).blockOptional(); + if (m.isPresent()) { + val mm = m.get(); + final String nick = mm.getDisplayName(); + dmessage = dmessage.replace(mm.getNicknameMention(), "@" + nick); } } for (GuildChannel ch : event.getGuild().flux().flatMap(Guild::getChannels).toIterable()) { @@ -340,7 +342,11 @@ public class MCChatListener implements Listener { channel.set(clmd.mcchannel); //Hack to send command in the channel } //TODO: Permcheck isn't implemented for commands try { - VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); + String mcpackage = Bukkit.getServer().getClass().getPackage().getName(); + if (mcpackage.contains("1_12")) + VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); + else if (mcpackage.contains("1_14")) + VanillaCommandListener14.runBukkitOrVanillaCommand(dsender, cmd); } catch (NoClassDefFoundError e) { Bukkit.dispatchCommand(dsender, cmd); } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java index 0880bd9..c3ad14a 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java @@ -118,7 +118,7 @@ class MCListener implements Listener { final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class) .getAs(DiscordPlayer.class); if (p == null) return; - DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID())) + DPUtils.ignoreError(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID())) .flatMap(user -> user.asMember(DiscordPlugin.mainServer.getId())) .flatMap(user -> role.flatMap(r -> { if (e.getValue()) @@ -131,7 +131,7 @@ class MCListener implements Listener { if (modlog != null) return modlog.flatMap(ch -> ch.createMessage(msg)); return Mono.empty(); - })).subscribe(); + }))).subscribe(); } @EventHandler diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java index 1902516..25b505e 100755 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java @@ -1,7 +1,9 @@ package buttondevteam.discordplugin.playerfaker; +import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.mcchat.MinecraftChatModule; +import discord4j.core.object.entity.Member; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; import lombok.Getter; @@ -143,7 +145,8 @@ public class DiscordFakePlayer extends DiscordHumanEntity implements Player { @Override public String getDisplayName() { - return Objects.requireNonNull(user.asMember(DiscordPlugin.mainServer.getId()).block()).getDisplayName(); + return DPUtils.ignoreError(user.asMember(DiscordPlugin.mainServer.getId())).blockOptional() + .map(Member::getDisplayName).orElse(name); } @Override diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java index 95a7811..4bcf5eb 100644 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java @@ -4,9 +4,36 @@ import buttondevteam.discordplugin.DiscordSenderBase; import buttondevteam.discordplugin.IMCPlayer; import lombok.Getter; import lombok.RequiredArgsConstructor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; @RequiredArgsConstructor -public class VCMDWrapper> { +public class VCMDWrapper { @Getter //Needed to mock the player - private final VanillaCommandListener listener; + private final Object listener; + + /** + * This constructor will only send raw vanilla messages to the sender in plain text. + * + * @param player The Discord sender player (the wrapper) + */ + public static > Object createListener(T player) { + return createListener(player, null); + } + + /** + * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. + * + * @param player The Discord sender player (the wrapper) + * @param bukkitplayer The Bukkit player to send the raw message to + */ + public static > Object createListener(T player, Player bukkitplayer) { + String mcpackage = Bukkit.getServer().getClass().getPackage().getName(); + if (mcpackage.contains("1_12")) + return bukkitplayer == null ? new VanillaCommandListener<>(player) : new VanillaCommandListener<>(player, bukkitplayer); + else if (mcpackage.contains("1_14")) + return bukkitplayer == null ? new VanillaCommandListener14<>(player) : new VanillaCommandListener14<>(player, bukkitplayer); + else + return null; + } } diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java index 9f2db22..c12a308 100755 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener.java @@ -87,7 +87,7 @@ public class VanillaCommandListener> if (!vcmd.testPermission(sender)) return true; - ICommandListener icommandlistener = sender.getVanillaCmdListener().getListener(); + ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener(); String[] args = cmdstr.split(" "); args = Arrays.copyOfRange(args, 1, args.length); try { diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener14.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener14.java new file mode 100644 index 0000000..fbaf958 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VanillaCommandListener14.java @@ -0,0 +1,106 @@ +package buttondevteam.discordplugin.playerfaker; + +import buttondevteam.discordplugin.DiscordSenderBase; +import buttondevteam.discordplugin.IMCPlayer; +import lombok.Getter; +import lombok.val; +import net.minecraft.server.v1_14_R1.*; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.v1_14_R1.CraftServer; +import org.bukkit.craftbukkit.v1_14_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_14_R1.command.ProxiedNativeCommandSender; +import org.bukkit.craftbukkit.v1_14_R1.command.VanillaCommandWrapper; +import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; + +import java.util.Arrays; + +public class VanillaCommandListener14> implements ICommandListener { + private @Getter T player; + private Player bukkitplayer; + + /** + * This constructor will only send raw vanilla messages to the sender in plain text. + * + * @param player The Discord sender player (the wrapper) + */ + public VanillaCommandListener14(T player) { + this.player = player; + this.bukkitplayer = null; + } + + /** + * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. + * + * @param player The Discord sender player (the wrapper) + * @param bukkitplayer The Bukkit player to send the raw message to + */ + public VanillaCommandListener14(T player, Player bukkitplayer) { + this.player = player; + this.bukkitplayer = bukkitplayer; + if (!(bukkitplayer instanceof CraftPlayer)) + throw new ClassCastException("bukkitplayer must be a Bukkit player!"); + } + + @Override + public void sendMessage(IChatBaseComponent arg0) { + player.sendMessage(arg0.getString()); + if (bukkitplayer != null) + ((CraftPlayer) bukkitplayer).getHandle().sendMessage(arg0); + } + + @Override + public boolean shouldSendSuccess() { + return true; + } + + @Override + public boolean shouldSendFailure() { + return true; + } + + @Override + public boolean shouldBroadcastCommands() { + return true; //Broadcast to in-game admins + } + + @Override + public CommandSender getBukkitSender(CommandListenerWrapper commandListenerWrapper) { + return player; + } + + public static boolean runBukkitOrVanillaCommand(DiscordSenderBase dsender, String cmdstr) { + val cmd = ((CraftServer) Bukkit.getServer()).getCommandMap().getCommand(cmdstr.split(" ")[0].toLowerCase()); + if (!(dsender instanceof Player) || !(cmd instanceof VanillaCommandWrapper)) + return Bukkit.dispatchCommand(dsender, cmdstr); // Unconnected users are treated well in vanilla cmds + + if (!(dsender instanceof IMCPlayer)) + throw new ClassCastException( + "dsender needs to implement IMCPlayer to use vanilla commands as it implements Player."); + + IMCPlayer sender = (IMCPlayer) dsender; // Don't use val on recursive interfaces :P + + val vcmd = (VanillaCommandWrapper) cmd; + if (!vcmd.testPermission(sender)) + return true; + + val world = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle(); + ICommandListener icommandlistener = (ICommandListener) sender.getVanillaCmdListener().getListener(); + 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); + val pncs = new ProxiedNativeCommandSender(wrapper, sender, sender); + String[] args = cmdstr.split(" "); + args = Arrays.copyOfRange(args, 1, args.length); + try { + return vcmd.execute(pncs, cmd.getLabel(), args); + } catch (CommandException commandexception) { + // Taken from CommandHandler + ChatMessage chatmessage = new ChatMessage(commandexception.getMessage(), commandexception.a()); + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); + icommandlistener.sendMessage(chatmessage); + } + return true; + } +} From 42e91409c7d4829627de91d14aecf5eb2bbd3558 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 14 Aug 2019 21:29:19 +0200 Subject: [PATCH 08/21] Work on 1.14 PLW support --- .../broadcaster/PlayerListWatcher.java | 335 +++--------------- 1 file changed, 41 insertions(+), 294 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index eafe7f5..c0b39c5 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -1,26 +1,20 @@ package buttondevteam.discordplugin.broadcaster; -import buttondevteam.discordplugin.mcchat.MCChatUtils; -import buttondevteam.lib.TBMCCoreAPI; -import com.mojang.authlib.GameProfile; import lombok.val; -import net.minecraft.server.v1_12_R1.*; import org.bukkit.Bukkit; -import org.bukkit.Location; import org.bukkit.craftbukkit.v1_12_R1.CraftServer; -import org.bukkit.craftbukkit.v1_12_R1.util.CraftChatMessage; -import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; -import org.objenesis.ObjenesisStd; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.List; -import java.util.UUID; -public class PlayerListWatcher extends DedicatedPlayerList { - private DedicatedPlayerList plist; +public class PlayerListWatcher { + private static Object plist; + private static Object mock; - public PlayerListWatcher(DedicatedServer minecraftserver) { + /*public PlayerListWatcher(DedicatedServer minecraftserver) { super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis } @@ -38,7 +32,7 @@ public class PlayerListWatcher extends DedicatedPlayerList { } @Override - public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overriden method + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overridden method plist.getServer().sendMessage(ichatbasecomponent); ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT; @@ -57,29 +51,50 @@ public class PlayerListWatcher extends DedicatedPlayerList { for (IChatBaseComponent component : iChatBaseComponents) { sendMessage(component, true); } - } + }*/ static void hookUp() throws Exception { - Field conf = CraftServer.class.getDeclaredField("console"); + val csc = Bukkit.getServer().getClass(); + Field conf = csc.getDeclaredField("console"); conf.setAccessible(true); - val server = (MinecraftServer) conf.get(Bukkit.getServer()); - val plw = new ObjenesisStd().newInstance(PlayerListWatcher.class); // Cannot call super constructor - plw.plist = (DedicatedPlayerList) server.getPlayerList(); - plw.maxPlayers = plw.plist.getMaxPlayers(); - Field plf = plw.getClass().getField("players"); + val server = conf.get(Bukkit.getServer()); + val nms = server.getClass().getPackage().getName(); + val dplc = Class.forName(nms + ".DedicatedPlayerList"); + mock = Mockito.mock(dplc, new Answer() { // Cannot call super constructor + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return invocation.getMethod().invoke(plist, invocation.getArguments()); + } + }); + plist = server.getClass().getMethod("getPlayerList").invoke(server); + try { + Field mpf = mock.getClass().getField("maxPlayers"); + mpf.setAccessible(true); + Field modf = mpf.getClass().getDeclaredField("modifiers"); + modf.setAccessible(true); + modf.set(mpf, mpf.getModifiers() & ~Modifier.FINAL); + mpf.set(mock, mpf.get(plist)); + } catch (NoSuchFieldException ignored) { + //The field no longer exists on 1.14 + } + Field plf = mock.getClass().getField("players"); plf.setAccessible(true); Field modf = plf.getClass().getDeclaredField("modifiers"); modf.setAccessible(true); modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); - plf.set(plw, plw.plist.players); - server.a(plw); + plf.set(mock, plf.get(plist)); + try { + server.getClass().getMethod("a", dplc).invoke(server, mock); + } catch (NoSuchMethodException e) { + server.getClass().getMethod("a", Class.forName(server.getClass().getPackage().getName() + ".PlayerList")).invoke(server, mock); + } Field pllf = CraftServer.class.getDeclaredField("playerList"); pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), plw); + pllf.set(Bukkit.getServer(), mock); } static boolean hookDown() throws Exception { - Field conf = CraftServer.class.getDeclaredField("console"); + /*Field conf = CraftServer.class.getDeclaredField("console"); conf.setAccessible(true); val server = (MinecraftServer) conf.get(Bukkit.getServer()); val plist = (DedicatedPlayerList) server.getPlayerList(); @@ -88,275 +103,7 @@ public class PlayerListWatcher extends DedicatedPlayerList { server.a(((PlayerListWatcher) plist).plist); Field pllf = CraftServer.class.getDeclaredField("playerList"); pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist); + pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist);*/ return true; } - - public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { - plist.a(entityhuman, ichatbasecomponent); - } - - public void a(EntityPlayer entityplayer, int i) { - plist.a(entityplayer, i); - } - - public void a(EntityPlayer entityplayer, WorldServer worldserver) { - plist.a(entityplayer, worldserver); - } - - public NBTTagCompound a(EntityPlayer entityplayer) { - return plist.a(entityplayer); - } - - public void a(int i) { - plist.a(i); - } - - public void a(NetworkManager networkmanager, EntityPlayer entityplayer) { - plist.a(networkmanager, entityplayer); - } - - public void a(Packet packet, int i) { - plist.a(packet, i); - } - - public EntityPlayer a(UUID uuid) { - return plist.a(uuid); - } - - public void addOp(GameProfile gameprofile) { - plist.addOp(gameprofile); - } - - public void addWhitelist(GameProfile gameprofile) { - plist.addWhitelist(gameprofile); - } - - public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) { - return plist.attemptLogin(loginlistener, gameprofile, hostname); - } - - public String b(boolean flag) { - return plist.b(flag); - } - - public void b(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { - plist.b(entityhuman, ichatbasecomponent); - } - - public void b(EntityPlayer entityplayer, WorldServer worldserver) { - plist.b(entityplayer, worldserver); - } - - public List b(String s) { - return plist.b(s); - } - - public Location calculateTarget(Location enter, World target) { - return plist.calculateTarget(enter, target); - } - - public void changeDimension(EntityPlayer entityplayer, int i, TeleportCause cause) { - plist.changeDimension(entityplayer, i, cause); - } - - public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) { - plist.changeWorld(entity, i, worldserver, worldserver1); - } - - public int d() { - return plist.d(); - } - - public void d(EntityPlayer entityplayer) { - plist.d(entityplayer); - } - - public String disconnect(EntityPlayer entityplayer) { - return plist.disconnect(entityplayer); - } - - public boolean equals(Object obj) { - return plist.equals(obj); - } - - public String[] f() { - return plist.f(); - } - - public void f(EntityPlayer entityplayer) { - plist.f(entityplayer); - } - - public boolean f(GameProfile gameprofile) { - return plist.f(gameprofile); - } - - public GameProfile[] g() { - return plist.g(); - } - - public boolean getHasWhitelist() { - return plist.getHasWhitelist(); - } - - public IpBanList getIPBans() { - return plist.getIPBans(); - } - - public int getMaxPlayers() { - return plist.getMaxPlayers(); - } - - public OpList getOPs() { - return plist.getOPs(); - } - - public EntityPlayer getPlayer(String s) { - return plist.getPlayer(s); - } - - public int getPlayerCount() { - return plist.getPlayerCount(); - } - - public GameProfileBanList getProfileBans() { - return plist.getProfileBans(); - } - - public String[] getSeenPlayers() { - return plist.getSeenPlayers(); - } - - public DedicatedServer getServer() { - return plist.getServer(); - } - - public WhiteList getWhitelist() { - return plist.getWhitelist(); - } - - public String[] getWhitelisted() { - return plist.getWhitelisted(); - } - - public AdvancementDataPlayer h(EntityPlayer entityplayer) { - return plist.h(entityplayer); - } - - public int hashCode() { - return plist.hashCode(); - } - - public boolean isOp(GameProfile gameprofile) { - return plist.isOp(gameprofile); - } - - public boolean isWhitelisted(GameProfile gameprofile) { - return plist.isWhitelisted(gameprofile); - } - - public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag, Location location, - boolean avoidSuffocation) { - return plist.moveToWorld(entityplayer, i, flag, location, avoidSuffocation); - } - - public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag) { - return plist.moveToWorld(entityplayer, i, flag); - } - - public String[] n() { - return plist.n(); - } - - public void onPlayerJoin(EntityPlayer entityplayer, String joinMessage) { - plist.onPlayerJoin(entityplayer, joinMessage); - } - - public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) { - return plist.processLogin(gameprofile, player); - } - - public void reload() { - plist.reload(); - } - - public void reloadWhitelist() { - plist.reloadWhitelist(); - } - - public void removeOp(GameProfile gameprofile) { - plist.removeOp(gameprofile); - } - - public void removeWhitelist(GameProfile gameprofile) { - plist.removeWhitelist(gameprofile); - } - - public void repositionEntity(Entity entity, Location exit, boolean portal) { - plist.repositionEntity(entity, exit, portal); - } - - public int s() { - return plist.s(); - } - - public void savePlayers() { - plist.savePlayers(); - } - - @SuppressWarnings("rawtypes") - public void sendAll(Packet packet, EntityHuman entityhuman) { - plist.sendAll(packet, entityhuman); - } - - @SuppressWarnings("rawtypes") - public void sendAll(Packet packet, World world) { - plist.sendAll(packet, world); - } - - public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, - Packet packet) { - plist.sendPacketNearby(entityhuman, d0, d1, d2, d3, i, packet); - } - - public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) { - plist.sendScoreboard(scoreboardserver, entityplayer); - } - - public void setHasWhitelist(boolean flag) { - plist.setHasWhitelist(flag); - } - - public void setPlayerFileData(WorldServer[] aworldserver) { - plist.setPlayerFileData(aworldserver); - } - - public NBTTagCompound t() { - return plist.t(); - } - - public void tick() { - plist.tick(); - } - - public String toString() { - return plist.toString(); - } - - public void u() { - plist.u(); - } - - public void updateClient(EntityPlayer entityplayer) { - plist.updateClient(entityplayer); - } - - public List v() { - return plist.v(); - } - - public ServerStatisticManager getStatisticManager(EntityPlayer entityhuman) { - return plist.getStatisticManager(entityhuman); - } } From c456b2433360776f0ae9849f47618156e4f24b5f Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 23 Aug 2019 01:58:47 +0200 Subject: [PATCH 09/21] Command handling improvement, PLW work More correct fallback for command handling Getting closer to finish PlayerListWatcher --- .../GeneralEventBroadcasterModule.java | 4 +- .../broadcaster/PlayerListWatcher.java | 106 ++++++++++-------- .../discordplugin/mcchat/MCChatListener.java | 6 +- 3 files changed, 68 insertions(+), 48 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java index 804e79e..44c2d79 100644 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java @@ -12,7 +12,7 @@ public class GeneralEventBroadcasterModule extends Component { @Override protected void enable() { try { - PlayerListWatcher.hookUp(); + PlayerListWatcher.hookUpDown(true); DPUtils.getLogger().info("Finished hooking into the player list"); hooked = true; } catch (Exception e) { @@ -27,7 +27,7 @@ public class GeneralEventBroadcasterModule extends Component { protected void disable() { try { if (!hooked) return; - if (PlayerListWatcher.hookDown()) + if (PlayerListWatcher.hookUpDown(false)) DPUtils.getLogger().info("Finished unhooking the player list!"); else DPUtils.getLogger().info("Didn't have the player list hooked."); diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index c0b39c5..e2b795f 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -1,8 +1,8 @@ package buttondevteam.discordplugin.broadcaster; +import buttondevteam.lib.TBMCCoreAPI; import lombok.val; import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_12_R1.CraftServer; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; @@ -53,57 +53,75 @@ public class PlayerListWatcher { } }*/ - static void hookUp() throws Exception { + static boolean hookUpDown(boolean up) throws Exception { val csc = Bukkit.getServer().getClass(); Field conf = csc.getDeclaredField("console"); conf.setAccessible(true); val server = conf.get(Bukkit.getServer()); val nms = server.getClass().getPackage().getName(); val dplc = Class.forName(nms + ".DedicatedPlayerList"); - mock = Mockito.mock(dplc, new Answer() { // Cannot call super constructor - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - return invocation.getMethod().invoke(plist, invocation.getArguments()); - } - }); - plist = server.getClass().getMethod("getPlayerList").invoke(server); - try { - Field mpf = mock.getClass().getField("maxPlayers"); - mpf.setAccessible(true); - Field modf = mpf.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(mpf, mpf.getModifiers() & ~Modifier.FINAL); - mpf.set(mock, mpf.get(plist)); - } catch (NoSuchFieldException ignored) { - //The field no longer exists on 1.14 - } - Field plf = mock.getClass().getField("players"); - plf.setAccessible(true); - Field modf = plf.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); - plf.set(mock, plf.get(plist)); - try { - server.getClass().getMethod("a", dplc).invoke(server, mock); - } catch (NoSuchMethodException e) { - server.getClass().getMethod("a", Class.forName(server.getClass().getPackage().getName() + ".PlayerList")).invoke(server, mock); - } - Field pllf = CraftServer.class.getDeclaredField("playerList"); - pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), mock); - } + val currentPL = server.getClass().getMethod("getPlayerList").invoke(server); + if (up) { + mock = Mockito.mock(dplc, new Answer() { // Cannot call super constructor + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + if (!invocation.getMethod().getName().equals("sendMessage")) + return invocation.getMethod().invoke(plist, invocation.getArguments()); + val args = invocation.getArguments(); + val params = invocation.getMethod().getParameterTypes(); + if (params.length == 0) { + TBMCCoreAPI.SendException("Found a strange method", + new Exception("Found a sendMessage() method without arguments.")); + return null; + } + if (params[0].getSimpleName().equals("IChatBaseComponent[]")) + for (val arg : (Object[]) args[0]) + sendMessage(arg, true); + else if (params[0].getSimpleName().equals("IChatBaseComponent")) + if (params.length > 1 && params[1].getSimpleName().equalsIgnoreCase("boolean")) + sendMessage(args[0], (Boolean) args[1]); + else + sendMessage(args[0], true); + else + TBMCCoreAPI.SendException("Found a method with interesting params", + new Exception("Found a sendMessage(" + params[0].getSimpleName() + ") method")); + return null; + } - static boolean hookDown() throws Exception { - /*Field conf = CraftServer.class.getDeclaredField("console"); - conf.setAccessible(true); - val server = (MinecraftServer) conf.get(Bukkit.getServer()); - val plist = (DedicatedPlayerList) server.getPlayerList(); - if (!(plist instanceof PlayerListWatcher)) - return false; - server.a(((PlayerListWatcher) plist).plist); - Field pllf = CraftServer.class.getDeclaredField("playerList"); + private void sendMessage(Object chatComponent, boolean system) { + //TODO + } + }); + plist = currentPL; + try { + Field mpf = mock.getClass().getField("maxPlayers"); + mpf.setAccessible(true); + Field modf = mpf.getClass().getDeclaredField("modifiers"); + modf.setAccessible(true); + modf.set(mpf, mpf.getModifiers() & ~Modifier.FINAL); + mpf.set(mock, mpf.get(plist)); + } catch (NoSuchFieldException ignored) { + //The field no longer exists on 1.14 + } + Field plf = mock.getClass().getField("players"); + plf.setAccessible(true); + Field modf = plf.getClass().getDeclaredField("modifiers"); + modf.setAccessible(true); + modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); + plf.set(mock, plf.get(plist)); + } else { + if (!(mock instanceof PlayerListWatcher)) + return false; + } + try { + server.getClass().getMethod("a", dplc).invoke(server, up ? mock : plist); + } catch (NoSuchMethodException e) { + server.getClass().getMethod("a", Class.forName(server.getClass().getPackage().getName() + ".PlayerList")) + .invoke(server, up ? mock : plist); + } + Field pllf = csc.getDeclaredField("playerList"); pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist);*/ + pllf.set(Bukkit.getServer(), up ? mock : plist); return true; } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 4ff36cb..f1f81d6 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -347,10 +347,12 @@ public class MCChatListener implements Listener { VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); else if (mcpackage.contains("1_14")) VanillaCommandListener14.runBukkitOrVanillaCommand(dsender, cmd); + else + Bukkit.dispatchCommand(dsender, cmd); } catch (NoClassDefFoundError e) { - Bukkit.dispatchCommand(dsender, cmd); + TBMCCoreAPI.SendException("A class is not found when trying to run command " + cmd + "!", e); } - Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased); + Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmd); if (clmd != null) channel.set(chtmp); }); From 2963990b5e62fd50ac7e45f6f3baa7d03b582162 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Fri, 23 Aug 2019 21:52:52 +0200 Subject: [PATCH 10/21] Check if Minecraft is a game role --- .../discordplugin/role/GameRoleModule.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java index 0ab0cac..3c6e775 100644 --- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java +++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.role; import buttondevteam.core.ComponentManager; import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlugin; +import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.architecture.Component; import buttondevteam.lib.architecture.ReadOnlyConfigData; import discord4j.core.event.domain.role.RoleCreateEvent; @@ -89,11 +90,24 @@ public class GameRoleModule extends Component { } private Mono isGameRole(Role r) { - if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong()) + boolean debug = r.getName().equalsIgnoreCase("Minecraft"); + if (debug) TBMCCoreAPI.sendDebugMessage("Checking if Minecraft is a game role..."); + if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong()) { + if (debug) TBMCCoreAPI.sendDebugMessage("Not in the main server: " + r.getGuildId().asString()); return Mono.just(false); //Only allow on the main server + } val rc = new Color(149, 165, 166, 0); - return Mono.just(r.getColor().equals(rc)).filter(b -> b).flatMap(b -> - DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId())).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles - .defaultIfEmpty(false); + if (debug) TBMCCoreAPI.sendDebugMessage("Game role color: " + rc + " - MC color: " + r.getColor()); + return Mono.just(r.getColor().equals(rc)) + .doAfterSuccessOrError((b, e) -> { + if (debug) TBMCCoreAPI.sendDebugMessage("1. b: " + b + " - e: " + e); + }).filter(b -> b).flatMap(b -> + DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId())) + .doAfterSuccessOrError((m, e) -> { + if (debug) TBMCCoreAPI.sendDebugMessage("2. m: " + m.getDisplayName() + " e: " + e); + }).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles + .doAfterSuccessOrError((b, e) -> { + if (debug) TBMCCoreAPI.sendDebugMessage("3. b: " + b + " - e: " + e); + }).defaultIfEmpty(false); } } From 91f1c8c4f75673580b91b389ccb485dc58870fd8 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 25 Aug 2019 03:23:14 +0200 Subject: [PATCH 11/21] Probably fixed PLW Mostly yesterday --- .../broadcaster/PlayerListWatcher.java | 57 +++++++++++++++++-- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index e2b795f..e1fbbe2 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -1,5 +1,6 @@ package buttondevteam.discordplugin.broadcaster; +import buttondevteam.discordplugin.mcchat.MCChatUtils; import buttondevteam.lib.TBMCCoreAPI; import lombok.val; import org.bukkit.Bukkit; @@ -8,6 +9,7 @@ import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class PlayerListWatcher { @@ -62,13 +64,38 @@ public class PlayerListWatcher { val dplc = Class.forName(nms + ".DedicatedPlayerList"); val currentPL = server.getClass().getMethod("getPlayerList").invoke(server); if (up) { + val icbcl = Class.forName(nms + ".IChatBaseComponent"); + val sendMessage = server.getClass().getMethod("sendMessage", icbcl); + val cmtcl = Class.forName(nms + ".ChatMessageType"); + val systemType = cmtcl.getDeclaredField("SYSTEM").get(null); + val chatType = cmtcl.getDeclaredField("CHAT").get(null); + + val obc = csc.getPackage().getName(); + val ccmcl = Class.forName(obc + ".util.CraftChatMessage"); + val fixComponent = ccmcl.getMethod("fixComponent", icbcl); + val ppoc = Class.forName(nms + ".PacketPlayOutChat"); + val ppocC = Class.forName(nms + ".PacketPlayOutChat").getConstructor(icbcl, cmtcl); + val sendAll = dplc.getMethod("sendAll", Class.forName(nms + ".Packet")); + Method tpt; + try { + tpt = icbcl.getMethod("toPlainText"); + } catch (NoSuchMethodException e) { + tpt = icbcl.getMethod("getString"); + } + val toPlainText = tpt; mock = Mockito.mock(dplc, new Answer() { // Cannot call super constructor @Override public Object answer(InvocationOnMock invocation) throws Throwable { - if (!invocation.getMethod().getName().equals("sendMessage")) - return invocation.getMethod().invoke(plist, invocation.getArguments()); + final Method method = invocation.getMethod(); + if (!method.getName().equals("sendMessage")) { + if (method.getName().equals("sendAll")) { + sendAll(invocation.getArgument(0)); + return null; + } + return method.invoke(plist, invocation.getArguments()); + } val args = invocation.getArguments(); - val params = invocation.getMethod().getParameterTypes(); + val params = method.getParameterTypes(); if (params.length == 0) { TBMCCoreAPI.SendException("Found a strange method", new Exception("Found a sendMessage() method without arguments.")); @@ -89,7 +116,29 @@ public class PlayerListWatcher { } private void sendMessage(Object chatComponent, boolean system) { - //TODO + try { //Converted to use reflection + sendMessage.invoke(server, chatComponent); + Object chatmessagetype = system ? systemType : chatType; + + // CraftBukkit start - we run this through our processor first so we can get web links etc + this.sendAll(ppocC.newInstance(fixComponent.invoke(null, chatComponent), chatmessagetype)); + // CraftBukkit end + } catch (Exception e) { + TBMCCoreAPI.SendException("An error occurred while passing a vanilla message through the player list", e); + } + } + + private void sendAll(Object packet) { + try { // Some messages get sent by directly constructing a packet + sendAll.invoke(plist, packet); + if (packet.getClass() == ppoc) { + Field msgf = ppoc.getDeclaredField("a"); + msgf.setAccessible(true); + MCChatUtils.forAllMCChat(MCChatUtils.send((String) toPlainText.invoke(msgf.get(packet)))); + } + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e); + } } }); plist = currentPL; From e4d5dcd0a2ad242669a80fd35eb52c29b6553936 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 26 Aug 2019 00:57:16 +0200 Subject: [PATCH 12/21] Update D4J & role debug refined & fixed a ) PLW tested and confirmed working Moved a ) to fix #105 --- pom.xml | 2 +- .../discordplugin/mcchat/MCChatListener.java | 4 ++-- .../discordplugin/role/GameRoleModule.java | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index c9d1eb8..1b74b55 100755 --- a/pom.xml +++ b/pom.xml @@ -193,7 +193,7 @@ com.discord4j discord4j-core - 3.0.6 + 3.0.8 diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index f1f81d6..2314994 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -233,8 +233,8 @@ public class MCChatListener implements Listener { timings.printElapsed("Filter 1"); return !(ev.getMessage().getChannelId().asLong() != module.chatChannel().get().asLong() && !(channel instanceof PrivateChannel - && author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false) - && !hasCustomChat)); //Chat isn't enabled on this channel + && author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false)) + && !hasCustomChat); //Chat isn't enabled on this channel }).filter(channel -> { timings.printElapsed("Filter 2"); return !(channel instanceof PrivateChannel //Only in private chat diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java index 3c6e775..babb47c 100644 --- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java +++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java @@ -27,7 +27,7 @@ public class GameRoleModule extends Component { @Override protected void enable() { getPlugin().getManager().registerCommand(new RoleCommand(this)); - GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(this::isGameRole).map(Role::getName).collect(Collectors.toList()).block(); + GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(r -> isGameRole(r, false)).map(Role::getName).collect(Collectors.toList()).block(); } @Override @@ -47,7 +47,7 @@ public class GameRoleModule extends Component { if (roleEvent instanceof RoleCreateEvent) { Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { Role role=((RoleCreateEvent) roleEvent).getRole(); - grm.isGameRole(role).flatMap(b -> { + grm.isGameRole(role, false).flatMap(b -> { if (!b) return Mono.empty(); //Deleted or not a game role GameRoles.add(role.getName()); @@ -68,7 +68,7 @@ public class GameRoleModule extends Component { return; } Role or=event.getOld().get(); - grm.isGameRole(event.getCurrent()).flatMap(b -> { + grm.isGameRole(event.getCurrent(), true).flatMap(b -> { if (!b) { if (GameRoles.remove(or.getName()) && logChannel != null) return logChannel.flatMap(ch -> ch.createMessage("Removed " + or.getName() + " as a game role because it's color changed.")); @@ -89,8 +89,8 @@ public class GameRoleModule extends Component { } } - private Mono isGameRole(Role r) { - boolean debug = r.getName().equalsIgnoreCase("Minecraft"); + private Mono isGameRole(Role r, boolean debugMC) { + boolean debug = debugMC && r.getName().equalsIgnoreCase("Minecraft"); if (debug) TBMCCoreAPI.sendDebugMessage("Checking if Minecraft is a game role..."); if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong()) { if (debug) TBMCCoreAPI.sendDebugMessage("Not in the main server: " + r.getGuildId().asString()); From ea0ef88068300f57d4b1d57331cd40ad098db24d Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 26 Aug 2019 22:26:10 +0200 Subject: [PATCH 13/21] Automatically get client ID for link, improvements Literally 2 improvements --- src/main/java/buttondevteam/discordplugin/DiscordPlugin.java | 5 ++++- .../discordplugin/mcchat/ChannelconCommand.java | 4 ++-- .../discordplugin/mcchat/MinecraftChatModule.java | 3 +++ src/main/resources/plugin.yml | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index b5a0efc..ab63ebe 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -142,9 +142,13 @@ public class DiscordPlugin extends ButtonPlugin { private void handleReady(List event) { try { mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards + getCommand2MC().registerCommand(new DiscordMCCommand()); //Register so that the reset command works if (mainServer == null) { if (event.size() == 0) { getLogger().severe("Main server not found! Invite the bot and do /discord reset"); + dc.getApplicationInfo().subscribe(info -> { + getLogger().severe("Click here: https://discordapp.com/oauth2/authorize?client_id=" + info.getId().asString() + "&scope=bot&permissions=268509264"); + }); saveConfig(); //Put default there return; //We should have all guilds by now, no need to retry } @@ -200,7 +204,6 @@ public class DiscordPlugin extends ButtonPlugin { CommonListeners.register(dc.getEventDispatcher()); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); - getCommand2MC().registerCommand(new DiscordMCCommand()); TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase ? ((DiscordSenderBase) sender).getChromaUser() : null)); diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java index d51a4b0..be1d284 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java @@ -31,7 +31,7 @@ import java.util.stream.Collectors; "Use the ID (command) of the channel, for example `g` for the global chat.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", // "Mentioning the bot is needed in this case because the / prefix only works in #bot.", // - "Invite link: " // + "Invite link: " // }) @RequiredArgsConstructor public class ChannelconCommand extends ICommand2DC { @@ -155,7 +155,7 @@ public class ChannelconCommand extends ICommand2DC { "Use the ID (command) of the channel, for example `g` for the global chat.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", // "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", // - "Invite link: " // TODO: Set correct client ID + "Invite link: " }; } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java index a44986a..b4586e1 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java @@ -104,10 +104,13 @@ public class MinecraftChatModule extends Component { return getConfig().getData("allowPrivateChat", true); } + String clientID; + @Override protected void enable() { if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono())) return; + DiscordPlugin.dc.getApplicationInfo().subscribe(info -> clientID = info.getId().asString()); listener = new MCChatListener(this); TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin()); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6507c5b..e045c2b 100755 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,8 +1,8 @@ -name: Thorpe-Discord +name: Chroma-Discord main: buttondevteam.discordplugin.DiscordPlugin version: 1.0 author: NorbiPeti -depend: [ThorpeCore] +depend: [ChromaCore] commands: discord: website: 'https://github.com/TBMCPlugins/DiscordPlugin' From acd45ce4aebe5d6a33299bf2ac917a1fbf24ad33 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 11 Sep 2019 15:30:07 +0200 Subject: [PATCH 14/21] Hopefully fixed the issue of multiple ready events #107 --- .../java/buttondevteam/discordplugin/DiscordPlugin.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index ab63ebe..38afecc 100755 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -141,6 +141,10 @@ public class DiscordPlugin extends ButtonPlugin { private void handleReady(List event) { try { + if(!SafeMode) { + getLogger().info("Ready again, already enabled."); //TODO: It should probably handle disconnections + return; + } mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards getCommand2MC().registerCommand(new DiscordMCCommand()); //Register so that the reset command works if (mainServer == null) { @@ -264,8 +268,8 @@ public class DiscordPlugin extends ButtonPlugin { try { SafeMode = true; // Stop interacting with Discord ChromaBot.delete(); - timings.printElapsed("Updating presence..."); - dc.updatePresence(Presence.idle(Activity.playing("Chromacraft"))).block(); //No longer using the same account for testing + //timings.printElapsed("Updating presence..."); + //dc.updatePresence(Presence.idle(Activity.playing("logging out"))).block(); //No longer using the same account for testing timings.printElapsed("Logging out..."); dc.logout().block(); //Configs are emptied so channels and servers are fetched again From ef44aa883064e4270c1322b8a207a98abcb5d78d Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 11 Sep 2019 15:33:02 +0200 Subject: [PATCH 15/21] Fix build (Java 8) --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 808d9b0..1d962ae 100755 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ language: java jdk: - oraclejdk8 sudo: true +dist: trusty # Needed for Java 8, although we might not need Java 8 deploy: # deploy develop to the staging environment - provider: script From 3bd7b879c4bac3dbb35641d99571aabf559e1ade Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 15 Sep 2019 03:33:30 +0200 Subject: [PATCH 16/21] Hopefully fixed the #106 issue Also removed mock check a while ago --- .../discordplugin/DiscordConnectedPlayer.java | 11 +++++++++++ .../discordplugin/broadcaster/PlayerListWatcher.java | 3 --- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index b5d7da4..33fae6b 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -9,6 +9,7 @@ import lombok.Setter; import lombok.experimental.Delegate; import org.bukkit.*; import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.permissions.PermissibleBase; @@ -144,6 +145,16 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement location.getYaw(), location.getPitch()); } + @Override + public double getMaxHealth() { + return 20; + } + + @Override + public Player getPlayer() { + return this; + } + @Getter @Setter private String displayName; diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index e1fbbe2..6bfb220 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -158,9 +158,6 @@ public class PlayerListWatcher { modf.setAccessible(true); modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); plf.set(mock, plf.get(plist)); - } else { - if (!(mock instanceof PlayerListWatcher)) - return false; } try { server.getClass().getMethod("a", dplc).invoke(server, up ? mock : plist); From f1cec2ced1f954390102ba756e599fdeaeb68333 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 20 Oct 2019 20:50:29 +0200 Subject: [PATCH 17/21] Update to Java 10/11 & fake player remove Apparently --- pom.xml | 10 +- .../discordplugin/mcchat/MCChatListener.java | 2 +- .../discordplugin/mcchat/MCChatUtils.java | 1 - .../playerfaker/DiscordEntity.java | 308 -------- .../playerfaker/DiscordFakePlayer.java | 746 ------------------ .../playerfaker/DiscordHumanEntity.java | 168 ---- .../playerfaker/DiscordInventory.java | 212 ----- .../playerfaker/DiscordLivingEntity.java | 288 ------- .../playerfaker/DiscordPlayerInventory.java | 105 --- .../playerfaker/perm/LPInjector.java | 14 +- 10 files changed, 13 insertions(+), 1841 deletions(-) delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordEntity.java delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordHumanEntity.java delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordInventory.java delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java delete mode 100755 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordPlayerInventory.java diff --git a/pom.xml b/pom.xml index 1b74b55..20b2069 100755 --- a/pom.xml +++ b/pom.xml @@ -38,8 +38,8 @@ maven-compiler-plugin 3.6.2 - 1.8 - 1.8 + 10 + 10 @@ -223,7 +223,7 @@ org.projectlombok lombok - 1.16.16 + 1.18.10 provided - - - org.apache.maven.plugins maven-shade-plugin diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java index 6bfb220..a424555 100755 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java @@ -142,22 +142,15 @@ public class PlayerListWatcher { } }); plist = currentPL; - try { - Field mpf = mock.getClass().getField("maxPlayers"); - mpf.setAccessible(true); - Field modf = mpf.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(mpf, mpf.getModifiers() & ~Modifier.FINAL); - mpf.set(mock, mpf.get(plist)); - } catch (NoSuchFieldException ignored) { - //The field no longer exists on 1.14 + for (var plc = dplc; plc != null; plc = plc.getSuperclass()) { //Set all fields + for (var f : plc.getDeclaredFields()) { + f.setAccessible(true); + Field modf = f.getClass().getDeclaredField("modifiers"); + modf.setAccessible(true); + modf.set(f, f.getModifiers() & ~Modifier.FINAL); + f.set(mock, f.get(plist)); + } } - Field plf = mock.getClass().getField("players"); - plf.setAccessible(true); - Field modf = plf.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(plf, plf.getModifiers() & ~Modifier.FINAL); - plf.set(mock, plf.get(plist)); } try { server.getClass().getMethod("a", dplc).invoke(server, up ? mock : plist); From 73fc4aedcc7e1413dbffd1aee656eb296dc67aeb Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 27 Oct 2019 02:33:13 +0100 Subject: [PATCH 19/21] Fixed DCP inventory, have no idea about MassiveCore Essentials error is fixed #106 --- .../discordplugin/DiscordConnectedPlayer.java | 47 +++- .../playerfaker/DiscordInventory.java | 208 ++++++++++++++++++ .../buttondevteam/DiscordPlugin/AppTest.java | 76 ++++--- 3 files changed, 293 insertions(+), 38 deletions(-) create mode 100644 src/main/java/buttondevteam/discordplugin/playerfaker/DiscordInventory.java diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index 33fae6b..31311b8 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -1,6 +1,7 @@ package buttondevteam.discordplugin; import buttondevteam.discordplugin.mcchat.MinecraftChatModule; +import buttondevteam.discordplugin.playerfaker.DiscordInventory; import buttondevteam.discordplugin.playerfaker.VCMDWrapper; import discord4j.core.object.entity.MessageChannel; import discord4j.core.object.entity.User; @@ -12,14 +13,19 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.PlayerInventory; import org.bukkit.permissions.PermissibleBase; import org.bukkit.permissions.ServerOperator; -import org.mockito.Answers; +import org.mockito.MockSettings; import org.mockito.Mockito; +import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.UUID; +import static org.mockito.Answers.RETURNS_DEFAULTS; + public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer { private @Getter VCMDWrapper vanillaCmdListener; @Getter @@ -37,7 +43,7 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement @Setter private PermissibleBase perm; - private Location location = Bukkit.getWorlds().get(0).getSpawnLocation(); + private Location location; private final MinecraftChatModule module; @@ -50,6 +56,7 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement protected DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { super(user, channel); + location = Bukkit.getWorlds().get(0).getSpawnLocation(); origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid)); name = mcname; this.module = module; @@ -64,6 +71,15 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement } } + /** + * For testing + */ + protected DiscordConnectedPlayer(User user, MessageChannel channel) { + super(user, channel); + module = null; + uniqueId = UUID.randomUUID(); + } + public void setOp(boolean value) { //CraftPlayer-compatible implementation this.origPerm.setOp(value); this.perm.recalculatePermissions(); @@ -161,7 +177,30 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { - return Mockito.mock(DiscordConnectedPlayer.class, Mockito.withSettings() - .defaultAnswer(Answers.CALLS_REAL_METHODS).useConstructor(user, channel, uuid, mcname, module)); + return Mockito.mock(DiscordConnectedPlayer.class, + getSettings().useConstructor(user, channel, uuid, mcname, module)); + } + + public static DiscordConnectedPlayer createTest() { + return Mockito.mock(DiscordConnectedPlayer.class, getSettings().useConstructor(null, null)); + } + + private static MockSettings getSettings() { + return Mockito.withSettings() + .defaultAnswer(invocation -> { + try { + if (!Modifier.isAbstract(invocation.getMethod().getModifiers())) + return invocation.callRealMethod(); + if (PlayerInventory.class.isAssignableFrom(invocation.getMethod().getReturnType())) + return Mockito.mock(DiscordInventory.class, Mockito.withSettings().extraInterfaces(PlayerInventory.class)); + if (Inventory.class.isAssignableFrom(invocation.getMethod().getReturnType())) + return new DiscordInventory(); + return RETURNS_DEFAULTS.answer(invocation); + } catch (Exception e) { + System.err.println("Error in mocked player!"); + e.printStackTrace(); + return RETURNS_DEFAULTS.answer(invocation); + } + }); } } diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordInventory.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordInventory.java new file mode 100644 index 0000000..ab22f52 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordInventory.java @@ -0,0 +1,208 @@ +package buttondevteam.discordplugin.playerfaker; + +import lombok.Getter; +import lombok.Setter; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.stream.IntStream; + +public class DiscordInventory implements Inventory { + private ItemStack[] items = new ItemStack[27]; + private List itemStacks = Arrays.asList(items); + @Getter + @Setter + public int maxStackSize; + private static ItemStack emptyStack = new ItemStack(Material.AIR, 0); + + @Override + public int getSize() { + return items.length; + } + + @Override + public String getName() { + return "Discord inventory"; + } + + @Override + public ItemStack getItem(int index) { + if (index >= items.length) + return emptyStack; + else + return items[index]; + } + + @Override + public void setItem(int index, ItemStack item) { + if (index < items.length) + items[index] = item; + } + + @Override + public HashMap addItem(ItemStack... items) throws IllegalArgumentException { + return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything + } + + @Override + public HashMap removeItem(ItemStack... items) throws IllegalArgumentException { + return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything + } + + @Override + public ItemStack[] getContents() { + return items; + } + + @Override + public void setContents(ItemStack[] items) throws IllegalArgumentException { + this.items = items; + } + + @Override + public ItemStack[] getStorageContents() { + return items; + } + + @Override + public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { + this.items = items; + } + + @SuppressWarnings("deprecation") + @Override + public boolean contains(int materialId) { + return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId); + } + + @Override + public boolean contains(Material material) throws IllegalArgumentException { + return itemStacks.stream().anyMatch(is -> is.getType() == material); + } + + @Override + public boolean contains(ItemStack item) { + return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == item.getAmount()); + } + + @SuppressWarnings("deprecation") + @Override + public boolean contains(int materialId, int amount) { + return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId && is.getAmount() == amount); + } + + @Override + public boolean contains(Material material, int amount) throws IllegalArgumentException { + return itemStacks.stream().anyMatch(is -> is.getType() == material && is.getAmount() == amount); + } + + @Override + public boolean contains(ItemStack item, int amount) { //Not correct implementation but whatever + return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == amount); + } + + @Override + public boolean containsAtLeast(ItemStack item, int amount) { + return false; + } + + @Override + public HashMap all(int materialId) { + return new HashMap<>(); + } + + @Override + public HashMap all(Material material) throws IllegalArgumentException { + return new HashMap<>(); + } + + @Override + public HashMap all(ItemStack item) { + return new HashMap<>(); + } + + @Override + public int first(int materialId) { + return -1; + } + + @Override + public int first(Material material) throws IllegalArgumentException { + return -1; + } + + @Override + public int first(ItemStack item) { + return -1; + } + + @Override + public int firstEmpty() { + return -1; + } + + @Override + public void remove(int materialId) { + } + + @Override + public void remove(Material material) throws IllegalArgumentException { + } + + @Override + public void remove(ItemStack item) { + } + + @Override + public void clear(int index) { + if (index < items.length) + items[index] = null; + } + + @Override + public void clear() { + Arrays.fill(items, null); + } + + @Override + public List getViewers() { + return Collections.emptyList(); + } + + @Override + public String getTitle() { + return "Discord inventory"; + } + + @Override + public InventoryType getType() { + return InventoryType.CHEST; + } + + @Override + public InventoryHolder getHolder() { + return null; + } + + @SuppressWarnings("NullableProblems") + @Override + public ListIterator iterator() { + return itemStacks.listIterator(); + } + + @Override + public ListIterator iterator(int index) { + return itemStacks.listIterator(index); + } + + @Override + public Location getLocation() { + return null; + } +} diff --git a/src/test/java/buttondevteam/DiscordPlugin/AppTest.java b/src/test/java/buttondevteam/DiscordPlugin/AppTest.java index 46ad699..93dbbb4 100755 --- a/src/test/java/buttondevteam/DiscordPlugin/AppTest.java +++ b/src/test/java/buttondevteam/DiscordPlugin/AppTest.java @@ -1,34 +1,42 @@ -package buttondevteam.DiscordPlugin; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest extends TestCase { - /** - * Create the test case - * - * @param testName - * name of the test case - */ - public AppTest(String testName) { - super(testName); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() { - return new TestSuite(AppTest.class); - } - - /** - * Rigourous Test :-) - */ - public void testApp() { - assertTrue(true); - } -} +package buttondevteam.DiscordPlugin; + +import buttondevteam.discordplugin.DiscordConnectedPlayer; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bukkit.entity.Damageable; +import org.bukkit.entity.Player; + +/** + * Unit test for simple App. + */ +public class AppTest extends TestCase { + /** + * Create the test case + * + * @param testName + * name of the test case + */ + public AppTest(String testName) { + super(testName); + } + + /** + * @return the suite of tests being tested + */ + public static Test suite() { + return new TestSuite(AppTest.class); + } + + /** + * Rigourous Test :-) + */ + public void testApp() { + Player dcp = DiscordConnectedPlayer.createTest(); + + double h = dcp.getMaxHealth(); + System.out.println(h); + Damageable d = (Damageable) dcp; + System.out.println(d.getMaxHealth()); + } +} From 98ee2ce771242f81fbe2402e18d0d7954eeb4e50 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sun, 27 Oct 2019 23:27:54 +0100 Subject: [PATCH 20/21] Fixed the MassiveCore error but there's more #106 --- .../discordplugin/DiscordConnectedPlayer.java | 47 +++++++++++++++++++ .../buttondevteam/DiscordPlugin/AppTest.java | 6 +-- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index 31311b8..c457d2a 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -9,6 +9,9 @@ import lombok.Getter; import lombok.Setter; import lombok.experimental.Delegate; import org.bukkit.*; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.attribute.AttributeModifier; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.player.AsyncPlayerChatEvent; @@ -21,6 +24,8 @@ import org.mockito.MockSettings; import org.mockito.Mockito; import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.UUID; @@ -175,6 +180,48 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement @Setter private String displayName; + @Override + public AttributeInstance getAttribute(Attribute attribute) { + return new AttributeInstance() { + @Override + public Attribute getAttribute() { + return attribute; + } + + @Override + public double getBaseValue() { + return getDefaultValue(); + } + + @Override + public void setBaseValue(double value) { + } + + @Override + public Collection getModifiers() { + return Collections.emptyList(); + } + + @Override + public void addModifier(AttributeModifier modifier) { + } + + @Override + public void removeModifier(AttributeModifier modifier) { + } + + @Override + public double getValue() { + return getDefaultValue(); + } + + @Override + public double getDefaultValue() { + return 20; //Works for max health, should be okay for the rest + } + }; + } + public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { return Mockito.mock(DiscordConnectedPlayer.class, diff --git a/src/test/java/buttondevteam/DiscordPlugin/AppTest.java b/src/test/java/buttondevteam/DiscordPlugin/AppTest.java index 93dbbb4..4b8bbb9 100755 --- a/src/test/java/buttondevteam/DiscordPlugin/AppTest.java +++ b/src/test/java/buttondevteam/DiscordPlugin/AppTest.java @@ -4,7 +4,7 @@ import buttondevteam.discordplugin.DiscordConnectedPlayer; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -import org.bukkit.entity.Damageable; +import org.bukkit.attribute.Attribute; import org.bukkit.entity.Player; /** @@ -34,9 +34,7 @@ public class AppTest extends TestCase { public void testApp() { Player dcp = DiscordConnectedPlayer.createTest(); - double h = dcp.getMaxHealth(); + double h = dcp.getAttribute(Attribute.GENERIC_MAX_HEALTH).getDefaultValue(); ; ; System.out.println(h); - Damageable d = (Damageable) dcp; - System.out.println(d.getMaxHealth()); } } From 9caf4c54edd0021f4d5431f929d41f0adf051c59 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Wed, 30 Oct 2019 15:05:31 +0100 Subject: [PATCH 21/21] Fixed that last error #106 --- .../buttondevteam/discordplugin/DiscordConnectedPlayer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java index c457d2a..ee0aa81 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java @@ -222,6 +222,11 @@ public abstract class DiscordConnectedPlayer extends DiscordSenderBase implement }; } + @Override + public GameMode getGameMode() { + return GameMode.SPECTATOR; + } + public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) { return Mockito.mock(DiscordConnectedPlayer.class,