diff --git a/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.java b/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.java
deleted file mode 100644
index c51471b..0000000
--- a/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.discordplugin.util.DPState;
-import org.apache.logging.log4j.Level;
-import org.apache.logging.log4j.core.Filter;
-import org.apache.logging.log4j.core.LogEvent;
-import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.filter.LevelRangeFilter;
-import org.apache.logging.log4j.core.layout.PatternLayout;
-
-public class BukkitLogWatcher extends AbstractAppender {
- protected BukkitLogWatcher() {
- super("ChromaDiscord",
- LevelRangeFilter.createFilter(Level.INFO, Level.INFO, Filter.Result.ACCEPT, Filter.Result.DENY),
- PatternLayout.createDefaultLayout());
- }
-
- @Override
- public void append(LogEvent logEvent) {
- if (logEvent.getMessage().getFormattedMessage().contains("Attempting to restart with "))
- MinecraftChatModule.state = DPState.RESTARTING_SERVER;
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.scala b/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.scala
new file mode 100644
index 0000000..7fee830
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/BukkitLogWatcher.scala
@@ -0,0 +1,17 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule
+import buttondevteam.discordplugin.util.DPState
+import org.apache.logging.log4j.Level
+import org.apache.logging.log4j.core.{Filter, LogEvent}
+import org.apache.logging.log4j.core.appender.AbstractAppender
+import org.apache.logging.log4j.core.filter.LevelRangeFilter
+import org.apache.logging.log4j.core.layout.PatternLayout
+
+class BukkitLogWatcher private[discordplugin]() extends AbstractAppender("ChromaDiscord",
+ LevelRangeFilter.createFilter(Level.INFO, Level.INFO, Filter.Result.ACCEPT, Filter.Result.DENY),
+ PatternLayout.createDefaultLayout) {
+ override def append(logEvent: LogEvent): Unit =
+ if (logEvent.getMessage.getFormattedMessage.contains("Attempting to restart with "))
+ MinecraftChatModule.state = DPState.RESTARTING_SERVER
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.java b/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.java
deleted file mode 100644
index 994c8ed..0000000
--- a/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package buttondevteam.discordplugin;
-
-public enum ChannelconBroadcast {
- JOINLEAVE,
- AFK,
- RESTART,
- DEATH,
- BROADCAST;
-
- public final int flag;
-
- ChannelconBroadcast() {
- this.flag = 1 << this.ordinal();
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.scala b/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.scala
new file mode 100644
index 0000000..317a759
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/ChannelconBroadcast.scala
@@ -0,0 +1,6 @@
+package buttondevteam.discordplugin
+
+object ChannelconBroadcast extends Enumeration {
+ type ChannelconBroadcast = Value
+ val JOINLEAVE, AFK, RESTART, DEATH, BROADCAST = Value
+}
\ No newline at end of file
diff --git a/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala b/src/main/java/buttondevteam/discordplugin/ChromaBot.scala
similarity index 94%
rename from src/main/scala/buttondevteam/discordplugin/ChromaBot.scala
rename to src/main/java/buttondevteam/discordplugin/ChromaBot.scala
index 6a8b7e0..187c7f8 100644
--- a/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala
+++ b/src/main/java/buttondevteam/discordplugin/ChromaBot.scala
@@ -1,5 +1,6 @@
package buttondevteam.discordplugin
+import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin.mcchat.MCChatUtils
import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.MessageChannel
diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java
deleted file mode 100755
index 747c21b..0000000
--- a/src/main/java/buttondevteam/discordplugin/DPUtils.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.lib.TBMCCoreAPI;
-import buttondevteam.lib.architecture.Component;
-import buttondevteam.lib.architecture.ConfigData;
-import buttondevteam.lib.architecture.IHaveConfig;
-import buttondevteam.lib.architecture.ReadOnlyConfigData;
-import discord4j.common.util.Snowflake;
-import discord4j.core.object.entity.Guild;
-import discord4j.core.object.entity.Message;
-import discord4j.core.object.entity.Role;
-import discord4j.core.object.entity.channel.MessageChannel;
-import discord4j.core.spec.EmbedCreateSpec;
-import lombok.val;
-import reactor.core.publisher.Mono;
-
-import javax.annotation.Nullable;
-import java.util.Comparator;
-import java.util.Optional;
-import java.util.TreeSet;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-public final class DPUtils {
-
- private static final Pattern URL_PATTERN = Pattern.compile("https?://\\S*");
- private static final Pattern FORMAT_PATTERN = Pattern.compile("[*_~]");
-
- public static EmbedCreateSpec embedWithHead(EmbedCreateSpec ecs, String displayname, String playername, String profileUrl) {
- return ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png");
- }
-
- /**
- * Removes §[char] colour codes from strings & escapes them for Discord
- * Ensure that this method only gets called once (escaping)
- */
- public static String sanitizeString(String string) {
- return escape(sanitizeStringNoEscape(string));
- }
-
- /**
- * Removes §[char] colour codes from strings
- */
- public static String sanitizeStringNoEscape(String string) {
- StringBuilder sanitizedString = new StringBuilder();
- boolean random = false;
- for (int i = 0; i < string.length(); i++) {
- if (string.charAt(i) == '§') {
- i++;// Skips the data value, the 4 in "§4Alisolarflare"
- random = string.charAt(i) == 'k';
- } else {
- if (!random) // Skip random/obfuscated characters
- sanitizedString.append(string.charAt(i));
- }
- }
- return sanitizedString.toString();
- }
-
- private static String escape(String message) {
- //var ts = new TreeSet<>();
- var ts = new TreeSet(Comparator.comparingInt(a -> a[0])); //Compare the start, then check the end
- var matcher = URL_PATTERN.matcher(message);
- while (matcher.find())
- ts.add(new int[]{matcher.start(), matcher.end()});
- matcher = FORMAT_PATTERN.matcher(message);
- /*Function aFunctionalInterface = result ->
- Optional.ofNullable(ts.floor(new int[]{result.start(), 0})).map(a -> a[1]).orElse(0) < result.start()
- ? "\\\\" + result.group() : result.group();
- return matcher.replaceAll(aFunctionalInterface); //Find nearest URL match and if it's not reaching to the char then escape*/
- StringBuffer sb = new StringBuffer();
- while (matcher.find()) {
- matcher.appendReplacement(sb, Optional.ofNullable(ts.floor(new int[]{matcher.start(), 0})) //Find a URL start <= our start
- .map(a -> a[1]).orElse(-1) < matcher.start() //Check if URL end < our start
- ? "\\\\" + matcher.group() : matcher.group());
- }
- matcher.appendTail(sb);
- return sb.toString();
- }
-
- public static Logger getLogger() {
- if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null)
- return Logger.getLogger("DiscordPlugin");
- return DiscordPlugin.plugin.getLogger();
- }
-
- public static ReadOnlyConfigData> channelData(IHaveConfig config, String key) {
- return config.getReadOnlyDataPrimDef(key, 0L, id -> getMessageChannel(key, Snowflake.of((Long) id)), ch -> 0L); //We can afford to search for the channel in the cache once (instead of using mainServer)
- }
-
- public static ReadOnlyConfigData> roleData(IHaveConfig config, String key, String defName) {
- return roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer));
- }
-
- /**
- * Needs to be a {@link ConfigData} for checking if it's set
- */
- public static ReadOnlyConfigData> roleData(IHaveConfig config, String key, String defName, Mono guild) {
- return config.getReadOnlyDataPrimDef(key, defName, name -> {
- if (!(name instanceof String) || ((String) name).length() == 0) return Mono.empty();
- return guild.flatMapMany(Guild::getRoles).filter(r -> r.getName().equals(name)).onErrorResume(e -> {
- getLogger().warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage());
- return Mono.empty();
- }).next();
- }, r -> defName);
- }
-
- public static ReadOnlyConfigData snowflakeData(IHaveConfig config, String key, long defID) {
- return config.getReadOnlyDataPrimDef(key, defID, id -> Snowflake.of((long) id), Snowflake::asLong);
- }
-
- /**
- * Mentions the bot channel. Useful for help texts.
- *
- * @return The string for mentioning the channel
- */
- public static String botmention() {
- if (DiscordPlugin.plugin == null) return "#bot";
- return channelMention(DiscordPlugin.plugin.commandChannel.get());
- }
-
- /**
- * Disables the component if one of the given configs return null. Useful for channel/role configs.
- *
- * @param component The component to disable if needed
- * @param configs The configs to check for null
- * @return Whether the component got disabled and a warning logged
- */
- public static boolean disableIfConfigError(@Nullable Component component, ConfigData>... configs) {
- for (val config : configs) {
- Object v = config.get();
- if (disableIfConfigErrorRes(component, config, v))
- return true;
- }
- return false;
- }
-
- /**
- * Disables the component if one of the given configs return null. Useful for channel/role configs.
- *
- * @param component The component to disable if needed
- * @param config The (snowflake) config to check for null
- * @param result The result of getting the value
- * @return Whether the component got disabled and a warning logged
- */
- public static boolean disableIfConfigErrorRes(@Nullable Component component, ConfigData> config, Object result) {
- //noinspection ConstantConditions
- if (result == null || (result instanceof Mono> && !((Mono>) result).hasElement().block())) {
- String path = null;
- try {
- if (component != null)
- Component.setComponentEnabled(component, false);
- path = config.getPath();
- } catch (Exception e) {
- if (component != null)
- TBMCCoreAPI.SendException("Failed to disable component after config error!", e, component);
- else
- TBMCCoreAPI.SendException("Failed to disable component after config error!", e, DiscordPlugin.plugin);
- }
- getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!"));
- getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message.");
- return true;
- }
- return false;
- }
-
- /**
- * Send a response in the form of "@User, message". Use Mono.empty() if you don't have a channel object.
- *
- * @param original The original message to reply to
- * @param channel The channel to send the message in, defaults to the original
- * @param message The message to send
- * @return A mono to send the message
- */
- public static Mono reply(Message original, @Nullable MessageChannel channel, String message) {
- Mono ch;
- if (channel == null)
- ch = original.getChannel();
- else
- ch = Mono.just(channel);
- return reply(original, ch, message);
- }
-
- /**
- * @see #reply(Message, MessageChannel, String)
- */
- public static Mono reply(Message original, Mono ch, String message) {
- return ch.flatMap(chan -> chan.createMessage((original.getAuthor().isPresent()
- ? original.getAuthor().get().getMention() + ", " : "") + message));
- }
-
- public static String nickMention(Snowflake userId) {
- return "<@!" + userId.asString() + ">";
- }
-
- public static String channelMention(Snowflake channelId) {
- return "<#" + channelId.asString() + ">";
- }
-
- /**
- * Gets a message channel for a config. Returns empty for ID 0.
- *
- * @param key The config key
- * @param id The channel ID
- * @return A message channel
- */
- public static Mono getMessageChannel(String key, Snowflake id) {
- if (id.asLong() == 0L) return Mono.empty();
- return DiscordPlugin.dc.getChannelById(id).onErrorResume(e -> {
- getLogger().warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage());
- return Mono.empty();
- }).filter(ch -> ch instanceof MessageChannel).cast(MessageChannel.class);
- }
-
- public static Mono getMessageChannel(ConfigData config) {
- 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/DPUtils.scala b/src/main/java/buttondevteam/discordplugin/DPUtils.scala
new file mode 100644
index 0000000..f69db27
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DPUtils.scala
@@ -0,0 +1,209 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.lib.TBMCCoreAPI
+import buttondevteam.lib.architecture.{Component, ConfigData, IHaveConfig, ReadOnlyConfigData}
+import discord4j.common.util.Snowflake
+import discord4j.core.`object`.entity.channel.MessageChannel
+import discord4j.core.`object`.entity.{Guild, Message, Role}
+import discord4j.core.spec.EmbedCreateSpec
+import reactor.core.publisher.Mono
+
+import java.util
+import java.util.{Comparator, Optional}
+import java.util.logging.Logger
+import java.util.regex.Pattern
+import javax.annotation.Nullable
+
+object DPUtils {
+ private val URL_PATTERN = Pattern.compile("https?://\\S*")
+ private val FORMAT_PATTERN = Pattern.compile("[*_~]")
+
+ def embedWithHead(ecs: EmbedCreateSpec, displayname: String, playername: String, profileUrl: String): EmbedCreateSpec =
+ ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png")
+
+ /**
+ * Removes §[char] colour codes from strings & escapes them for Discord
+ * Ensure that this method only gets called once (escaping)
+ */
+ def sanitizeString(string: String): String = escape(sanitizeStringNoEscape(string))
+
+ /**
+ * Removes §[char] colour codes from strings
+ */
+ def sanitizeStringNoEscape(string: String): String = {
+ val sanitizedString = new StringBuilder
+ var random = false
+ var i = 0
+ while ( {
+ i < string.length
+ }) {
+ if (string.charAt(i) == '§') {
+ i += 1 // Skips the data value, the 4 in "§4Alisolarflare"
+ random = string.charAt(i) == 'k'
+ }
+ else if (!random) { // Skip random/obfuscated characters
+ sanitizedString.append(string.charAt(i))
+ }
+ i += 1
+ }
+ sanitizedString.toString
+ }
+
+ private def escape(message: String) = { //var ts = new TreeSet<>();
+ val ts = new util.TreeSet[Array[Int]](Comparator.comparingInt((a: Array[Int]) => a(0)): Comparator[Array[Int]]) //Compare the start, then check the end
+ var matcher = URL_PATTERN.matcher(message)
+ while ( {
+ matcher.find
+ }) ts.add(Array[Int](matcher.start, matcher.end))
+ matcher = FORMAT_PATTERN.matcher(message)
+ /*Function aFunctionalInterface = result ->
+ Optional.ofNullable(ts.floor(new int[]{result.start(), 0})).map(a -> a[1]).orElse(0) < result.start()
+ ? "\\\\" + result.group() : result.group();
+ return matcher.replaceAll(aFunctionalInterface); //Find nearest URL match and if it's not reaching to the char then escape*/ val sb = new StringBuffer
+ while ( {
+ matcher.find
+ }) matcher.appendReplacement(sb, if (Optional.ofNullable(ts.floor(Array[Int](matcher.start, 0))).map( //Find a URL start <= our start
+ (a: Array[Int]) => a(1)).orElse(-1) < matcher.start //Check if URL end < our start
+ ) "\\\\" + matcher.group else matcher.group)
+ matcher.appendTail(sb)
+ sb.toString
+ }
+
+ def getLogger: Logger = {
+ if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger == null) return Logger.getLogger("DiscordPlugin")
+ DiscordPlugin.plugin.getLogger
+ }
+
+ def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[Mono[MessageChannel]] =
+ config.getReadOnlyDataPrimDef(key, 0L, (id: Any) =>
+ getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), (_: Mono[MessageChannel]) => 0L) //We can afford to search for the channel in the cache once (instead of using mainServer)
+ def roleData(config: IHaveConfig, key: String, defName: String): ReadOnlyConfigData[Mono[Role]] =
+ roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer))
+
+ /**
+ * Needs to be a {@link ConfigData} for checking if it's set
+ */
+ def roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): ReadOnlyConfigData[Mono[Role]] = config.getReadOnlyDataPrimDef(key, defName, (name: Any) => {
+ def foo(name: Any): Mono[Role] = {
+ if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) return Mono.empty[Role]
+ guild.flatMapMany(_.getRoles).filter((r: Role) => r.getName == name).onErrorResume((e: Throwable) => {
+ def foo(e: Throwable): Mono[Role] = {
+ getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage)
+ Mono.empty[Role]
+ }
+
+ foo(e)
+ }).next
+ }
+
+ foo(name)
+ }, (_: Mono[Role]) => defName)
+
+ def snowflakeData(config: IHaveConfig, key: String, defID: Long): ReadOnlyConfigData[Snowflake] =
+ config.getReadOnlyDataPrimDef(key, defID, (id: Any) => Snowflake.of(id.asInstanceOf[Long]), _.asLong)
+
+ /**
+ * Mentions the bot channel. Useful for help texts.
+ *
+ * @return The string for mentioning the channel
+ */
+ def botmention: String = {
+ if (DiscordPlugin.plugin == null) return "#bot"
+ channelMention(DiscordPlugin.plugin.commandChannel.get)
+ }
+
+ /**
+ * Disables the component if one of the given configs return null. Useful for channel/role configs.
+ *
+ * @param component The component to disable if needed
+ * @param configs The configs to check for null
+ * @return Whether the component got disabled and a warning logged
+ */
+ def disableIfConfigError(@Nullable component: Component[DiscordPlugin], configs: ConfigData[_]*): Boolean = {
+ for (config <- configs) {
+ val v = config.get
+ if (disableIfConfigErrorRes(component, config, v)) return true
+ }
+ false
+ }
+
+ /**
+ * Disables the component if one of the given configs return null. Useful for channel/role configs.
+ *
+ * @param component The component to disable if needed
+ * @param config The (snowflake) config to check for null
+ * @param result The result of getting the value
+ * @return Whether the component got disabled and a warning logged
+ */
+ def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = {
+ //noinspection ConstantConditions
+ if (result == null || (result.isInstanceOf[Mono[_]] && !result.asInstanceOf[Mono[_]].hasElement.block)) {
+ var path: String = null
+ try {
+ if (component != null) Component.setComponentEnabled(component, false)
+ path = config.getPath
+ } catch {
+ case e: Exception =>
+ if (component != null) TBMCCoreAPI.SendException("Failed to disable component after config error!", e, component)
+ else TBMCCoreAPI.SendException("Failed to disable component after config error!", e, DiscordPlugin.plugin)
+ }
+ getLogger.warning("The config value " + path + " isn't set correctly " + (if (component == null) "in global settings!"
+ else "for component " + component.getClass.getSimpleName + "!"))
+ getLogger.warning("Set the correct ID in the config" + (if (component == null) ""
+ else " or disable this component") + " to remove this message.")
+ return true
+ }
+ false
+ }
+
+ /**
+ * Send a response in the form of "@User, message". Use Mono.empty() if you don't have a channel object.
+ *
+ * @param original The original message to reply to
+ * @param channel The channel to send the message in, defaults to the original
+ * @param message The message to send
+ * @return A mono to send the message
+ */
+ def reply(original: Message, @Nullable channel: MessageChannel, message: String): Mono[Message] = {
+ val ch = if (channel == null) original.getChannel
+ else Mono.just(channel)
+ reply(original, ch, message)
+ }
+
+ /**
+ * @see #reply(Message, MessageChannel, String)
+ */
+ def reply(original: Message, ch: Mono[MessageChannel], message: String): Mono[Message] =
+ ch.flatMap(_.createMessage((if (original.getAuthor.isPresent)
+ original.getAuthor.get.getMention + ", "
+ else "") + message))
+
+ def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">"
+
+ def channelMention(channelId: Snowflake): String = "<#" + channelId.asString + ">"
+
+ /**
+ * Gets a message channel for a config. Returns empty for ID 0.
+ *
+ * @param key The config key
+ * @param id The channel ID
+ * @return A message channel
+ */
+ def getMessageChannel(key: String, id: Snowflake): Mono[MessageChannel] = {
+ if (id.asLong == 0L) return Mono.empty[MessageChannel]
+
+ DiscordPlugin.dc.getChannelById(id).onErrorResume(e => {
+ def foo(e: Throwable) = {
+ getLogger.warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage)
+ Mono.empty
+ }
+
+ foo(e)
+ }).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel])
+ }
+
+ def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] =
+ getMessageChannel(config.getPath, config.get)
+
+ def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.empty)
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
deleted file mode 100644
index 714d347..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
+++ /dev/null
@@ -1,324 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.discordplugin.playerfaker.DiscordInventory;
-import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
-import discord4j.core.object.entity.User;
-import discord4j.core.object.entity.channel.MessageChannel;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.experimental.Delegate;
-import net.md_5.bungee.api.ChatMessageType;
-import net.md_5.bungee.api.chat.BaseComponent;
-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;
-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.MockSettings;
-import org.mockito.Mockito;
-
-import java.lang.reflect.Modifier;
-import java.net.InetSocketAddress;
-import java.util.*;
-
-import static org.mockito.Answers.RETURNS_DEFAULTS;
-
-public abstract class DiscordConnectedPlayer extends DiscordSenderBase implements IMCPlayer {
- private @Getter VCMDWrapper vanillaCmdListener;
- @Getter
- @Setter
- private boolean loggedIn = false;
-
- @Delegate(excludes = ServerOperator.class)
- private PermissibleBase origPerm;
-
- private @Getter String name;
-
- private @Getter OfflinePlayer basePlayer;
-
- @Getter
- @Setter
- private PermissibleBase perm;
-
- private Location location;
-
- 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);
- location = Bukkit.getWorlds().get(0).getSpawnLocation();
- origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid));
- name = mcname;
- this.module = module;
- uniqueId = uuid;
- displayName = mcname;
- vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, module));
- }
-
- /**
- * 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();
- }
-
- 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());
- }
-
- @Override
- public Location getEyeLocation() {
- return getLocation();
- }
-
- @Override
- @Deprecated
- public double getMaxHealth() {
- return 20;
- }
-
- @Override
- public Player getPlayer() {
- return this;
- }
-
- @Getter
- @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
- }
- };
- }
-
- @Override
- public GameMode getGameMode() {
- return GameMode.SPECTATOR;
- }
-
- @SuppressWarnings("deprecation")
- private final Player.Spigot spigot = new Player.Spigot() {
- @Override
- public InetSocketAddress getRawAddress() {
- return null;
- }
-
- @Override
- public void playEffect(Location location, Effect effect, int id, int data, float offsetX, float offsetY, float offsetZ, float speed, int particleCount, int radius) {
- }
-
- @Override
- public boolean getCollidesWithEntities() {
- return false;
- }
-
- @Override
- public void setCollidesWithEntities(boolean collides) {
- }
-
- @Override
- public void respawn() {
- }
-
- @Override
- public String getLocale() {
- return "en_us";
- }
-
- @Override
- public Set getHiddenPlayers() {
- return Collections.emptySet();
- }
-
- @Override
- public void sendMessage(BaseComponent component) {
- DiscordConnectedPlayer.super.sendMessage(component.toPlainText());
- }
-
- @Override
- public void sendMessage(BaseComponent... components) {
- for (var component : components)
- sendMessage(component);
- }
-
- @Override
- public void sendMessage(ChatMessageType position, BaseComponent component) {
- sendMessage(component); //Ignore position
- }
-
- @Override
- public void sendMessage(ChatMessageType position, BaseComponent... components) {
- sendMessage(components); //Ignore position
- }
-
- @Override
- public boolean isInvulnerable() {
- return true;
- }
- };
-
- @Override
- public Player.Spigot spigot() {
- return spigot;
- }
-
- public static DiscordConnectedPlayer create(User user, MessageChannel channel, UUID uuid, String mcname,
- MinecraftChatModule 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);
- }
- })
- .stubOnly();
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala
new file mode 100644
index 0000000..e627049
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala
@@ -0,0 +1,251 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule
+import buttondevteam.discordplugin.playerfaker.{DiscordInventory, VCMDWrapper}
+import discord4j.core.`object`.entity.User
+import discord4j.core.`object`.entity.channel.MessageChannel
+import net.md_5.bungee.api.ChatMessageType
+import net.md_5.bungee.api.chat.BaseComponent
+import org.bukkit._
+import org.bukkit.attribute.{Attribute, AttributeInstance, AttributeModifier}
+import org.bukkit.entity.Player.Spigot
+import org.bukkit.entity.{Entity, Player}
+import org.bukkit.event.player.{AsyncPlayerChatEvent, PlayerTeleportEvent}
+import org.bukkit.inventory.{Inventory, PlayerInventory}
+import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
+import org.bukkit.plugin.Plugin
+import org.mockito.Answers.RETURNS_DEFAULTS
+import org.mockito.{MockSettings, Mockito}
+import org.mockito.invocation.InvocationOnMock
+
+import java.lang.reflect.Modifier
+import java.net.InetSocketAddress
+import java.util
+import java.util._
+
+object DiscordConnectedPlayer {
+ def create(user: User, channel: MessageChannel, uuid: UUID, mcname: String, module: MinecraftChatModule): DiscordConnectedPlayer =
+ Mockito.mock(classOf[DiscordConnectedPlayer], getSettings.useConstructor(user, channel, uuid, mcname, module))
+
+ def createTest: DiscordConnectedPlayer =
+ Mockito.mock(classOf[DiscordConnectedPlayer], getSettings.useConstructor(null, null))
+
+ private def getSettings: MockSettings = Mockito.withSettings.defaultAnswer((invocation: InvocationOnMock) => {
+ def foo(invocation: InvocationOnMock): AnyRef =
+ try {
+ if (!Modifier.isAbstract(invocation.getMethod.getModifiers))
+ invocation.callRealMethod
+ else if (classOf[PlayerInventory].isAssignableFrom(invocation.getMethod.getReturnType))
+ Mockito.mock(classOf[DiscordInventory], Mockito.withSettings.extraInterfaces(classOf[PlayerInventory]))
+ else if (classOf[Inventory].isAssignableFrom(invocation.getMethod.getReturnType))
+ new DiscordInventory
+ else
+ RETURNS_DEFAULTS.answer(invocation)
+ } catch {
+ case e: Exception =>
+ System.err.println("Error in mocked player!")
+ e.printStackTrace()
+ RETURNS_DEFAULTS.answer(invocation)
+ }
+
+ foo(invocation)
+ }).stubOnly
+}
+
+abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordConnectedPlayer] {
+ override def isPermissionSet(name: String): Boolean = this.origPerm.isPermissionSet(name)
+
+ override def isPermissionSet(perm: Permission): Boolean = this.origPerm.isPermissionSet(perm)
+
+ override def hasPermission(inName: String): Boolean = this.origPerm.hasPermission(inName)
+
+ override def hasPermission(perm: Permission): Boolean = this.origPerm.hasPermission(perm)
+
+ override def addAttachment(plugin: Plugin, name: String, value: Boolean): PermissionAttachment = this.origPerm.addAttachment(plugin, name, value)
+
+ override def addAttachment(plugin: Plugin): PermissionAttachment = this.origPerm.addAttachment(plugin)
+
+ override def removeAttachment(attachment: PermissionAttachment): Unit = this.origPerm.removeAttachment(attachment)
+
+ override def recalculatePermissions(): Unit = this.origPerm.recalculatePermissions()
+
+ def clearPermissions(): Unit = this.origPerm.clearPermissions()
+
+ override def addAttachment(plugin: Plugin, name: String, value: Boolean, ticks: Int): PermissionAttachment =
+ this.origPerm.addAttachment(plugin, name, value, ticks)
+
+ override def addAttachment(plugin: Plugin, ticks: Int): PermissionAttachment = this.origPerm.addAttachment(plugin, ticks)
+
+ override def getEffectivePermissions: util.Set[PermissionAttachmentInfo] = this.origPerm.getEffectivePermissions
+
+ def setLoggedIn(loggedIn: Boolean): Unit = this.loggedIn = loggedIn
+
+ def setPerm(perm: PermissibleBase): Unit = this.perm = perm
+
+ override def setDisplayName(displayName: String): Unit = this.displayName = displayName
+
+ override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener
+
+ def isLoggedIn: Boolean = this.loggedIn
+
+ override def getName: String = this.name
+
+ def getBasePlayer: OfflinePlayer = this.basePlayer
+
+ def getPerm: PermissibleBase = this.perm
+
+ override def getUniqueId: UUID = this.uniqueId
+
+ override def getDisplayName: String = this.displayName
+
+ private var vanillaCmdListener: VCMDWrapper = null
+ private var loggedIn = false
+ private var origPerm: PermissibleBase = null
+ private var name: String = null
+ private var basePlayer: OfflinePlayer = null
+ private var perm: PermissibleBase = null
+ private var location: Location = null
+ final private var module: MinecraftChatModule = null
+ final private var uniqueId: UUID = null
+ final private var displayName: String = null
+
+ /**
+ * The parameters must match with {@link #create ( User, MessageChannel, UUID, String, MinecraftChatModule)}
+ */
+ def this(user: User, channel: MessageChannel, uuid: UUID, mcname: String, module: MinecraftChatModule) {
+ this(user, channel)
+ location = Bukkit.getWorlds.get(0).getSpawnLocation
+ perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid))
+ origPerm = perm
+ name = mcname
+ this.module = module
+ uniqueId = uuid
+ displayName = mcname
+ vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, module))
+ }
+
+ /**
+ * For testing
+ */
+ def this(user: User, channel: MessageChannel) {
+ this(user, channel)
+ module = null
+ uniqueId = UUID.randomUUID
+ }
+
+ override def setOp(value: Boolean): Unit = { //CraftPlayer-compatible implementation
+ this.origPerm.setOp(value)
+ this.perm.recalculatePermissions()
+ }
+
+ override def isOp: Boolean = this.origPerm.isOp
+
+ override def teleport(location: Location): Boolean = {
+ if (module.allowFakePlayerTeleports.get) this.location = location
+ true
+ }
+
+ def teleport(location: Location, cause: PlayerTeleportEvent.TeleportCause): Boolean = {
+ if (module.allowFakePlayerTeleports.get) this.location = location
+ true
+ }
+
+ override def teleport(destination: Entity): Boolean = {
+ if (module.allowFakePlayerTeleports.get) this.location = destination.getLocation
+ true
+ }
+
+ def teleport(destination: Entity, cause: PlayerTeleportEvent.TeleportCause): Boolean = {
+ if (module.allowFakePlayerTeleports.get) this.location = destination.getLocation
+ true
+ }
+
+ override def getLocation(loc: Location): Location = {
+ 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)
+ }
+ loc
+ }
+
+ override def getServer: Server = Bukkit.getServer
+
+ override def sendRawMessage(message: String): Unit = sendMessage(message)
+
+ override def chat(msg: String): Unit = Bukkit.getPluginManager.callEvent(new AsyncPlayerChatEvent(true, this, msg, new util.HashSet[Player](Bukkit.getOnlinePlayers)))
+
+ override def getWorld: World = Bukkit.getWorlds.get(0)
+
+ override def isOnline = true
+
+ override def getLocation = new Location(getWorld, location.getX, location.getY, location.getZ, location.getYaw, location.getPitch)
+
+ override def getEyeLocation: Location = getLocation
+
+ @deprecated override def getMaxHealth = 20
+
+ override def getPlayer: DiscordConnectedPlayer = this
+
+ override def getAttribute(attribute: Attribute): AttributeInstance = new AttributeInstance() {
+ override def getAttribute: Attribute = attribute
+
+ override def getBaseValue: Double = getDefaultValue
+
+ override def setBaseValue(value: Double): Unit = {
+ }
+
+ override def getModifiers: util.Collection[AttributeModifier] = Collections.emptyList
+
+ override def addModifier(modifier: AttributeModifier): Unit = {
+ }
+
+ override def removeModifier(modifier: AttributeModifier): Unit = {
+ }
+
+ override def getValue: Double = getDefaultValue
+
+ override def getDefaultValue: Double = 20 //Works for max health, should be okay for the rest
+ }
+
+ override def getGameMode = GameMode.SPECTATOR
+
+ //noinspection ScalaDeprecation
+ @SuppressWarnings(Array("deprecation")) final private val spigot: Spigot = new Spigot() {
+ override def getRawAddress: InetSocketAddress = null
+
+ override def playEffect(location: Location, effect: Effect, id: Int, data: Int, offsetX: Float, offsetY: Float, offsetZ: Float, speed: Float, particleCount: Int, radius: Int): Unit = {
+ }
+
+ override def getCollidesWithEntities = false
+
+ override def setCollidesWithEntities(collides: Boolean): Unit = {
+ }
+
+ override def respawn(): Unit = {
+ }
+
+ override def getLocale = "en_us"
+
+ override def getHiddenPlayers: util.Set[Player] = Collections.emptySet
+
+ override def sendMessage(component: BaseComponent): Unit =
+ DiscordConnectedPlayer.super.sendMessage(component.toPlainText)
+
+ override def sendMessage(components: BaseComponent*): Unit =
+ for (component <- components)
+ sendMessage(component)
+
+ override def sendMessage(position: ChatMessageType, component: BaseComponent): Unit =
+ sendMessage(component) //Ignore position
+ override def sendMessage(position: ChatMessageType, components: BaseComponent*) =
+ sendMessage(components)
+
+ override def isInvulnerable = true
+ }
+
+ override def spigot: Spigot = spigot
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java
deleted file mode 100755
index 40ce273..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.lib.player.ChromaGamerBase;
-import buttondevteam.lib.player.UserClass;
-import discord4j.core.object.entity.User;
-import discord4j.core.object.entity.channel.MessageChannel;
-
-@UserClass(foldername = "discord")
-public class DiscordPlayer extends ChromaGamerBase {
- private String did;
- // private @Getter @Setter boolean minecraftChatEnabled;
-
- public DiscordPlayer() {
- }
-
- public String getDiscordID() {
- if (did == null)
- did = getFileName();
- return did;
- }
-
- /**
- * Returns true if player has the private Minecraft chat enabled. For setting the value, see
- * {@link MCChatPrivate#privateMCChat(MessageChannel, boolean, User, DiscordPlayer)}
- */
- public boolean isMinecraftChatEnabled() {
- return MCChatPrivate.isMinecraftChatEnabled(this);
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.scala b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.scala
new file mode 100644
index 0000000..017e12a
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.scala
@@ -0,0 +1,20 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.discordplugin.mcchat.MCChatPrivate
+import buttondevteam.lib.player.{ChromaGamerBase, UserClass}
+
+@UserClass(foldername = "discord") class DiscordPlayer() extends ChromaGamerBase {
+ private var did: String = null
+
+ // private @Getter @Setter boolean minecraftChatEnabled;
+ def getDiscordID: String = {
+ if (did == null) did = getFileName
+ did
+ }
+
+ /**
+ * Returns true if player has the private Minecraft chat enabled. For setting the value, see
+ * {@link MCChatPrivate# privateMCChat ( MessageChannel, boolean, User, DiscordPlayer)}
+ */
+ def isMinecraftChatEnabled: Boolean = MCChatPrivate.isMinecraftChatEnabled(this)
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java
deleted file mode 100755
index b9e7f86..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
-import discord4j.core.object.entity.User;
-import discord4j.core.object.entity.channel.MessageChannel;
-import lombok.Getter;
-import org.bukkit.entity.Player;
-import org.mockito.Mockito;
-
-import java.lang.reflect.Modifier;
-
-public abstract class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer {
-
- protected Player player;
- private @Getter final VCMDWrapper vanillaCmdListener;
-
- public DiscordPlayerSender(User user, MessageChannel channel, Player player, MinecraftChatModule module) {
- super(user, channel);
- this.player = player;
- vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, player, module));
- }
-
- @Override
- public void sendMessage(String message) {
- player.sendMessage(message);
- super.sendMessage(message);
- }
-
- @Override
- public void sendMessage(String[] messages) {
- player.sendMessage(messages);
- super.sendMessage(messages);
- }
-
- public static DiscordPlayerSender create(User user, MessageChannel channel, Player player, MinecraftChatModule module) {
- return Mockito.mock(DiscordPlayerSender.class, Mockito.withSettings().stubOnly().defaultAnswer(invocation -> {
- if (!Modifier.isAbstract(invocation.getMethod().getModifiers()))
- return invocation.callRealMethod();
- return invocation.getMethod().invoke(((DiscordPlayerSender) invocation.getMock()).player, invocation.getArguments());
- }).useConstructor(user, channel, player, module));
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.scala b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.scala
new file mode 100644
index 0000000..5994da7
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.scala
@@ -0,0 +1,41 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule
+import buttondevteam.discordplugin.playerfaker.VCMDWrapper
+import discord4j.core.`object`.entity.User
+import discord4j.core.`object`.entity.channel.MessageChannel
+import org.bukkit.entity.Player
+import org.mockito.Mockito
+import org.mockito.invocation.InvocationOnMock
+
+import java.lang.reflect.Modifier
+
+object DiscordPlayerSender {
+ def create(user: User, channel: MessageChannel, player: Player, module: MinecraftChatModule): DiscordPlayerSender =
+ Mockito.mock(classOf[DiscordPlayerSender], Mockito.withSettings.stubOnly.defaultAnswer((invocation: InvocationOnMock) => {
+ def foo(invocation: InvocationOnMock): AnyRef = {
+ if (!Modifier.isAbstract(invocation.getMethod.getModifiers))
+ invocation.callRealMethod
+ else
+ invocation.getMethod.invoke(invocation.getMock.asInstanceOf[DiscordPlayerSender].player, invocation.getArguments)
+ }
+
+ foo(invocation)
+ }).useConstructor(user, channel, player, module))
+}
+
+abstract class DiscordPlayerSender(val user: User, val channel: MessageChannel, var player: Player, val module: Nothing) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordPlayerSender] {
+ val vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, player, module))
+
+ override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener
+
+ override def sendMessage(message: String): Unit = {
+ player.sendMessage(message)
+ super.sendMessage(message)
+ }
+
+ override def sendMessage(messages: Array[String]): Unit = {
+ player.sendMessage(messages)
+ super.sendMessage(messages)
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.java b/src/main/java/buttondevteam/discordplugin/DiscordSender.java
deleted file mode 100755
index 0ef62a9..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordSender.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package buttondevteam.discordplugin;
-
-import discord4j.core.object.entity.Member;
-import discord4j.core.object.entity.User;
-import discord4j.core.object.entity.channel.MessageChannel;
-import lombok.val;
-import org.bukkit.Bukkit;
-import org.bukkit.Server;
-import org.bukkit.command.CommandSender;
-import org.bukkit.permissions.PermissibleBase;
-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;
-
-public class DiscordSender extends DiscordSenderBase implements CommandSender {
- private PermissibleBase perm = new PermissibleBase(this);
-
- private String name;
-
- public DiscordSender(User user, MessageChannel channel) {
- super(user, channel);
- val def = "Discord user";
- 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) {
- super(user, channel);
- this.name = name;
- }
-
- @Override
- public boolean isPermissionSet(String name) {
- return perm.isPermissionSet(name);
- }
-
- @Override
- public boolean isPermissionSet(Permission perm) {
- return this.perm.isPermissionSet(perm);
- }
-
- @Override
- public boolean hasPermission(String name) {
- if (name.contains("essentials") && !name.equals("essentials.list"))
- return false;
- return perm.hasPermission(name);
- }
-
- @Override
- public boolean hasPermission(Permission perm) {
- return this.perm.hasPermission(perm);
- }
-
- @Override
- public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) {
- return perm.addAttachment(plugin, name, value);
- }
-
- @Override
- public PermissionAttachment addAttachment(Plugin plugin) {
- return perm.addAttachment(plugin);
- }
-
- @Override
- public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) {
- return perm.addAttachment(plugin, name, value, ticks);
- }
-
- @Override
- public PermissionAttachment addAttachment(Plugin plugin, int ticks) {
- return perm.addAttachment(plugin, ticks);
- }
-
- @Override
- public void removeAttachment(PermissionAttachment attachment) {
- perm.removeAttachment(attachment);
- }
-
- @Override
- public void recalculatePermissions() {
- perm.recalculatePermissions();
- }
-
- @Override
- public Set getEffectivePermissions() {
- return perm.getEffectivePermissions();
- }
-
- @Override
- public boolean isOp() {
- return false;
- }
-
- @Override
- public void setOp(boolean value) {
- }
-
- @Override
- public Server getServer() {
- return Bukkit.getServer();
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public Spigot spigot() {
- return new CommandSender.Spigot();
- }
-
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.scala b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala
new file mode 100644
index 0000000..5e9c038
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala
@@ -0,0 +1,64 @@
+package buttondevteam.discordplugin
+
+import discord4j.core.`object`.entity.User
+import discord4j.core.`object`.entity.channel.MessageChannel
+import org.bukkit.{Bukkit, Server}
+import org.bukkit.command.CommandSender
+import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
+import org.bukkit.plugin.Plugin
+import reactor.core.publisher.Mono
+
+import java.util
+
+class DiscordSender(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with CommandSender {
+ private val perm = new PermissibleBase(this)
+ private var name: String = null
+
+ def this(user: User, channel: MessageChannel) {
+ this(user, channel)
+ val `def` = "Discord user"
+ name = if (user == null) `def`
+ else user.asMember(DiscordPlugin.mainServer.getId).onErrorResume((_: Throwable) => Mono.empty).blockOptional.map(_.getDisplayName).orElse(`def`)
+ }
+
+ def this(user: User, channel: MessageChannel, name: String) {
+ this(user, channel)
+ this.name = name
+ }
+
+ override def isPermissionSet(name: String): Boolean = perm.isPermissionSet(name)
+
+ override def isPermissionSet(perm: Permission): Boolean = this.perm.isPermissionSet(perm)
+
+ override def hasPermission(name: String): Boolean = {
+ if (name.contains("essentials") && !(name == "essentials.list")) return false
+ perm.hasPermission(name)
+ }
+
+ override def hasPermission(perm: Permission): Boolean = this.perm.hasPermission(perm)
+
+ override def addAttachment(plugin: Plugin, name: String, value: Boolean): PermissionAttachment = perm.addAttachment(plugin, name, value)
+
+ override def addAttachment(plugin: Plugin): PermissionAttachment = perm.addAttachment(plugin)
+
+ override def addAttachment(plugin: Plugin, name: String, value: Boolean, ticks: Int): PermissionAttachment = perm.addAttachment(plugin, name, value, ticks)
+
+ override def addAttachment(plugin: Plugin, ticks: Int): PermissionAttachment = perm.addAttachment(plugin, ticks)
+
+ override def removeAttachment(attachment: PermissionAttachment): Unit = perm.removeAttachment(attachment)
+
+ override def recalculatePermissions(): Unit = perm.recalculatePermissions()
+
+ override def getEffectivePermissions: util.Set[PermissionAttachmentInfo] = perm.getEffectivePermissions
+
+ override def isOp = false
+
+ override def setOp(value: Boolean): Unit = {
+ }
+
+ override def getServer: Server = Bukkit.getServer
+
+ override def getName: String = name
+
+ override def spigot = new CommandSender.Spigot
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java
deleted file mode 100755
index 7ada97f..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.lib.TBMCCoreAPI;
-import discord4j.core.object.entity.User;
-import discord4j.core.object.entity.channel.MessageChannel;
-import org.bukkit.Bukkit;
-import org.bukkit.command.CommandSender;
-import org.bukkit.scheduler.BukkitTask;
-
-public abstract class DiscordSenderBase implements CommandSender {
- /**
- * May be null.
- */
- protected User user;
- protected MessageChannel channel;
-
- protected DiscordSenderBase(User user, MessageChannel channel) {
- this.user = user;
- this.channel = channel;
- }
-
- private volatile String msgtosend = "";
- private volatile BukkitTask sendtask;
-
- /**
- * Returns the user. May be null.
- *
- * @return The user or null.
- */
- public User getUser() {
- return user;
- }
-
- public MessageChannel getChannel() {
- return channel;
- }
-
- private DiscordPlayer chromaUser;
-
- /**
- * Loads the user data on first query.
- *
- * @return A Chroma user of Discord or a Discord user of Chroma
- */
- public DiscordPlayer getChromaUser() {
- if (chromaUser == null) chromaUser = DiscordPlayer.getUser(user.getId().asString(), DiscordPlayer.class);
- return chromaUser;
- }
-
- @Override
- public void sendMessage(String message) {
- try {
- final boolean broadcast = new Exception().getStackTrace()[2].getMethodName().contains("broadcast");
- if (broadcast) //We're catching broadcasts using the Bukkit event
- return;
- final String sendmsg = DPUtils.sanitizeString(message);
- synchronized (this) {
- msgtosend += "\n" + sendmsg;
- if (sendtask == null)
- sendtask = Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
- channel.createMessage((user != null ? user.getMention() + "\n" : "") + msgtosend.trim()).subscribe();
- sendtask = null;
- msgtosend = "";
- }, 4); // Waits a 0.2 second to gather all/most of the different messages
- }
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while sending message to DiscordSender", e, DiscordPlugin.plugin);
- }
- }
-
- @Override
- public void sendMessage(String[] messages) {
- sendMessage(String.join("\n", messages));
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala
new file mode 100644
index 0000000..83121aa
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala
@@ -0,0 +1,64 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.lib.TBMCCoreAPI
+import buttondevteam.lib.player.ChromaGamerBase
+import discord4j.core.`object`.entity.User
+import discord4j.core.`object`.entity.channel.MessageChannel
+import org.bukkit.Bukkit
+import org.bukkit.command.CommandSender
+import org.bukkit.scheduler.BukkitTask
+
+/**
+ *
+ * @param user May be null.
+ * @param channel May not be null.
+ */
+abstract class DiscordSenderBase protected(var user: User, var channel: MessageChannel) extends CommandSender {
+ private var msgtosend = ""
+ private var sendtask: BukkitTask = null
+
+ /**
+ * Returns the user. May be null.
+ *
+ * @return The user or null.
+ */
+ def getUser: User = user
+
+ def getChannel: MessageChannel = channel
+
+ private var chromaUser: DiscordPlayer = null
+
+ /**
+ * Loads the user data on first query.
+ *
+ * @return A Chroma user of Discord or a Discord user of Chroma
+ */
+ def getChromaUser: DiscordPlayer = {
+ if (chromaUser == null) chromaUser = ChromaGamerBase.getUser(user.getId.asString, classOf[DiscordPlayer])
+ chromaUser
+ }
+
+ override def sendMessage(message: String): Unit = try {
+ val broadcast = new Exception().getStackTrace()(2).getMethodName.contains("broadcast")
+ if (broadcast) { //We're catching broadcasts using the Bukkit event
+ return
+ }
+ val sendmsg = DPUtils.sanitizeString(message)
+ this synchronized msgtosend += "\n" + sendmsg
+ if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => {
+ def foo(): Unit = {
+ channel.createMessage((if (user != null) user.getMention + "\n"
+ else "") + msgtosend.trim).subscribe
+ sendtask = null
+ msgtosend = ""
+ }
+
+ foo()
+ }, 4) // Waits a 0.2 second to gather all/most of the different messages
+ } catch {
+ case e: Exception =>
+ TBMCCoreAPI.SendException("An error occured while sending message to DiscordSender", e, DiscordPlugin.plugin)
+ }
+
+ override def sendMessage(messages: Array[String]): Unit = sendMessage(String.join("\n", messages))
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java b/src/main/java/buttondevteam/discordplugin/IMCPlayer.java
deleted file mode 100755
index c2ee28e..0000000
--- a/src/main/java/buttondevteam/discordplugin/IMCPlayer.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.discordplugin.playerfaker.VCMDWrapper;
-import org.bukkit.entity.Player;
-
-public interface IMCPlayer extends Player {
- VCMDWrapper getVanillaCmdListener();
-}
diff --git a/src/main/java/buttondevteam/discordplugin/IMCPlayer.scala b/src/main/java/buttondevteam/discordplugin/IMCPlayer.scala
new file mode 100644
index 0000000..70f1a5d
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/IMCPlayer.scala
@@ -0,0 +1,8 @@
+package buttondevteam.discordplugin
+
+import buttondevteam.discordplugin.playerfaker.VCMDWrapper
+import org.bukkit.entity.Player
+
+trait IMCPlayer[T] extends Player {
+ def getVanillaCmdListener: VCMDWrapper
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala
index e06a34d..7eaae1e 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala
@@ -1,6 +1,7 @@
package buttondevteam.discordplugin.mcchat
import buttondevteam.core.{ComponentManager, MainPlugin, component}
+import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin._
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule
import buttondevteam.discordplugin.mcchat.MCChatCustom.CustomLMD
@@ -105,14 +106,14 @@ object MCChatUtils {
def getSender[T <: DiscordSenderBase](senders: ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = {
val map = senders.get(user.getId.asString)
- if (map != null) return map.get(channel)
- null
+ if (map != null) map.get(channel)
+ else null.asInstanceOf
}
def removeSender[T <: DiscordSenderBase](senders: ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = {
val map = senders.get(user.getId.asString)
- if (map != null) return map.remove(channel)
- null
+ if (map != null) map.remove(channel)
+ else null.asInstanceOf
}
def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = {
@@ -154,7 +155,7 @@ object MCChatUtils {
if (notEnabled) return Mono.empty
val st = MCChatCustom.lastmsgCustom.stream.filter((clmd) => {
def foo(clmd: CustomLMD): Boolean = { //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
- if (toggle != null && ((clmd.toggles & toggle.flag) eq 0)) return false //If null then allow
+ if (toggle != null && ((clmd.toggles & (1 << toggle.id)) eq 0)) return false //If null then allow
if (sender == null) return true
clmd.groupID.equals(clmd.mcchannel.getGroupID(sender))
}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java
deleted file mode 100644
index e4cd17d..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.DPUtils;
-import buttondevteam.discordplugin.DiscordPlayer;
-import buttondevteam.discordplugin.DiscordPlugin;
-import buttondevteam.discordplugin.DiscordSenderBase;
-import buttondevteam.discordplugin.util.DPState;
-import buttondevteam.lib.chat.Command2;
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.chat.ICommand2MC;
-import buttondevteam.lib.player.ChromaGamerBase;
-import buttondevteam.lib.player.TBMCPlayer;
-import buttondevteam.lib.player.TBMCPlayerBase;
-import org.bukkit.Bukkit;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-import reactor.core.publisher.Mono;
-
-import java.lang.reflect.Method;
-
-@CommandClass(path = "discord", helpText = {
- "Discord",
- "This command allows performing Discord-related actions."
-})
-public class DiscordMCCommand extends ICommand2MC {
- @Command2.Subcommand
- public boolean accept(Player player) {
- if (checkSafeMode(player)) return true;
- String did = ConnectCommand.WaitingToConnect.get(player.getName());
- if (did == null) {
- player.sendMessage("§cYou don't have a pending connection to Discord.");
- return true;
- }
- DiscordPlayer dp = ChromaGamerBase.getUser(did, DiscordPlayer.class);
- TBMCPlayer mcp = TBMCPlayerBase.getPlayer(player.getUniqueId(), TBMCPlayer.class);
- dp.connectWith(mcp);
- ConnectCommand.WaitingToConnect.remove(player.getName());
- MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
- player.sendMessage("§bAccounts connected.");
- return true;
- }
-
- @Command2.Subcommand
- public boolean decline(Player player) {
- if (checkSafeMode(player)) return true;
- String did = ConnectCommand.WaitingToConnect.remove(player.getName());
- if (did == null) {
- player.sendMessage("§cYou don't have a pending connection to Discord.");
- return true;
- }
- player.sendMessage("§bPending connection declined.");
- return true;
- }
-
- @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
- "Reload Discord plugin",
- "Reloads the config. To apply some changes, you may need to also run /discord restart."
- })
- public void reload(CommandSender sender) {
- if (DiscordPlugin.plugin.tryReloadConfig())
- sender.sendMessage("§bConfig reloaded.");
- else
- sender.sendMessage("§cFailed to reload config.");
- }
-
- @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
- "Restart the plugin", //
- "This command disables and then enables the plugin." //
- })
- public void restart(CommandSender sender) {
- Runnable task = () -> {
- if (!DiscordPlugin.plugin.tryReloadConfig()) {
- sender.sendMessage("§cFailed to reload config so not restarting. Check the console.");
- return;
- }
- MinecraftChatModule.state = DPState.RESTARTING_PLUGIN; //Reset in MinecraftChatModule
- sender.sendMessage("§bDisabling DiscordPlugin...");
- Bukkit.getPluginManager().disablePlugin(DiscordPlugin.plugin);
- if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
- sender.sendMessage("§bEnabling DiscordPlugin...");
- Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin);
- if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
- sender.sendMessage("§bRestart finished!");
- };
- if (!Bukkit.getName().equals("Paper")) {
- getPlugin().getLogger().warning("Async plugin events are not supported by the server, running on main thread");
- Bukkit.getScheduler().runTask(DiscordPlugin.plugin, task);
- } else
- Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, task);
- }
-
- @Command2.Subcommand(helpText = {
- "Version command",
- "Prints the plugin version"
- })
- public void version(CommandSender sender) {
- sender.sendMessage(VersionCommand.getVersion());
- }
-
- @Command2.Subcommand(helpText = {
- "Invite",
- "Shows an invite link to the server"
- })
- public void invite(CommandSender sender) {
- if (checkSafeMode(sender)) return;
- String invi = DiscordPlugin.plugin.inviteLink.get();
- if (invi.length() > 0) {
- sender.sendMessage("§bInvite link: " + invi);
- return;
- }
- DiscordPlugin.mainServer.getInvites().limitRequest(1)
- .switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("§cNo invites found for the server.")))
- .subscribe(inv -> sender.sendMessage("§bInvite link: https://discord.gg/" + inv.getCode()),
- e -> sender.sendMessage("§cThe invite link is not set and the bot has no permission to get it."));
- }
-
- @Override
- public String[] getHelpText(Method method, Command2.Subcommand ann) {
- switch (method.getName()) {
- case "accept":
- return new String[]{ //
- "Accept Discord connection", //
- "Accept a pending connection between your Discord and Minecraft account.", //
- "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
- };
- case "decline":
- return new String[]{ //
- "Decline Discord connection", //
- "Decline a pending connection between your Discord and Minecraft account.", //
- "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
- };
- default:
- return super.getHelpText(method, ann);
- }
- }
-
- private boolean checkSafeMode(CommandSender sender) {
- if (DiscordPlugin.SafeMode) {
- sender.sendMessage("§cThe plugin isn't initialized. Check console for details.");
- return true;
- }
- return false;
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala
new file mode 100644
index 0000000..76cb49d
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala
@@ -0,0 +1,128 @@
+package buttondevteam.discordplugin.mccommands
+
+import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin, DiscordSenderBase}
+import buttondevteam.discordplugin.commands.{ConnectCommand, VersionCommand}
+import buttondevteam.discordplugin.mcchat.{MCChatUtils, MinecraftChatModule}
+import buttondevteam.discordplugin.util.DPState
+import buttondevteam.lib.chat.{Command2, CommandClass, ICommand2MC}
+import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer, TBMCPlayerBase}
+import discord4j.core.`object`.ExtendedInvite
+import org.bukkit.Bukkit
+import org.bukkit.command.CommandSender
+import org.bukkit.entity.Player
+import reactor.core.publisher.Mono
+
+import java.lang.reflect.Method
+
+@CommandClass(path = "discord", helpText = Array(Array(
+ "Discord",
+ "This command allows performing Discord-related actions."
+))) class DiscordMCCommand extends ICommand2MC {
+ @Command2.Subcommand def accept(player: Player): Boolean = {
+ if (checkSafeMode(player)) return true
+ val did = ConnectCommand.WaitingToConnect.get(player.getName)
+ if (did == null) {
+ player.sendMessage("§cYou don't have a pending connection to Discord.")
+ return true
+ }
+ val dp = ChromaGamerBase.getUser(did, classOf[DiscordPlayer])
+ val mcp = TBMCPlayerBase.getPlayer(player.getUniqueId, classOf[TBMCPlayer])
+ dp.connectWith(mcp)
+ ConnectCommand.WaitingToConnect.remove(player.getName)
+ MCChatUtils.UnconnectedSenders.remove(did) //Remove all unconnected, will be recreated where needed
+ player.sendMessage("§bAccounts connected.")
+ true
+ }
+
+ @Command2.Subcommand def decline(player: Player): Boolean = {
+ if (checkSafeMode(player)) return true
+ val did = ConnectCommand.WaitingToConnect.remove(player.getName)
+ if (did == null) {
+ player.sendMessage("§cYou don't have a pending connection to Discord.")
+ return true
+ }
+ player.sendMessage("§bPending connection declined.")
+ true
+ }
+
+ @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = Array(Array(
+ "Reload Discord plugin",
+ "Reloads the config. To apply some changes, you may need to also run /discord restart."
+ ))) def reload(sender: CommandSender): Unit =
+ if (DiscordPlugin.plugin.tryReloadConfig) sender.sendMessage("§bConfig reloaded.")
+ else sender.sendMessage("§cFailed to reload config.")
+
+ @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = Array(Array(
+ "Restart the plugin", //
+ "This command disables and then enables the plugin." //
+ ))) def restart(sender: CommandSender): Unit = {
+ val task: Runnable = () => {
+ def foo(): Unit = {
+ if (!DiscordPlugin.plugin.tryReloadConfig) {
+ sender.sendMessage("§cFailed to reload config so not restarting. Check the console.")
+ return
+ }
+ MinecraftChatModule.state = DPState.RESTARTING_PLUGIN //Reset in MinecraftChatModule
+ sender.sendMessage("§bDisabling DiscordPlugin...")
+ Bukkit.getPluginManager.disablePlugin(DiscordPlugin.plugin)
+ if (!sender.isInstanceOf[DiscordSenderBase]) { //Sending to Discord errors
+ sender.sendMessage("§bEnabling DiscordPlugin...")
+ }
+ Bukkit.getPluginManager.enablePlugin(DiscordPlugin.plugin)
+ if (!sender.isInstanceOf[DiscordSenderBase]) sender.sendMessage("§bRestart finished!")
+ }
+
+ foo()
+ }
+
+ if (!(Bukkit.getName == "Paper")) {
+ getPlugin.getLogger.warning("Async plugin events are not supported by the server, running on main thread")
+ Bukkit.getScheduler.runTask(DiscordPlugin.plugin, task)
+ }
+ else {
+ Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, task)
+ }
+ }
+
+ @Command2.Subcommand(helpText = Array(Array(
+ "Version command",
+ "Prints the plugin version"))) def version(sender: CommandSender): Unit = {
+ sender.sendMessage(VersionCommand.getVersion)
+ }
+
+ @Command2.Subcommand(helpText = Array(Array(
+ "Invite",
+ "Shows an invite link to the server"
+ ))) def invite(sender: CommandSender): Unit = {
+ if (checkSafeMode(sender)) {
+ return
+ }
+ val invi: String = DiscordPlugin.plugin.inviteLink.get
+ if (invi.nonEmpty) {
+ sender.sendMessage("§bInvite link: " + invi)
+ return
+ }
+ DiscordPlugin.mainServer.getInvites.limitRequest(1)
+ .switchIfEmpty(Mono.fromRunnable(() => sender.sendMessage("§cNo invites found for the server.")))
+ .subscribe((inv: ExtendedInvite) => sender.sendMessage("§bInvite link: https://discord.gg/" + inv.getCode), _ => sender.sendMessage("§cThe invite link is not set and the bot has no permission to get it."))
+ }
+
+ override def getHelpText(method: Method, ann: Command2.Subcommand): Array[String] = {
+ method.getName match {
+ case "accept" =>
+ Array[String]("Accept Discord connection", "Accept a pending connection between your Discord and Minecraft account.", "To start the connection process, do §b/connect §r in the " + DPUtils.botmention + " channel on Discord")
+ case "decline" =>
+ Array[String]("Decline Discord connection", "Decline a pending connection between your Discord and Minecraft account.", "To start the connection process, do §b/connect §r in the " + DPUtils.botmention + " channel on Discord")
+ case _ =>
+ super.getHelpText(method, ann)
+ }
+ }
+
+ private def checkSafeMode(sender: CommandSender): Boolean = {
+ if (DiscordPlugin.SafeMode) {
+ sender.sendMessage("§cThe plugin isn't initialized. Check console for details.")
+ true
+ }
+ else false
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.java
deleted file mode 100644
index 116cade..0000000
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package buttondevteam.discordplugin.playerfaker;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.experimental.Delegate;
-import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker;
-import org.mockito.plugins.MockMaker;
-
-public class DelegatingMockMaker implements MockMaker {
- @Getter
- @Setter
- @Delegate
- private MockMaker mockMaker = new SubclassByteBuddyMockMaker();
- @Getter
- private static DelegatingMockMaker instance;
-
- public DelegatingMockMaker() {
- instance = this;
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.scala b/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.scala
new file mode 100644
index 0000000..0e14a89
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DelegatingMockMaker.scala
@@ -0,0 +1,49 @@
+package buttondevteam.discordplugin.playerfaker
+
+import org.mockito.MockedConstruction
+import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker
+import org.mockito.invocation.MockHandler
+import org.mockito.mock.MockCreationSettings
+import org.mockito.plugins.MockMaker
+
+import java.util.Optional
+
+object DelegatingMockMaker {
+ def getInstance: DelegatingMockMaker = DelegatingMockMaker.instance
+
+ private var instance: DelegatingMockMaker = null
+}
+
+class DelegatingMockMaker() extends MockMaker {
+ DelegatingMockMaker.instance = this
+
+ override def createMock[T](settings: MockCreationSettings[T], handler: MockHandler[_]): T =
+ this.mockMaker.createMock(settings, handler)
+
+ override def createSpy[T](settings: MockCreationSettings[T], handler: MockHandler[_], instance: T): Optional[T] =
+ this.mockMaker.createSpy(settings, handler, instance)
+
+ override def getHandler(mock: Any): MockHandler[_] =
+ this.mockMaker.getHandler(mock)
+
+ override def resetMock(mock: Any, newHandler: MockHandler[_], settings: MockCreationSettings[_]): Unit = {
+ this.mockMaker.resetMock(mock, newHandler, settings)
+ }
+
+ override def isTypeMockable(`type`: Class[_]): MockMaker.TypeMockability =
+ this.mockMaker.isTypeMockable(`type`)
+
+ override def createStaticMock[T](`type`: Class[T], settings: MockCreationSettings[T], handler: MockHandler[_]): MockMaker.StaticMockControl[T] =
+ this.mockMaker.createStaticMock(`type`, settings, handler)
+
+ override def createConstructionMock[T](`type`: Class[T], settingsFactory: Function[MockedConstruction.Context, MockCreationSettings[T]], handlerFactory: Function[MockedConstruction.Context, MockHandler[T]], mockInitializer: MockedConstruction.MockInitializer[T]): MockMaker.ConstructionMockControl[T] =
+ this.mockMaker.createConstructionMock[T](`type`, settingsFactory: Function[MockedConstruction.Context, MockCreationSettings[T]], handlerFactory, mockInitializer)
+
+ def setMockMaker(mockMaker: MockMaker): Unit = {
+ this.mockMaker = mockMaker
+ }
+
+ def getMockMaker: MockMaker = this.mockMaker
+
+ private var mockMaker: MockMaker = new SubclassByteBuddyMockMaker
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.java b/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.java
deleted file mode 100644
index bf3051e..0000000
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package buttondevteam.discordplugin.playerfaker;
-
-import com.destroystokyo.paper.profile.CraftPlayerProfile;
-import lombok.RequiredArgsConstructor;
-import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding;
-import org.bukkit.Bukkit;
-import org.bukkit.Server;
-import org.bukkit.entity.Player;
-import org.mockito.Mockito;
-import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker;
-
-import java.lang.reflect.Modifier;
-import java.util.*;
-
-public class ServerWatcher {
- private List playerList;
- public final List fakePlayers = new ArrayList<>();
- private Server origServer;
-
- @IgnoreForBinding
- public void enableDisable(boolean enable) throws Exception {
- var serverField = Bukkit.class.getDeclaredField("server");
- serverField.setAccessible(true);
- if (enable) {
- var serverClass = Bukkit.getServer().getClass();
- var originalServer = serverField.get(null);
- DelegatingMockMaker.getInstance().setMockMaker(new InlineByteBuddyMockMaker());
- var settings = Mockito.withSettings().stubOnly()
- .defaultAnswer(invocation -> {
- var method = invocation.getMethod();
- int pc = method.getParameterCount();
- Player player = null;
- switch (method.getName()) {
- case "getPlayer":
- if (pc == 1 && method.getParameterTypes()[0] == UUID.class)
- player = MCChatUtils.LoggedInPlayers.get(invocation.getArgument(0));
- break;
- case "getPlayerExact":
- if (pc == 1) {
- final String argument = invocation.getArgument(0);
- player = MCChatUtils.LoggedInPlayers.values().stream()
- .filter(dcp -> dcp.getName().equalsIgnoreCase(argument)).findAny().orElse(null);
- }
- break;
- /*case "getOnlinePlayers":
- if (playerList == null) {
- @SuppressWarnings("unchecked") var list = (List) method.invoke(origServer, invocation.getArguments());
- playerList = new AppendListView<>(list, fakePlayers);
- } - Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
- return playerList;*/
- case "createProfile": //Paper's method, casts the player to a CraftPlayer
- if (pc == 2) {
- UUID uuid = invocation.getArgument(0);
- String name = invocation.getArgument(1);
- player = uuid != null ? MCChatUtils.LoggedInPlayers.get(uuid) : null;
- if (player == null && name != null)
- player = MCChatUtils.LoggedInPlayers.values().stream()
- .filter(dcp -> dcp.getName().equalsIgnoreCase(name)).findAny().orElse(null);
- if (player != null)
- return new CraftPlayerProfile(player.getUniqueId(), player.getName());
- }
- break;
- }
- if (player != null)
- return player;
- return method.invoke(origServer, invocation.getArguments());
- });
- //var mock = mockMaker.createMock(settings, MockHandlerFactory.createMockHandler(settings));
- //thread.setContextClassLoader(cl);
- var mock = Mockito.mock(serverClass, settings);
- for (var field : serverClass.getFields()) //Copy public fields, private fields aren't accessible directly anyways
- if (!Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()))
- field.set(mock, field.get(originalServer));
- serverField.set(null, mock);
- origServer = (Server) originalServer;
- } else if (origServer != null)
- serverField.set(null, origServer);
- }
-
- @RequiredArgsConstructor
- public static class AppendListView extends AbstractSequentialList {
- private final List originalList;
- private final List additionalList;
-
- @Override
- public ListIterator listIterator(int i) {
- int os = originalList.size();
- return i < os ? originalList.listIterator(i) : additionalList.listIterator(i - os);
- }
-
- @Override
- public int size() {
- return originalList.size() + additionalList.size();
- }
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.scala b/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.scala
new file mode 100644
index 0000000..ceb01b8
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/ServerWatcher.scala
@@ -0,0 +1,91 @@
+package buttondevteam.discordplugin.playerfaker
+
+import buttondevteam.discordplugin.DiscordConnectedPlayer
+import buttondevteam.discordplugin.mcchat.MCChatUtils
+import com.destroystokyo.paper.profile.CraftPlayerProfile
+import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding
+import org.bukkit.{Bukkit, Server}
+import org.bukkit.entity.Player
+import org.mockito.Mockito
+import org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker
+import org.mockito.invocation.InvocationOnMock
+
+import java.lang.reflect.Modifier
+import java.util
+import java.util._
+
+object ServerWatcher {
+
+ class AppendListView[T](private val originalList: java.util.List[T], private val additionalList: java.util.List[T]) extends java.util.AbstractSequentialList[T] {
+
+ override def listIterator(i: Int): util.ListIterator[T] = {
+ val os = originalList.size
+ if (i < os) originalList.listIterator(i)
+ else additionalList.listIterator(i - os)
+ }
+
+ override def size: Int = originalList.size + additionalList.size
+ }
+
+}
+
+class ServerWatcher {
+ final val fakePlayers = new util.ArrayList[Player]
+ private var origServer: Server = null
+
+ @IgnoreForBinding
+ @throws[Exception]
+ def enableDisable(enable: Boolean): Unit = {
+ val serverField = classOf[Bukkit].getDeclaredField("server")
+ serverField.setAccessible(true)
+ if (enable) {
+ val serverClass = Bukkit.getServer.getClass
+ val originalServer = serverField.get(null)
+ DelegatingMockMaker.getInstance.setMockMaker(new InlineByteBuddyMockMaker)
+ val settings = Mockito.withSettings.stubOnly.defaultAnswer((invocation: InvocationOnMock) => {
+ def foo(invocation: InvocationOnMock): AnyRef = {
+ val method = invocation.getMethod
+ val pc = method.getParameterCount
+ var player: DiscordConnectedPlayer = null
+ method.getName match {
+ case "getPlayer" =>
+ if (pc == 1 && (method.getParameterTypes()(0) eq classOf[UUID])) player = MCChatUtils.LoggedInPlayers.get(invocation.getArgument[UUID](0))
+ case "getPlayerExact" =>
+ if (pc == 1) {
+ val argument = invocation.getArgument(0)
+ player = MCChatUtils.LoggedInPlayers.values.stream.filter((dcp) => dcp.getName.equalsIgnoreCase(argument)).findAny.orElse(null)
+ }
+
+ /*case "getOnlinePlayers":
+ if (playerList == null) {
+ @SuppressWarnings("unchecked") var list = (List) method.invoke(origServer, invocation.getArguments());
+ playerList = new AppendListView<>(list, fakePlayers);
+ } - Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
+ return playerList;*/ case "createProfile" => //Paper's method, casts the player to a CraftPlayer
+ if (pc == 2) {
+ val uuid = invocation.getArgument(0)
+ val name = invocation.getArgument(1)
+ player = if (uuid != null) MCChatUtils.LoggedInPlayers.get(uuid)
+ else null
+ if (player == null && name != null) player = MCChatUtils.LoggedInPlayers.values.stream.filter((dcp) => dcp.getName.equalsIgnoreCase(name)).findAny.orElse(null)
+ if (player != null) return new CraftPlayerProfile(player.getUniqueId, player.getName)
+ }
+ }
+ if (player != null) return player
+ method.invoke(origServer, invocation.getArguments)
+ }
+
+ foo(invocation)
+ })
+ //var mock = mockMaker.createMock(settings, MockHandlerFactory.createMockHandler(settings));
+ //thread.setContextClassLoader(cl);
+ val mock = Mockito.mock(serverClass, settings)
+ for (field <- serverClass.getFields) { //Copy public fields, private fields aren't accessible directly anyways
+ if (!Modifier.isFinal(field.getModifiers) && !Modifier.isStatic(field.getModifiers)) field.set(mock, field.get(originalServer))
+ }
+ serverField.set(null, mock)
+ origServer = originalServer.asInstanceOf[Server]
+ }
+ else if (origServer != null) serverField.set(null, origServer)
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java
index 7727673..6ff856b 100644
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java
@@ -2,6 +2,7 @@ package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.lib.TBMCCoreAPI;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java b/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java
index c13b7dd..055c5e8 100644
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java
@@ -2,6 +2,7 @@ package buttondevteam.discordplugin.playerfaker.perm;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
+import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.lib.TBMCCoreAPI;
import me.lucko.luckperms.bukkit.LPBukkitBootstrap;
import me.lucko.luckperms.bukkit.LPBukkitPlugin;
diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java
deleted file mode 100644
index c1a4079..0000000
--- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package buttondevteam.discordplugin.role;
-
-import buttondevteam.core.ComponentManager;
-import buttondevteam.discordplugin.DPUtils;
-import buttondevteam.discordplugin.DiscordPlugin;
-import buttondevteam.lib.architecture.Component;
-import buttondevteam.lib.architecture.ComponentMetadata;
-import buttondevteam.lib.architecture.ReadOnlyConfigData;
-import discord4j.core.event.domain.role.RoleCreateEvent;
-import discord4j.core.event.domain.role.RoleDeleteEvent;
-import discord4j.core.event.domain.role.RoleEvent;
-import discord4j.core.event.domain.role.RoleUpdateEvent;
-import discord4j.core.object.entity.Role;
-import discord4j.core.object.entity.channel.MessageChannel;
-import discord4j.rest.util.Color;
-import lombok.val;
-import org.bukkit.Bukkit;
-import reactor.core.publisher.Mono;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-/**
- * Automatically collects roles with a certain color.
- * Users can add these roles to themselves using the /role Discord command.
- */
-@ComponentMetadata(enabledByDefault = false)
-public class GameRoleModule extends Component {
- public List GameRoles;
-
- private final RoleCommand command = new RoleCommand(this);
-
- @Override
- protected void enable() {
- getPlugin().getManager().registerCommand(command);
- GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(this::isGameRole).map(Role::getName).collect(Collectors.toList()).block();
- }
-
- @Override
- protected void disable() {
- getPlugin().getManager().unregisterCommand(command);
- }
-
- /**
- * The channel where the bot logs when it detects a role change that results in a new game role or one being removed.
- */
- private final ReadOnlyConfigData> logChannel = DPUtils.channelData(getConfig(), "logChannel");
-
- /**
- * The role color that is used by game roles.
- * Defaults to the second to last in the upper row - #95a5a6.
- */
- private final ReadOnlyConfigData roleColor = getConfig().getConfig("roleColor")
- .def(Color.of(149, 165, 166))
- .getter(rgb -> Color.of(Integer.parseInt(((String) rgb).substring(1), 16)))
- .setter(color -> String.format("#%08x", color.getRGB())).buildReadOnly();
-
- public static void handleRoleEvent(RoleEvent roleEvent) {
- val grm = ComponentManager.getIfEnabled(GameRoleModule.class);
- if (grm == null) return;
- val GameRoles = grm.GameRoles;
- val logChannel = grm.logChannel.get();
- Predicate notMainServer = r -> r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong();
- if (roleEvent instanceof RoleCreateEvent) {
- Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
- Role role = ((RoleCreateEvent) roleEvent).getRole();
- if (notMainServer.test(role))
- return;
- grm.isGameRole(role).flatMap(b -> {
- if (!b)
- return Mono.empty(); //Deleted or not a game role
- GameRoles.add(role.getName());
- if (logChannel != null)
- return logChannel.flatMap(ch -> ch.createMessage("Added " + role.getName() + " as game role. If you don't want this, change the role's color from the game role color."));
- return Mono.empty();
- }).subscribe();
- }, 100);
- } else if (roleEvent instanceof RoleDeleteEvent) {
- Role role = ((RoleDeleteEvent) roleEvent).getRole().orElse(null);
- if (role == null) return;
- if (notMainServer.test(role))
- return;
- if (GameRoles.remove(role.getName()) && logChannel != null)
- logChannel.flatMap(ch -> ch.createMessage("Removed " + role.getName() + " as a game role.")).subscribe();
- } else if (roleEvent instanceof RoleUpdateEvent) {
- val event = (RoleUpdateEvent) roleEvent;
- if (!event.getOld().isPresent()) {
- grm.logWarn("Old role not stored, cannot update game role!");
- return;
- }
- Role or = event.getOld().get();
- if (notMainServer.test(or))
- return;
- grm.isGameRole(event.getCurrent()).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 its color changed."));
- } else {
- if (GameRoles.contains(or.getName()) && or.getName().equals(event.getCurrent().getName()))
- return Mono.empty();
- boolean removed = GameRoles.remove(or.getName()); //Regardless of whether it was a game role
- GameRoles.add(event.getCurrent().getName()); //Add it because it has no color
- if (logChannel != null) {
- if (removed)
- return logChannel.flatMap(ch -> ch.createMessage("Changed game role from " + or.getName() + " to " + event.getCurrent().getName() + "."));
- else
- return logChannel.flatMap(ch -> ch.createMessage("Added " + event.getCurrent().getName() + " as game role because it has the color of one."));
- }
- }
- return Mono.empty();
- }).subscribe();
- }
- }
-
- private Mono isGameRole(Role r) {
- if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong())
- return Mono.just(false); //Only allow on the main server
- val rc = roleColor.get();
- 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.getId())))) //Below one of our roles
- .defaultIfEmpty(false);
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.scala b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.scala
new file mode 100644
index 0000000..95648ec
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.scala
@@ -0,0 +1,107 @@
+package buttondevteam.discordplugin.role
+
+import buttondevteam.core.ComponentManager
+import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
+import buttondevteam.lib.architecture.{Component, ComponentMetadata}
+import discord4j.core.`object`.entity.Role
+import discord4j.core.`object`.entity.channel.MessageChannel
+import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleEvent, RoleUpdateEvent}
+import discord4j.rest.util.Color
+import org.bukkit.Bukkit
+import reactor.core.publisher.Mono
+
+import java.util.Collections
+import java.util.stream.Collectors
+
+/**
+ * Automatically collects roles with a certain color.
+ * Users can add these roles to themselves using the /role Discord command.
+ */
+@ComponentMetadata(enabledByDefault = false) object GameRoleModule {
+ def handleRoleEvent(roleEvent: RoleEvent): Unit = {
+ val grm = ComponentManager.getIfEnabled(classOf[GameRoleModule])
+ if (grm == null) return
+ val GameRoles = grm.GameRoles
+ val logChannel = grm.logChannel.get
+ val notMainServer = (r: Role) => r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong
+ roleEvent match {
+ case roleCreateEvent: RoleCreateEvent => Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => {
+ def foo(): Unit = {
+ val role = roleCreateEvent.getRole
+ if (notMainServer(role)) return
+ grm.isGameRole(role).flatMap((b: Boolean) => {
+ def foo(b: Boolean): Mono[_] = {
+ if (!b) return Mono.empty //Deleted or not a game role
+ GameRoles.add(role.getName)
+ if (logChannel != null) return logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + role.getName + " as game role. If you don't want this, change the role's color from the game role color."))
+ Mono.empty
+ }
+
+ foo(b)
+ }).subscribe
+ }
+
+ foo()
+ }, 100)
+ case roleDeleteEvent: RoleDeleteEvent =>
+ val role = roleDeleteEvent.getRole.orElse(null)
+ if (role == null) return
+ if (notMainServer(role)) return
+ if (GameRoles.remove(role.getName) && logChannel != null) logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Removed " + role.getName + " as a game role.")).subscribe
+ case roleUpdateEvent: RoleUpdateEvent =>
+ if (!roleUpdateEvent.getOld.isPresent) {
+ grm.logWarn("Old role not stored, cannot update game role!")
+ return
+ }
+ val or = roleUpdateEvent.getOld.get
+ if (notMainServer(or)) return
+ val cr = roleUpdateEvent.getCurrent
+ grm.isGameRole(cr).flatMap((b: Boolean) => {
+ def foo(b: Boolean): Mono[_] = {
+ if (!b) if (GameRoles.remove(or.getName) && logChannel != null) return logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Removed " + or.getName + " as a game role because its color changed."))
+ else {
+ if (GameRoles.contains(or.getName) && or.getName == cr.getName) return Mono.empty
+ val removed = GameRoles.remove(or.getName) //Regardless of whether it was a game role
+ GameRoles.add(cr.getName) //Add it because it has no color
+ if (logChannel != null) if (removed) return logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Changed game role from " + or.getName + " to " + cr.getName + "."))
+ else return logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + cr.getName + " as game role because it has the color of one."))
+ }
+ Mono.empty
+ }
+
+ foo(b)
+ }).subscribe
+ case _ =>
+ }
+ }
+}
+
+@ComponentMetadata(enabledByDefault = false) class GameRoleModule extends Component[DiscordPlugin] {
+ var GameRoles: java.util.List[String] = null
+ final private val command = new RoleCommand(this)
+
+ override protected def enable(): Unit = {
+ getPlugin.manager.registerCommand(command)
+ GameRoles = DiscordPlugin.mainServer.getRoles.filterWhen(this.isGameRole _).map(_.getName).collect(Collectors.toList).block
+ }
+
+ override protected def disable(): Unit = getPlugin.manager.unregisterCommand(command)
+
+ /**
+ * The channel where the bot logs when it detects a role change that results in a new game role or one being removed.
+ */
+ final private val logChannel = DPUtils.channelData(getConfig, "logChannel")
+ /**
+ * The role color that is used by game roles.
+ * Defaults to the second to last in the upper row - #95a5a6.
+ */
+ final private val roleColor = getConfig.getConfig[Color]("roleColor").`def`(Color.of(149, 165, 166)).getter((rgb: Any) => Color.of(Integer.parseInt(rgb.asInstanceOf[String].substring(1), 16))).setter((color: Color) => String.format("#%08x", color.getRGB)).buildReadOnly
+
+ private def isGameRole(r: Role): Mono[Boolean] = {
+ if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return Mono.just(false) //Only allow on the main server
+ val rc = roleColor.get
+ if (r.getColor equals rc)
+ DiscordPlugin.dc.getSelf.flatMap((u) => u.asMember(DiscordPlugin.mainServer.getId)).flatMap((m) => m.hasHigherRoles(Collections.singleton(r.getId))).defaultIfEmpty(false) //Below one of our roles
+ else Mono.just(false)
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java
deleted file mode 100755
index aac0aae..0000000
--- a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package buttondevteam.discordplugin.role;
-
-import buttondevteam.discordplugin.DiscordPlugin;
-import buttondevteam.lib.TBMCCoreAPI;
-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;
-
-@CommandClass
-public class RoleCommand extends ICommand2DC {
-
- private GameRoleModule grm;
-
- RoleCommand(GameRoleModule grm) {
- this.grm = grm;
- }
-
- @Command2.Subcommand(helpText = {
- "Add role",
- "This command adds a role to your account."
- })
- public boolean add(Command2DCSender sender, @Command2.TextArg String rolename) {
- final Role role = checkAndGetRole(sender, rolename);
- if (role == null)
- return true;
- try {
- sender.getMessage().getAuthorAsMember()
- .flatMap(m -> m.addRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("added role."))))
- .subscribe();
- } catch (Exception e) {
- TBMCCoreAPI.SendException("Error while adding role!", e, grm);
- sender.sendMessage("an error occured while adding the role.");
- }
- return true;
- }
-
- @Command2.Subcommand(helpText = {
- "Remove role",
- "This command removes a role from your account."
- })
- public boolean remove(Command2DCSender sender, @Command2.TextArg String rolename) {
- final Role role = checkAndGetRole(sender, rolename);
- if (role == null)
- return true;
- try {
- sender.getMessage().getAuthorAsMember()
- .flatMap(m -> m.removeRole(role.getId()).switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("removed role."))))
- .subscribe();
- } catch (Exception e) {
- TBMCCoreAPI.SendException("Error while removing role!", e, grm);
- sender.sendMessage("an error occured while removing the role.");
- }
- return true;
- }
-
- @Command2.Subcommand
- public void list(Command2DCSender sender) {
- var sb = new StringBuilder();
- boolean b = false;
- for (String role : (Iterable) grm.GameRoles.stream().sorted()::iterator) {
- sb.append(role);
- if (!b)
- for (int j = 0; j < Math.max(1, 20 - role.length()); j++)
- sb.append(" ");
- else
- sb.append("\n");
- b = !b;
- }
- if (sb.length() > 0 && sb.charAt(sb.length() - 1) != '\n')
- sb.append('\n');
- sender.sendMessage("list of roles:\n```\n" + sb + "```");
- }
-
- private Role checkAndGetRole(Command2DCSender sender, String rolename) {
- String rname = rolename;
- if (!grm.GameRoles.contains(rolename)) { //If not found as-is, correct case
- val orn = grm.GameRoles.stream().filter(r -> r.equalsIgnoreCase(rolename)).findAny();
- if (!orn.isPresent()) {
- sender.sendMessage("that role cannot be found.");
- list(sender);
- return null;
- }
- rname = orn.get();
- }
- val frname = rname;
- final List roles = DiscordPlugin.mainServer.getRoles().filter(r -> r.getName().equals(frname)).collectList().block();
- if (roles == null) {
- sender.sendMessage("an error occured.");
- return null;
- }
- if (roles.size() == 0) {
- sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.");
- grm.GameRoles.remove(rolename);
- return null;
- }
- if (roles.size() > 1) {
- sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?");
- return null;
- }
- return roles.get(0);
- }
-
-}
diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.scala b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.scala
new file mode 100644
index 0000000..08db583
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.scala
@@ -0,0 +1,84 @@
+package buttondevteam.discordplugin.role
+
+import buttondevteam.discordplugin.DiscordPlugin
+import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC}
+import buttondevteam.lib.TBMCCoreAPI
+import buttondevteam.lib.chat.{Command2, CommandClass}
+import discord4j.core.`object`.entity.Role
+import reactor.core.publisher.Mono
+
+@CommandClass class RoleCommand private[role](var grm: GameRoleModule) extends ICommand2DC {
+ @Command2.Subcommand(helpText = Array(Array(
+ "Add role",
+ "This command adds a role to your account."
+ ))) def add(sender: Command2DCSender, @Command2.TextArg rolename: String): Boolean = {
+ val role = checkAndGetRole(sender, rolename)
+ if (role == null) return true
+ try sender.getMessage.getAuthorAsMember.flatMap(m => m.addRole(role.getId).switchIfEmpty(Mono.fromRunnable(() => sender.sendMessage("added role.")))).subscribe
+ catch {
+ case e: Exception =>
+ TBMCCoreAPI.SendException("Error while adding role!", e, grm)
+ sender.sendMessage("an error occured while adding the role.")
+ }
+ true
+ }
+
+ @Command2.Subcommand(helpText = Array(Array(
+ "Remove role",
+ "This command removes a role from your account."
+ ))) def remove(sender: Command2DCSender, @Command2.TextArg rolename: String): Boolean = {
+ val role = checkAndGetRole(sender, rolename)
+ if (role == null) return true
+ try sender.getMessage.getAuthorAsMember.flatMap(m => m.removeRole(role.getId).switchIfEmpty(Mono.fromRunnable(() => sender.sendMessage("removed role.")))).subscribe
+ catch {
+ case e: Exception =>
+ TBMCCoreAPI.SendException("Error while removing role!", e, grm)
+ sender.sendMessage("an error occured while removing the role.")
+ }
+ true
+ }
+
+ @Command2.Subcommand def list(sender: Command2DCSender): Unit = {
+ val sb = new StringBuilder
+ var b = false
+ for (role <- grm.GameRoles.stream.sorted.iterator.asInstanceOf[Iterable[String]]) {
+ sb.append(role)
+ if (!b) for (_ <- 0 until Math.max(1, 20 - role.length)) {
+ sb.append(" ")
+ }
+ else sb.append("\n")
+ b = !b
+ }
+ if (sb.nonEmpty && sb.charAt(sb.length - 1) != '\n') sb.append('\n')
+ sender.sendMessage("list of roles:\n```\n" + sb + "```")
+ }
+
+ private def checkAndGetRole(sender: Command2DCSender, rolename: String): Role = {
+ var rname = rolename
+ if (!grm.GameRoles.contains(rolename)) { //If not found as-is, correct case
+ val orn = grm.GameRoles.stream.filter(r => r.equalsIgnoreCase(rolename)).findAny
+ if (!orn.isPresent) {
+ sender.sendMessage("that role cannot be found.")
+ list(sender)
+ return null
+ }
+ rname = orn.get
+ }
+ val frname = rname
+ val roles = DiscordPlugin.mainServer.getRoles.filter(r => r.getName.equals(frname)).collectList.block
+ if (roles == null) {
+ sender.sendMessage("an error occured.")
+ return null
+ }
+ if (roles.size == 0) {
+ sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.")
+ grm.GameRoles.remove(rolename)
+ return null
+ }
+ if (roles.size > 1) {
+ sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?")
+ return null
+ }
+ roles.get(0)
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/util/DPState.java b/src/main/java/buttondevteam/discordplugin/util/DPState.java
deleted file mode 100644
index b83d4ac..0000000
--- a/src/main/java/buttondevteam/discordplugin/util/DPState.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package buttondevteam.discordplugin.util;
-
-public enum DPState {
- /**
- * Used from server start until anything else happens
- */
- RUNNING,
- /**
- * Used when /restart is detected
- */
- RESTARTING_SERVER,
- /**
- * Used when the plugin is disabled by outside forces
- */
- STOPPING_SERVER,
- /**
- * Used when /discord restart is run
- */
- RESTARTING_PLUGIN,
- /**
- * Used when the plugin is in the RUNNING state when the chat is disabled
- */
- DISABLED_MCCHAT
-}
diff --git a/src/main/java/buttondevteam/discordplugin/util/DPState.scala b/src/main/java/buttondevteam/discordplugin/util/DPState.scala
new file mode 100644
index 0000000..34bde89
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/util/DPState.scala
@@ -0,0 +1,31 @@
+package buttondevteam.discordplugin.util
+
+object DPState extends Enumeration {
+ type DPState = Value
+ val
+
+ /**
+ * Used from server start until anything else happens
+ */
+ RUNNING,
+
+ /**
+ * Used when /restart is detected
+ */
+ RESTARTING_SERVER,
+
+ /**
+ * Used when the plugin is disabled by outside forces
+ */
+ STOPPING_SERVER,
+
+ /**
+ * Used when /discord restart is run
+ */
+ RESTARTING_PLUGIN,
+
+ /**
+ * Used when the plugin is in the RUNNING state when the chat is disabled
+ */
+ DISABLED_MCCHAT = Value
+}
\ No newline at end of file
diff --git a/src/main/java/buttondevteam/discordplugin/util/Timings.java b/src/main/java/buttondevteam/discordplugin/util/Timings.java
deleted file mode 100644
index af91b0f..0000000
--- a/src/main/java/buttondevteam/discordplugin/util/Timings.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package buttondevteam.discordplugin.util;
-
-public class Timings {
- private long start;
-
- public Timings() {
- start = System.nanoTime();
- }
-
- public void printElapsed(String message) {
- CommonListeners.debug(message + " (" + (System.nanoTime() - start) / 1000000L + ")");
- start = System.nanoTime();
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/util/Timings.scala b/src/main/java/buttondevteam/discordplugin/util/Timings.scala
new file mode 100644
index 0000000..58702a0
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/util/Timings.scala
@@ -0,0 +1,12 @@
+package buttondevteam.discordplugin.util
+
+import buttondevteam.discordplugin.listeners.CommonListeners
+
+class Timings() {
+ private var start = System.nanoTime
+
+ def printElapsed(message: String): Unit = {
+ CommonListeners.debug(message + " (" + (System.nanoTime - start) / 1000000L + ")")
+ start = System.nanoTime
+ }
+}
\ No newline at end of file
diff --git a/src/main/scala/Test.scala b/src/main/scala/Test.scala
deleted file mode 100644
index 743d12a..0000000
--- a/src/main/scala/Test.scala
+++ /dev/null
@@ -1,5 +0,0 @@
-import buttondevteam.discordplugin.DiscordPlugin
-
-object Test extends App {
- println(DiscordPlugin.plugin)
-}
\ No newline at end of file