diff --git a/.travis.yml b/.travis.yml index 36779c2..808d9b0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,10 @@ cache: directories: - $HOME/.m2/repository/org/ before_install: | # Wget BuildTools and run if cached folder not found - if [ ! -d "$HOME/.m2/repository/org/spigotmc/spigot/1.12-R0.1-SNAPSHOT" ]; then + if [ ! -d "$HOME/.m2/repository/org/spigotmc/spigot/1.12.2-R0.1-SNAPSHOT" ]; then wget -O BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar # grep so that download counts don't appear in log files - java -jar BuildTools.jar --rev 1.12 | grep -vE "[^/ ]*/[^/ ]*\s*KB\s*$" | grep -v "^\s*$" + java -jar BuildTools.jar --rev 1.12.2 | grep -vE "[^/ ]*/[^/ ]*\s*KB\s*$" | grep -v "^\s*$" fi language: java jdk: diff --git a/pom.xml b/pom.xml index d2d1e7b..2c3031f 100644 --- a/pom.xml +++ b/pom.xml @@ -153,7 +153,7 @@ org.spigotmc spigot - 1.12-R0.1-SNAPSHOT + 1.12.2-R0.1-SNAPSHOT provided @@ -210,5 +210,13 @@ + + + org.objenesis + objenesis + 2.6 + test + + diff --git a/src/main/java/buttondevteam/discordplugin/ChromaBot.java b/src/main/java/buttondevteam/discordplugin/ChromaBot.java new file mode 100644 index 0000000..0a4507a --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/ChromaBot.java @@ -0,0 +1,147 @@ +package buttondevteam.discordplugin; + +import java.awt.Color; +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scheduler.BukkitScheduler; + +import buttondevteam.discordplugin.listeners.MCChatListener; +import lombok.Getter; +import sx.blah.discord.api.internal.json.objects.EmbedObject; +import sx.blah.discord.handle.obj.IChannel; +import sx.blah.discord.util.EmbedBuilder; + +public class ChromaBot { + /** + * May be null if it's not initialized. Initialization happens after the server is done loading (using {@link BukkitScheduler#runTaskAsynchronously(org.bukkit.plugin.Plugin, Runnable)}) + */ + private static @Getter ChromaBot instance; + private DiscordPlugin dp; + + /** + * This will set the instance field. + * + * @param dp + * The Discord plugin + */ + ChromaBot(DiscordPlugin dp) { + instance = this; + this.dp = dp; + } + + static void delete() { + instance = null; + } + + /** + * Send a message to the chat channel and private chats. + * + * @param message + * The message to send, duh + */ + public void sendMessage(String message) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message)); + } + + /** + * Send a message to the chat channel and private chats. + * + * @param message + * The message to send, duh + * @param embed + * Custom fancy stuff, use {@link EmbedBuilder} to create one + */ + public void sendMessage(String message, EmbedObject embed) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed)); + } + + /** + * Send a message to an arbitrary channel. This will not send it to the private chats. + * + * @param channel + * The channel to send to, use the channel variables in {@link DiscordPlugin} + * @param message + * The message to send, duh + * @param embed + * Custom fancy stuff, use {@link EmbedBuilder} to create one + */ + public void sendMessage(IChannel channel, String message, EmbedObject embed) { + DiscordPlugin.sendMessageToChannel(channel, message, embed); + } + + /** + * Send a fancy message to the chat channel. This will show a bold text with a colored line. + * + * @param message + * The message to send, duh + * @param color + * The color of the line before the text + */ + public void sendMessage(String message, Color color) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, + new EmbedBuilder().withTitle(message).withColor(color).build())); + } + + /** + * Send a fancy message to the chat channel. This will show a bold text with a colored line. + * + * @param message + * The message to send, duh + * @param color + * The color of the line before the text + * @param mcauthor + * The name of the Minecraft player who is the author of this message + */ + public void sendMessage(String message, Color color, String mcauthor) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, + DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build())); + } + + /** + * Send a fancy message to the chat channel. This will show a bold text with a colored line. + * + * @param message + * The message to send, duh + * @param color + * The color of the line before the text + * @param authorname + * The name of the author of this message + * @param authorimg + * The URL of the avatar image for this message's author + */ + public void sendMessage(String message, Color color, String authorname, String authorimg) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder() + .withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build())); + } + + /** + * Send a message to the chat channel. This will show a bold text with a colored line. + * + * @param message + * The message to send, duh + * @param color + * The color of the line before the text + * @param sender + * The player who sends this message + */ + public void sendMessage(String message, Color color, Player sender) { + MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils + .embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build())); + } + + public void updatePlayerList() { + DPUtils.performNoWait(() -> { + String[] s = DiscordPlugin.chatchannel.getTopic().split("\\n----\\n"); + if (s.length < 3) + return; + s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "") + + " online"; + s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream() + .map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", ")); + DiscordPlugin.chatchannel.changeTopic(Arrays.stream(s).collect(Collectors.joining("\n----\n"))); + }); + } +} diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java new file mode 100644 index 0000000..22fc8d7 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java @@ -0,0 +1,67 @@ +package buttondevteam.discordplugin; + +import sx.blah.discord.util.EmbedBuilder; +import sx.blah.discord.util.RequestBuffer; +import sx.blah.discord.util.RequestBuffer.IRequest; +import sx.blah.discord.util.RequestBuffer.IVoidRequest; + +public final class DPUtils { + + public static EmbedBuilder embedWithHead(EmbedBuilder builder, String playername) { + return builder.withAuthorIcon("https://minotar.net/avatar/" + playername + "/32.png"); + } + + /** Removes §[char] colour codes from strings */ + public static String sanitizeString(String string) { + String sanitizedString = ""; + boolean random = false; + for (int i = 0; i < string.length(); i++) { + if (string.charAt(i) == '§') { + i++;// Skips the data value, the 4 in "§4Alisolarflare" + if (string.charAt(i) == 'k') + random = true; + else + random = false; + } else { + if (!random) // Skip random/obfuscated characters + sanitizedString += string.charAt(i); + } + } + return sanitizedString; + } + + /** + * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode. + */ + public static T perform(IRequest action) { + if (DiscordPlugin.SafeMode) + return null; + if (Thread.currentThread() == DiscordPlugin.mainThread) + throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag."); + return RequestBuffer.request(action).get(); // Let the pros handle this + } + + /** + * Performs Discord actions, retrying when ratelimited. + */ + public static Void perform(IVoidRequest action) { + if (DiscordPlugin.SafeMode) + return null; + if (Thread.currentThread() == DiscordPlugin.mainThread) + throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag."); + return RequestBuffer.request(action).get(); // Let the pros handle this + } + + public static void performNoWait(IVoidRequest action) { + if (DiscordPlugin.SafeMode) + return; + RequestBuffer.request(action); + } + + public static void performNoWait(IRequest action) { + if (DiscordPlugin.SafeMode) + return; + RequestBuffer.request(action); + } + +} diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java index e2d22e2..e15904b 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java @@ -4,11 +4,9 @@ import java.awt.Color; import java.io.File; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Random; -import java.util.stream.Collectors; import org.bukkit.Bukkit; import org.bukkit.event.player.PlayerQuitEvent; @@ -33,22 +31,30 @@ import sx.blah.discord.handle.impl.events.ReadyEvent; import sx.blah.discord.handle.impl.obj.ReactionEmoji; import sx.blah.discord.handle.obj.*; import sx.blah.discord.util.*; -import sx.blah.discord.util.RequestBuffer.IRequest; -import sx.blah.discord.util.RequestBuffer.IVoidRequest; public class DiscordPlugin extends JavaPlugin implements IListener { private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers"; private static boolean stop = false; + static Thread mainThread; public static IDiscordClient dc; public static DiscordPlugin plugin; public static boolean SafeMode = true; public static List GameRoles; + public static boolean hooked = false; @SuppressWarnings("unchecked") @Override public void onEnable() { try { Bukkit.getLogger().info("Initializing DiscordPlugin..."); + try { + PlayerListWatcher.hookUp(); + hooked = true; + Bukkit.getLogger().info("Finished hooking into the player list"); + } catch (Throwable e) { + e.printStackTrace(); + Bukkit.getLogger().warning("Couldn't hook into the player list!"); + } plugin = this; lastannouncementtime = getConfig().getLong("lastannouncementtime"); lastseentime = getConfig().getLong("lastseentime"); @@ -57,6 +63,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { cb.withToken(Files.readFirstLine(new File("TBMC", "Token.txt"), StandardCharsets.UTF_8)); dc = cb.login(); dc.getDispatcher().registerListener(this); + mainThread = Thread.currentThread(); } catch (Exception e) { e.printStackTrace(); Bukkit.getPluginManager().disablePlugin(this); @@ -121,14 +128,16 @@ public class DiscordPlugin extends JavaPlugin implements IListener { if (getConfig().getBoolean("serverup", false)) { sendMessageToChannel(chatchannel, "", new EmbedBuilder().withColor(Color.YELLOW) .withTitle("Server recovered from a crash - chat connected.").build()); - TBMCCoreAPI.SendException("The server crashed!", new Throwable( - "The server shut down unexpectedly. See the log of the previous run for more details.")); + val thr = new Throwable( + "The server shut down unexpectedly. See the log of the previous run for more details."); + thr.setStackTrace(new StackTraceElement[0]); + TBMCCoreAPI.SendException("The server crashed!", thr); } else sendMessageToChannel(chatchannel, "", new EmbedBuilder().withColor(Color.GREEN) .withTitle("Server started - chat connected.").build()); getConfig().set("serverup", true); saveConfig(); - performNoWait(() -> { + DPUtils.performNoWait(() -> { try { List msgs = genchannel.getPinnedMessages(); for (int i = msgs.size() - 1; i >= 10; i--) { // Unpin all pinned messages except the newest 10 @@ -159,7 +168,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { "You could make a religion out of this"); } } - updatePlayerList(); + new ChromaBot(this).updatePlayerList(); } }, 0, 10); for (IListener listener : CommandListener.getListeners()) @@ -205,6 +214,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener { sendMessageToChannel(chatchannel, "", new EmbedBuilder().withColor(Restart ? Color.ORANGE : Color.RED) .withTitle(Restart ? "Server restarting" : "Server stopping").build()); try { + ChromaBot.delete(); dc.online("on TBMC"); dc.logout(); } catch (Exception e) { @@ -313,9 +323,9 @@ public class DiscordPlugin extends JavaPlugin implements IListener { RequestBuffer.IRequest r = () -> embed == null ? channel.sendMessage(content) : channel.sendMessage(content, embed, false); if (wait) - return perform(r); + return DPUtils.perform(r); else { - performNoWait(r); + DPUtils.performNoWait(r); return null; } } catch (Exception e) { @@ -325,10 +335,6 @@ public class DiscordPlugin extends JavaPlugin implements IListener { } } - public static EmbedBuilder embedWithHead(EmbedBuilder builder, String playername) { - return builder.withAuthorIcon("https://minotar.net/avatar/" + playername + "/32.png"); - } - public static Permission perms; public boolean setupProviders() { @@ -344,66 +350,4 @@ public class DiscordPlugin extends JavaPlugin implements IListener { perms = permsProvider.getProvider(); return perms != null; } - - /** Removes §[char] colour codes from strings */ - public static String sanitizeString(String string) { - String sanitizedString = ""; - boolean random = false; - for (int i = 0; i < string.length(); i++) { - if (string.charAt(i) == '§') { - i++;// Skips the data value, the 4 in "§4Alisolarflare" - if (string.charAt(i) == 'k') - random = true; - else - random = false; - } else { - if (!random) // Skip random/obfuscated characters - sanitizedString += string.charAt(i); - } - } - return sanitizedString; - } - - /** - * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode. - */ - public static T perform(IRequest action) { - if (SafeMode) - return null; - return RequestBuffer.request(action).get(); // Let the pros handle this - } - - /** - * Performs Discord actions, retrying when ratelimited. - */ - public static Void perform(IVoidRequest action) { - if (SafeMode) - return null; - return RequestBuffer.request(action).get(); // Let the pros handle this - } - - public static void performNoWait(IVoidRequest action) { - if (SafeMode) - return; - RequestBuffer.request(action); - } - - public static void performNoWait(IRequest action) { - if (SafeMode) - return; - RequestBuffer.request(action); - } - - public static void updatePlayerList() { - performNoWait(() -> { - String[] s = chatchannel.getTopic().split("\\n----\\n"); - if (s.length < 3) - return; - s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "") - + " online"; - s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream() - .map(p -> DiscordPlugin.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", ")); - chatchannel.changeTopic(Arrays.stream(s).collect(Collectors.joining("\n----\n"))); - }); - } } diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java index 5b18f50..6da2ff4 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java +++ b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java @@ -50,7 +50,7 @@ public abstract class DiscordSenderBase implements IDiscordSender { final boolean broadcast = new Exception().getStackTrace()[2].getMethodName().contains("broadcast"); if (broadcast) return; - final String sendmsg = DiscordPlugin.sanitizeString(message); + final String sendmsg = DPUtils.sanitizeString(message); msgtosend += "\n" + sendmsg; if (sendtask == null) sendtask = Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> { diff --git a/src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java new file mode 100644 index 0000000..365e1be --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/PlayerListWatcher.java @@ -0,0 +1,375 @@ +package buttondevteam.discordplugin; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.List; +import java.util.UUID; + +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 com.mojang.authlib.GameProfile; + +import buttondevteam.discordplugin.listeners.MCChatListener; +import buttondevteam.lib.TBMCCoreAPI; +import lombok.val; +import net.minecraft.server.v1_12_R1.AdvancementDataPlayer; +import net.minecraft.server.v1_12_R1.ChatMessageType; +import net.minecraft.server.v1_12_R1.DedicatedPlayerList; +import net.minecraft.server.v1_12_R1.DedicatedServer; +import net.minecraft.server.v1_12_R1.Entity; +import net.minecraft.server.v1_12_R1.EntityHuman; +import net.minecraft.server.v1_12_R1.EntityPlayer; +import net.minecraft.server.v1_12_R1.GameProfileBanList; +import net.minecraft.server.v1_12_R1.IChatBaseComponent; +import net.minecraft.server.v1_12_R1.IpBanList; +import net.minecraft.server.v1_12_R1.LoginListener; +import net.minecraft.server.v1_12_R1.MinecraftServer; +import net.minecraft.server.v1_12_R1.NBTTagCompound; +import net.minecraft.server.v1_12_R1.NetworkManager; +import net.minecraft.server.v1_12_R1.OpList; +import net.minecraft.server.v1_12_R1.Packet; +import net.minecraft.server.v1_12_R1.PacketPlayOutChat; +import net.minecraft.server.v1_12_R1.ScoreboardServer; +import net.minecraft.server.v1_12_R1.ServerStatisticManager; +import net.minecraft.server.v1_12_R1.WhiteList; +import net.minecraft.server.v1_12_R1.World; +import net.minecraft.server.v1_12_R1.WorldServer; + +public class PlayerListWatcher extends DedicatedPlayerList { + private DedicatedPlayerList plist; + + public PlayerListWatcher(DedicatedServer minecraftserver) { + super(minecraftserver); // <-- Does some init stuff and calls Bukkit.setServer() so we have to use Objenesis + } + + public void sendAll(Packet packet) { + plist.sendAll(packet); + try { // Some messages get sent by directly constructing a packet + if (packet instanceof PacketPlayOutChat) { + Field msgf = PacketPlayOutChat.class.getDeclaredField("a"); + msgf.setAccessible(true); + MCChatListener.sendSystemMessageToChat(((IChatBaseComponent) msgf.get(packet)).toPlainText()); + } + } catch (Exception e) { + TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e); + } + } + + @Override + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { // Needed so it calls the overriden method + plist.getServer().sendMessage(ichatbasecomponent); + ChatMessageType chatmessagetype = flag ? ChatMessageType.SYSTEM : ChatMessageType.CHAT; + + // CraftBukkit start - we run this through our processor first so we can get web links etc + this.sendAll(new PacketPlayOutChat(CraftChatMessage.fixComponent(ichatbasecomponent), chatmessagetype)); + // CraftBukkit end + } + + @Override + public void sendMessage(IChatBaseComponent ichatbasecomponent) { // Needed so it calls the overriden method + this.sendMessage(ichatbasecomponent, true); + } + + @Override + public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overriden method + for (IChatBaseComponent component : iChatBaseComponents) { + sendMessage(component, true); + } + } + + public static void hookUp() { + try { + Field conf = CraftServer.class.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"); + 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); + Field pllf = CraftServer.class.getDeclaredField("playerList"); + pllf.setAccessible(true); + pllf.set(Bukkit.getServer(), plw); + } catch (Exception e) { + TBMCCoreAPI.SendException("Error while hacking the player list!", e); + } + } + + 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); + } +} diff --git a/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java index 6f573f8..6a8d8e6 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java +++ b/src/main/java/buttondevteam/discordplugin/commands/RoleCommand.java @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.commands; import java.util.List; import java.util.stream.Collectors; +import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.TBMCCoreAPI; import sx.blah.discord.handle.obj.IMessage; @@ -28,7 +29,7 @@ public class RoleCommand extends DiscordCommandBase { if (role == null) return; try { - DiscordPlugin.perform(() -> message.getAuthor().addRole(role)); + DPUtils.perform(() -> message.getAuthor().addRole(role)); DiscordPlugin.sendMessageToChannel(message.getChannel(), "Added game role."); } catch (Exception e) { TBMCCoreAPI.SendException("Error while adding role!", e); @@ -39,7 +40,7 @@ public class RoleCommand extends DiscordCommandBase { if (role == null) return; try { - DiscordPlugin.perform(() -> message.getAuthor().removeRole(role)); + DPUtils.perform(() -> message.getAuthor().removeRole(role)); DiscordPlugin.sendMessageToChannel(message.getChannel(), "Removed game role."); } catch (Exception e) { TBMCCoreAPI.SendException("Error while removing role!", e); diff --git a/src/main/java/buttondevteam/discordplugin/listeners/AutoUpdaterListener.java b/src/main/java/buttondevteam/discordplugin/listeners/AutoUpdaterListener.java index c0fb834..cc8593a 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/AutoUpdaterListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/AutoUpdaterListener.java @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.listeners; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.lib.PluginUpdater; import buttondevteam.lib.TBMCCoreAPI; @@ -13,7 +14,7 @@ public class AutoUpdaterListener implements Listener { if (DiscordPlugin.SafeMode) return; try { - DiscordPlugin.performNoWait(() -> DiscordPlugin.officechannel.getMessageHistory(10).stream() + DPUtils.performNoWait(() -> DiscordPlugin.officechannel.getMessageHistory(10).stream() .filter(m -> m.getWebhookLongID() == 239123781401051138L && m.getEmbeds().get(0).getTitle() .contains(event.getData().get("repository").getAsJsonObject().get("name").getAsString())) .findFirst().get().addReaction(DiscordPlugin.DELIVERED_REACTION)); diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java index 52c9991..49704ae 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCChatListener.java @@ -35,7 +35,7 @@ public class MCChatListener implements Listener, IListener return; Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> { synchronized (this) { - final String authorPlayer = DiscordPlugin.sanitizeString(e.getSender() instanceof Player // + final String authorPlayer = DPUtils.sanitizeString(e.getSender() instanceof Player // ? ((Player) e.getSender()).getDisplayName() // : e.getSender().getName()); final EmbedBuilder embed = new EmbedBuilder().withAuthorName(authorPlayer) @@ -44,11 +44,10 @@ public class MCChatListener implements Listener, IListener // embed.appendField("Channel", ((e.getSender() instanceof DiscordSenderBase ? "d|" : "") // + DiscordPlugin.sanitizeString(e.getChannel().DisplayName)), false); if (e.getSender() instanceof Player) - DiscordPlugin - .embedWithHead( - embed.withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=minecraft&id=" - + ((Player) e.getSender()).getUniqueId()), - ((Player) e.getSender()).getName()); + DPUtils.embedWithHead( + embed.withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=minecraft&id=" + + ((Player) e.getSender()).getUniqueId()), + ((Player) e.getSender()).getName()); else if (e.getSender() instanceof DiscordSenderBase) embed.withAuthorIcon(((DiscordSenderBase) e.getSender()).getUser().getAvatarURL()) .withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=discord&id=" @@ -59,13 +58,14 @@ public class MCChatListener implements Listener, IListener Consumer doit = lastmsgdata -> { final EmbedObject embedObject = embed.build(); final String dmsg = lastmsgdata.channel.isPrivate() - ? DiscordPlugin.sanitizeString(e.getChannel().DisplayName) : ""; + ? DPUtils.sanitizeString(e.getChannel().DisplayName) + : ""; if (lastmsgdata.message == null || lastmsgdata.message.isDeleted() || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName()) || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 || !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) { lastmsgdata.message = DiscordPlugin.sendMessageToChannelWait(lastmsgdata.channel, dmsg, - embedObject); + embedObject); // TODO Use ChromaBot API lastmsgdata.time = nanoTime; lastmsgdata.mcchannel = e.getChannel(); lastmsgdata.content = embedObject.description; @@ -74,7 +74,7 @@ public class MCChatListener implements Listener, IListener lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n" + embedObject.description;// The message object doesn't get updated final LastMsgData _lastmsgdata = lastmsgdata; - DiscordPlugin.perform(() -> _lastmsgdata.message.edit(dmsg, embedObject)); + DPUtils.perform(() -> _lastmsgdata.message.edit(dmsg, embedObject)); } catch (MissingPermissionsException | DiscordException e1) { TBMCCoreAPI.SendException("An error occured while editing chat message!", e1); } @@ -85,8 +85,9 @@ public class MCChatListener implements Listener, IListener if ((e.getChannel() == Channel.GlobalChat || e.getChannel().ID.equals("rp")) && isdifferentchannel.test(DiscordPlugin.chatchannel)) - doit.accept(lastmsgdata == null - ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null, null) : lastmsgdata); + doit.accept( + lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null, null) + : lastmsgdata); for (LastMsgData data : lastmsgPerUser) { if (data.dp.isMinecraftChatEnabled() && isdifferentchannel.test(data.channel) @@ -123,8 +124,7 @@ public class MCChatListener implements Listener, IListener DiscordPlugin.dc.getUsersByName(event.getMessage().substring(start + 1, mid)).stream() .filter(u -> u.getDiscriminator().equals(event.getMessage().substring(mid + 1, end))).findAny() .ifPresent(user -> event.setMessage(event.getMessage().substring(0, startF) + "@" + user.getName() - + (event.getMessage().length() > end ? event.getMessage().substring(end) - : ""))); // TODO: Add formatting + + (event.getMessage().length() > end ? event.getMessage().substring(end) : ""))); // TODO: Add formatting start = end; // Skip any @s inside the mention } } @@ -203,18 +203,26 @@ public class MCChatListener implements Listener, IListener * This overload sends it to the global chat. */ public static void sendSystemMessageToChat(String msg) { - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, DiscordPlugin.sanitizeString(msg)); - for (LastMsgData data : lastmsgPerUser) - DiscordPlugin.sendMessageToChannel(data.channel, DiscordPlugin.sanitizeString(msg)); + forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(msg))); } public static void sendSystemMessageToChat(TBMCSystemChatEvent event) { + forAllowedMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(event.getMessage())), + event); + } + + public static void forAllMCChat(Consumer action) { + action.accept(DiscordPlugin.chatchannel); + for (LastMsgData data : lastmsgPerUser) + action.accept(data.channel); + } + + private static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) { if (Channel.GlobalChat.ID.equals(event.getChannel().ID)) - DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, - DiscordPlugin.sanitizeString(event.getMessage())); + action.accept(DiscordPlugin.chatchannel); for (LastMsgData data : lastmsgPerUser) if (event.shouldSendTo(getSender(data.channel, data.user, data.dp))) - DiscordPlugin.sendMessageToChannel(data.channel, DiscordPlugin.sanitizeString(event.getMessage())); + action.accept(data.channel); } @Override // Discord @@ -257,7 +265,7 @@ public class MCChatListener implements Listener, IListener boolean react = false; if (dmessage.startsWith("/")) { // Ingame command - DiscordPlugin.perform(() -> { + DPUtils.perform(() -> { if (!event.getMessage().isDeleted() && !event.getChannel().isPrivate()) event.getMessage().delete(); }); @@ -308,7 +316,7 @@ public class MCChatListener implements Listener, IListener } else dsender.setMcchannel(Channel.GlobalChat); dsender.sendMessage("You're now talking in: " - + DiscordPlugin.sanitizeString(dsender.getMcchannel().DisplayName)); + + DPUtils.sanitizeString(dsender.getMcchannel().DisplayName)); } else { // Send single message sendChatMessage.accept(chc, cmd.substring(spi + 1)); react = true; @@ -333,12 +341,12 @@ public class MCChatListener implements Listener, IListener try { final IReaction reaction = m.getReactionByEmoji(DiscordPlugin.DELIVERED_REACTION); if (reaction != null) - DiscordPlugin.perform(() -> m.removeReaction(DiscordPlugin.dc.getOurUser(), reaction)); + DPUtils.perform(() -> m.removeReaction(DiscordPlugin.dc.getOurUser(), reaction)); } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e); } }); - DiscordPlugin.performNoWait(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION)); + DPUtils.performNoWait(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION)); } } catch (Exception e) { TBMCCoreAPI.SendException("An error occured while handling message \"" + dmessage + "\"!", e); diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java index 6f1bf9a..57a4ba2 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java @@ -23,6 +23,8 @@ import org.bukkit.plugin.RegisteredListener; import com.earth2me.essentials.CommandSource; +import buttondevteam.discordplugin.ChromaBot; +import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordConnectedPlayer; import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlayerSender; @@ -68,9 +70,10 @@ public class MCListener implements Listener { + " do /discord accept"); p.sendMessage("§bIf it wasn't you, do /discord decline"); } - MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game"); + if (!DiscordPlugin.hooked) + MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game"); MCChatListener.ListC = 0; - DiscordPlugin.updatePlayerList(); + ChromaBot.getInstance().updatePlayerList(); } @EventHandler(priority = EventPriority.HIGHEST) @@ -83,8 +86,10 @@ public class MCListener implements Listener { () -> MCChatListener.ConnectedSenders.values().stream() .filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny() .ifPresent(dcp -> callEventExcludingSome(new PlayerJoinEvent(dcp, "")))); - MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " left the game"); - Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, DiscordPlugin::updatePlayerList, 5); + if (!DiscordPlugin.hooked) + MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " left the game"); + Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, + ChromaBot.getInstance()::updatePlayerList, 5); } @EventHandler @@ -105,14 +110,15 @@ public class MCListener implements Listener { @EventHandler(priority = EventPriority.LOW) public void onPlayerDeath(PlayerDeathEvent e) { - MCChatListener.sendSystemMessageToChat(e.getDeathMessage()); + if (!DiscordPlugin.hooked) + MCChatListener.sendSystemMessageToChat(e.getDeathMessage()); } @EventHandler public void onPlayerAFK(AfkStatusChangeEvent e) { if (e.isCancelled() || !e.getAffected().getBase().isOnline()) return; - MCChatListener.sendSystemMessageToChat(DiscordPlugin.sanitizeString(e.getAffected().getBase().getDisplayName()) + MCChatListener.sendSystemMessageToChat(DPUtils.sanitizeString(e.getAffected().getBase().getDisplayName()) + " is " + (e.getValue() ? "now" : "no longer") + " AFK."); } @@ -124,7 +130,7 @@ public class MCListener implements Listener { @EventHandler public void onPlayerMute(MuteStatusChangeEvent e) { try { - DiscordPlugin.performNoWait(() -> { + DPUtils.performNoWait(() -> { final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L); final CommandSource source = e.getAffected().getSource(); if (!source.isPlayer())