From fce6b91b97063b63df29b34c6481c1a71adef2f9 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Tue, 2 Mar 2021 01:18:20 +0100 Subject: [PATCH] Use Scala version of Reactor & data types --- pom.xml | 15 ---- .../buttondevteam/discordplugin/DPUtils.scala | 58 +++++++------- .../DiscordConnectedPlayer.scala | 62 ++++++--------- .../discordplugin/DiscordPlugin.scala | 25 +++--- .../discordplugin/DiscordSender.scala | 22 +++--- .../announcer/AnnouncerModule.scala | 3 +- .../commands/VersionCommand.scala | 3 +- .../exceptions/ExceptionListenerModule.scala | 7 +- .../discordplugin/fun/FunModule.scala | 2 +- .../listeners/CommandListener.scala | 45 ++++++----- .../listeners/CommonListeners.scala | 11 +-- .../mcchat/ChannelconCommand.scala | 58 +++++++------- .../discordplugin/mcchat/MCChatCustom.scala | 8 +- .../discordplugin/mcchat/MCChatListener.scala | 64 +++++++-------- .../discordplugin/mcchat/MCChatPrivate.scala | 77 ++++++++++--------- .../discordplugin/mcchat/MCChatUtils.scala | 16 ++-- .../playerfaker/VCMDWrapper.java | 66 ---------------- .../playerfaker/VCMDWrapper.scala | 54 +++++++++++++ 18 files changed, 282 insertions(+), 314 deletions(-) delete mode 100644 src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java create mode 100644 src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.scala diff --git a/pom.xml b/pom.xml index 38ff5f4..c1c5b3b 100644 --- a/pom.xml +++ b/pom.xml @@ -78,21 +78,6 @@ - - maven-compiler-plugin - - - pre-scala - - -proc:only - - generate-sources - - compile - - - - net.alchim31.maven scala-maven-plugin diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.scala b/src/main/java/buttondevteam/discordplugin/DPUtils.scala index f69db27..4841150 100644 --- a/src/main/java/buttondevteam/discordplugin/DPUtils.scala +++ b/src/main/java/buttondevteam/discordplugin/DPUtils.scala @@ -6,10 +6,10 @@ 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 reactor.core.scala.publisher.SMono import java.util -import java.util.{Comparator, Optional} +import java.util.Comparator import java.util.logging.Logger import java.util.regex.Pattern import javax.annotation.Nullable @@ -62,8 +62,8 @@ object DPUtils { 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.appendReplacement(sb, if (Option(ts.floor(Array[Int](matcher.start, 0))).map( //Find a URL start <= our start + (a: Array[Int]) => a(1)).getOrElse(-1) < matcher.start //Check if URL end < our start ) "\\\\" + matcher.group else matcher.group) matcher.appendTail(sb) sb.toString @@ -74,22 +74,22 @@ object DPUtils { DiscordPlugin.plugin.getLogger } - def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[Mono[MessageChannel]] = + def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[SMono[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)) + getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), (_: SMono[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[SMono[Role]] = + roleData(config, key, defName, SMono.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] + def roleData(config: IHaveConfig, key: String, defName: String, guild: SMono[Guild]): ReadOnlyConfigData[SMono[Role]] = config.getReadOnlyDataPrimDef(key, defName, (name: Any) => { + def foo(name: Any): SMono[Role] = { + if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) return SMono.empty[Role] guild.flatMapMany(_.getRoles).filter((r: Role) => r.getName == name).onErrorResume((e: Throwable) => { - def foo(e: Throwable): Mono[Role] = { + def foo(e: Throwable): SMono[Role] = { getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage) - Mono.empty[Role] + SMono.empty[Role] } foo(e) @@ -97,7 +97,7 @@ object DPUtils { } foo(name) - }, (_: Mono[Role]) => defName) + }, (_: SMono[Role]) => defName) def snowflakeData(config: IHaveConfig, key: String, defID: Long): ReadOnlyConfigData[Snowflake] = config.getReadOnlyDataPrimDef(key, defID, (id: Any) => Snowflake.of(id.asInstanceOf[Long]), _.asLong) @@ -137,7 +137,7 @@ object DPUtils { */ def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = { //noinspection ConstantConditions - if (result == null || (result.isInstanceOf[Mono[_]] && !result.asInstanceOf[Mono[_]].hasElement.block)) { + if (result == null || (result.isInstanceOf[SMono[_]] && !result.asInstanceOf[SMono[_]].hasElement.block())) { var path: String = null try { if (component != null) Component.setComponentEnabled(component, false) @@ -157,26 +157,26 @@ object DPUtils { } /** - * Send a response in the form of "@User, message". Use Mono.empty() if you don't have a channel object. + * Send a response in the form of "@User, message". Use SMono.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) + def reply(original: Message, @Nullable channel: MessageChannel, message: String): SMono[Message] = { + val ch = if (channel == null) SMono(original.getChannel) + else SMono.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) + def reply(original: Message, ch: SMono[MessageChannel], message: String): SMono[Message] = + ch.flatMap(channel => SMono(channel.createMessage((if (original.getAuthor.isPresent) original.getAuthor.get.getMention + ", " - else "") + message)) + else "") + message))) def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">" @@ -189,21 +189,21 @@ object DPUtils { * @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] + def getMessageChannel(key: String, id: Snowflake): SMono[MessageChannel] = { + if (id.asLong == 0L) return SMono.empty[MessageChannel] - DiscordPlugin.dc.getChannelById(id).onErrorResume(e => { + SMono(DiscordPlugin.dc.getChannelById(id)).onErrorResume(e => { def foo(e: Throwable) = { getLogger.warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage) - Mono.empty + SMono.empty } foo(e) - }).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel]) + }).filter(ch => ch.isInstanceOf[MessageChannel]).cast[MessageChannel] } - def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] = + def getMessageChannel(config: ConfigData[Snowflake]): SMono[MessageChannel] = getMessageChannel(config.getPath, config.get) - def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.empty) + def ignoreError[T](mono: SMono[T]): SMono[T] = mono.onErrorResume((_: Throwable) => SMono.empty) } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala index e627049..ee791a4 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala +++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.scala @@ -15,8 +15,8 @@ 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 org.mockito.{MockSettings, Mockito} import java.lang.reflect.Modifier import java.net.InetSocketAddress @@ -52,7 +52,24 @@ object DiscordConnectedPlayer { }).stubOnly } -abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordConnectedPlayer] { +/** + * @constructor The parameters must match with {@link #create ( User, MessageChannel, UUID, String, MinecraftChatModule)} + * @param user May be null. + * @param channel May not be null. + * @param uniqueId The UUID of the player. + * @param name The Minecraft name of the player. + * @param module The MinecraftChatModule or null if testing. + */ +abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val uniqueId: UUID, val name: String, val module: MinecraftChatModule) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordConnectedPlayer] { + private var loggedIn = false + private var displayName: String = name + + private var location: Location = if (module == null) null else Bukkit.getWorlds.get(0).getSpawnLocation + private val basePlayer: OfflinePlayer = if (module == null) null else Bukkit.getOfflinePlayer(uniqueId) + private var perm: PermissibleBase = if (module == null) null else new PermissibleBase(basePlayer) + private val origPerm: PermissibleBase = perm + private val vanillaCmdListener: VCMDWrapper = if (module == null) null else new VCMDWrapper(VCMDWrapper.createListener(this, module)) + override def isPermissionSet(name: String): Boolean = this.origPerm.isPermissionSet(name) override def isPermissionSet(perm: Permission): Boolean = this.origPerm.isPermissionSet(perm) @@ -98,40 +115,11 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten 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 - } + def this(user: User, channel: MessageChannel) = + this(user, channel, UUID.randomUUID(), "Test", null) override def setOp(value: Boolean): Unit = { //CraftPlayer-compatible implementation this.origPerm.setOp(value) @@ -214,7 +202,7 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten override def getGameMode = GameMode.SPECTATOR //noinspection ScalaDeprecation - @SuppressWarnings(Array("deprecation")) final private val spigot: Spigot = new Spigot() { + @SuppressWarnings(Array("deprecation")) override def 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 = { @@ -241,11 +229,9 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten override def sendMessage(position: ChatMessageType, component: BaseComponent): Unit = sendMessage(component) //Ignore position - override def sendMessage(position: ChatMessageType, components: BaseComponent*) = - sendMessage(components) + override def sendMessage(position: ChatMessageType, components: BaseComponent*): Unit = + 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/DiscordPlugin.scala b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.scala index 7624ec8..3d16fb5 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.scala +++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.scala @@ -15,12 +15,12 @@ import buttondevteam.lib.architecture._ import buttondevteam.lib.player.ChromaGamerBase import com.google.common.io.Files import discord4j.common.util.Snowflake -import discord4j.core.{DiscordClientBuilder, GatewayDiscordClient} import discord4j.core.`object`.entity.{ApplicationInfo, Guild, Role} import discord4j.core.`object`.presence.{Activity, Presence} import discord4j.core.`object`.reaction.ReactionEmoji import discord4j.core.event.domain.guild.GuildCreateEvent import discord4j.core.event.domain.lifecycle.ReadyEvent +import discord4j.core.{DiscordClientBuilder, GatewayDiscordClient} import discord4j.gateway.ShardInfo import discord4j.store.jdk.JdkStoreService import org.apache.logging.log4j.LogManager @@ -29,7 +29,7 @@ import org.bukkit.command.CommandSender import org.bukkit.configuration.file.YamlConfiguration import org.mockito.internal.util.MockUtil import reactor.core.Disposable -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono import java.io.File import java.nio.charset.StandardCharsets @@ -65,13 +65,16 @@ import java.util.Optional * The main server where the roles and other information is pulled from. It's automatically set to the first server the bot's invited to. */ private def mainServer = getIConfig.getDataPrimDef("mainServer", 0L, (id: Any) => { - def foo(id: Any) = { //It attempts to get the default as well - if (id.asInstanceOf[Long] == 0L) Optional.empty //Hack? - else DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long])).onErrorResume((t: Throwable) => Mono.fromRunnable(() => getLogger.warning("Failed to get guild: " + t.getMessage))).blockOptional + def foo(id: Any): Option[Guild] = { //It attempts to get the default as well + if (id.asInstanceOf[Long] == 0L) Option.empty + else SMono.fromPublisher(DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long]))) + .onErrorResume((t: Throwable) => { + getLogger.warning("Failed to get guild: " + t.getMessage); SMono.empty + }).blockOption() } foo(id) - }, (g: Optional[Guild]) => (g.map((gg: Guild) => gg.getId.asLong): Optional[Long]).orElse(0L)) + }, (g: Option[Guild]) => (g.map(_.getId.asLong): Option[Long]).getOrElse(0L)) /** * The (bot) channel to use for Discord commands like /role. @@ -81,7 +84,7 @@ import java.util.Optional * The role that allows using mod-only Discord commands. * If empty (''), then it will only allow for the owner. */ - var modRole: ReadOnlyConfigData[Mono[Role]] = null + var modRole: ReadOnlyConfigData[SMono[Role]] = null /** * The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access. */ @@ -153,7 +156,9 @@ import java.util.Optional } private def stopStarting(): Unit = { - this synchronized (starting = false) + this synchronized { + starting = false + } notifyAll() } @@ -164,7 +169,7 @@ import java.util.Optional DiscordPlugin.dc.updatePresence(Presence.online(Activity.playing("Minecraft"))).subscribe //Update from the initial presence return } - DiscordPlugin.mainServer = mainServer.get.orElse(null) //Shouldn't change afterwards + DiscordPlugin.mainServer = mainServer.get.orNull //Shouldn't change afterwards if (DiscordPlugin.mainServer == null) { if (event.size == 0) { getLogger.severe("Main server not found! Invite the bot and do /discord restart") @@ -174,7 +179,7 @@ import java.util.Optional } DiscordPlugin.mainServer = event.get(0).getGuild getLogger.warning("Main server set to first one: " + DiscordPlugin.mainServer.getName) - mainServer.set(Optional.of(DiscordPlugin.mainServer)) //Save in config + mainServer.set(Option(DiscordPlugin.mainServer)) //Save in config } DiscordPlugin.SafeMode = false setupConfig() diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.scala b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala index 5e9c038..dadb809 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordSender.scala +++ b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala @@ -2,28 +2,24 @@ 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 org.bukkit.{Bukkit, Server} +import reactor.core.scala.publisher.SMono import java.util -class DiscordSender(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with CommandSender { +class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender { private val perm = new PermissibleBase(this) - private var name: String = null + private val name: String = Option(pname) + .orElse(Option(user).map(u => SMono(u.asMember(DiscordPlugin.mainServer.getId)) + .onErrorResume(_ => SMono.empty).blockOption() + .map(u => u.getDisplayName))) + .getOrElse("Discord user") 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 + this(user, channel, null) } override def isPermissionSet(name: String): Boolean = perm.isPermissionSet(name) diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala index 118ec9f..e54764b 100644 --- a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala +++ b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala @@ -90,7 +90,8 @@ import reactor.core.publisher.Flux } if (msgsb.length > 0) channel.get.flatMap((ch: MessageChannel) => ch.createMessage(msgsb.toString)).flatMap(Message.pin).subscribe if (modmsgsb.length > 0) modChannel.get.flatMap((ch: MessageChannel) => ch.createMessage(modmsgsb.toString)).flatMap(Message.pin).subscribe - if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded} catch { + if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded + } catch { case e: Exception => e.printStackTrace() } diff --git a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala index 59be8e3..137f869 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala @@ -7,8 +7,7 @@ import buttondevteam.lib.chat.{Command2, CommandClass} object VersionCommand { def getVersion: Array[String] = { val desc = DiscordPlugin.plugin.getDescription - Array[String]( // - desc.getFullName, desc.getWebsite //) + Array[String](desc.getFullName, desc.getWebsite) } } diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala index 79f3e3e..14b9ba5 100644 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala @@ -1,7 +1,6 @@ package buttondevteam.discordplugin.exceptions import buttondevteam.core.ComponentManager -import buttondevteam.discordplugin.exceptions.ExceptionListenerModule.SendException import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.lib.architecture.Component import buttondevteam.lib.{TBMCCoreAPI, TBMCExceptionEvent} @@ -12,6 +11,7 @@ import org.bukkit.Bukkit import org.bukkit.event.{EventHandler, Listener} import reactor.core.publisher.Mono +import java.util import java.util.stream.Collectors /** @@ -65,7 +65,10 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { @EventHandler def onException(e: TBMCExceptionEvent): Unit = { if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass)) return if (lastthrown.stream.anyMatch((ex: Throwable) => util.Arrays.equals(e.getException.getStackTrace, ex.getStackTrace) && (if (e.getException.getMessage == null) ex.getMessage == null - else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message && lastsourcemsg.contains(e.getSourceMessage)) { return } + else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message + && lastsourcemsg.contains(e.getSourceMessage)) { + return + } ExceptionListenerModule .SendException(e.getException, e.getSourceMessage) if (lastthrown.size >= 10) lastthrown.remove(0) diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala index c5f20d1..4b71d87 100644 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala @@ -59,7 +59,7 @@ object FunModule { return true //Handled } lastlistp = Bukkit.getOnlinePlayers.size.toShort //Didn't handle - if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady.get.anyMatch(msglowercased.contains)) { + if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady).get.anyMatch(msglowercased.contains _)) { var next = 0 if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala index 8f41efe..0ae0459 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala @@ -6,7 +6,7 @@ import buttondevteam.lib.TBMCCoreAPI import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} import discord4j.core.`object`.entity.{Member, Message, Role, User} -import reactor.core.publisher.{Flux, Mono} +import reactor.core.scala.publisher.{SFlux, SMono} import java.util.concurrent.atomic.AtomicBoolean @@ -18,14 +18,14 @@ object CommandListener { * @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message * @return Whether it did not run the command */ - def runCommand(message: Message, commandChannelID: Snowflake, mentionedonly: Boolean): Mono[Boolean] = { + def runCommand(message: Message, commandChannelID: Snowflake, mentionedonly: Boolean): SMono[Boolean] = { val timings = CommonListeners.timings - val ret = Mono.just(true) + val ret = SMono.just(true) if (message.getContent.isEmpty) return ret //Pin messages and such, let the mcchat listener deal with it val content = message.getContent timings.printElapsed("A") - message.getChannel.flatMap((channel: MessageChannel) => { - def foo(channel: MessageChannel): Mono[Boolean] = { + SMono(message.getChannel).flatMap((channel: MessageChannel) => { + def foo(channel: MessageChannel): SMono[Boolean] = { var tmp = ret if (!mentionedonly) { //mentionedonly conditions are in CommonListeners timings.printElapsed("B") @@ -33,22 +33,24 @@ object CommandListener { return ret } timings.printElapsed("C") - tmp = ret.`then`(channel.`type`).thenReturn(true) // Fun (this true is ignored - x) + tmp = ret.`then`(SMono(channel.`type`)).`then`(ret) // Fun (this true is ignored - x) } val cmdwithargs = new StringBuilder(content) val gotmention = new AtomicBoolean timings.printElapsed("Before self") - tmp.flatMapMany((x: Boolean) => DiscordPlugin.dc.getSelf.flatMap((self: User) => self.asMember(DiscordPlugin.mainServer.getId)).flatMapMany((self: Member) => { - def foo(self: Member): Flux[String] = { - timings.printElapsed("D") - gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message)) - gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get) - val mentions = message.getRoleMentions - self.getRoles.filterWhen((r: Role) => mentions.any((rr: Role) => rr.getName == r.getName)).map(_.getMention) - } + tmp.flatMapMany((_: Boolean) => SMono(DiscordPlugin.dc.getSelf) + .flatMap((self: User) => SMono(self.asMember(DiscordPlugin.mainServer.getId))) + .flatMapMany((self: Member) => { + def foo(self: Member) = { + timings.printElapsed("D") + gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message)) + gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get) + val mentions = SFlux(message.getRoleMentions) + SFlux(self.getRoles).filterWhen((r: Role) => mentions.any((rr: Role) => rr.getName == r.getName)).map(_.getMention) + } - foo(self) - }).map((mentionRole: String) => { + foo(self) + }).map((mentionRole: String) => { def foo(mentionRole: String): Boolean = { timings.printElapsed("E") gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get) // Delete all mentions @@ -56,17 +58,20 @@ object CommandListener { } foo(mentionRole) - }: Boolean)[Mono[Boolean]].switchIfEmpty(Mono.fromSupplier[Boolean](() => !mentionedonly || gotmention.get)))[Mono[Boolean]].filter((b: Boolean) => b).last(false).filter((b: Boolean) => b).doOnNext((b: Boolean) => channel.`type`.subscribe).flatMap((b: Boolean) => { - def foo(): Mono[Boolean] = { + }).switchIfEmpty(SMono.fromCallable(() => !mentionedonly || gotmention.get))).filter(b => b) + .last(Option(false)).filter(b => b) + .doOnNext(_ => channel.`type`.subscribe).flatMap(_ => { + def foo(): SMono[Boolean] = { val cmdwithargsString = cmdwithargs.toString try { timings.printElapsed("F") - if (!DiscordPlugin.plugin.manager.handleCommand(new Command2DCSender(message), cmdwithargsString)) return DPUtils.reply(message, channel, "unknown command. Do " + DiscordPlugin.getPrefix + "help for help.").map((_: Message) => false) + if (!DiscordPlugin.plugin.manager.handleCommand(new Command2DCSender(message), cmdwithargsString)) + return DPUtils.reply(message, channel, "unknown command. Do " + DiscordPlugin.getPrefix + "help for help.").map(_ => false) } catch { case e: Exception => TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e, DiscordPlugin.plugin) } - Mono.just(false) //If the command succeeded or there was an error, return false + SMono.just(false) //If the command succeeded or there was an error, return false } foo() diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala index afb28f2..5927757 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala @@ -13,6 +13,7 @@ import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent} import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono object CommonListeners { val timings = new Timings @@ -36,24 +37,24 @@ object CommonListeners { if (!author.isPresent || author.get.isBot) return `def` if (FunModule.executeMemes(event.getMessage)) return `def` val commandChannel = DiscordPlugin.plugin.commandChannel.get - event.getMessage.getChannel.map((mch: MessageChannel) => (commandChannel != null && mch.getId.asLong == commandChannel.asLong) //If mentioned, that's higher than chat + SMono(event.getMessage.getChannel).map((mch: MessageChannel) => (commandChannel != null && mch.getId.asLong == commandChannel.asLong) //If mentioned, that's higher than chat || mch.isInstanceOf[PrivateChannel] || event.getMessage.getContent.contains("channelcon")).flatMap( (shouldRun: Boolean) => { //Only 'channelcon' is allowed in other channels - def foo(shouldRun: Boolean): Mono[Boolean] = { //Only continue if this doesn't handle the event - if (!shouldRun) return Mono.just(true) //The condition is only for the first command execution, not mcchat + def foo(shouldRun: Boolean): SMono[Boolean] = { //Only continue if this doesn't handle the event + if (!shouldRun) return SMono.just(true) //The condition is only for the first command execution, not mcchat timings.printElapsed("Run command 1") CommandListener.runCommand(event.getMessage, commandChannel, mentionedonly = true) //#bot is handled here } foo(shouldRun) }).filterWhen((ch: Any) => { - def foo(): Mono[Boolean] = { + def foo() = { timings.printElapsed("mcchat") val mcchat = Component.getComponents.get(classOf[MinecraftChatModule]) if (mcchat != null && mcchat.isEnabled) { //ComponentManager.isEnabled() searches the component again return mcchat.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event) //Also runs Discord commands in chat channels } - Mono.just(true) //Wasn't handled, continue + SMono.just(true) //Wasn't handled, continue } foo() diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala index cb1df83..94ad744 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala @@ -1,27 +1,24 @@ package buttondevteam.discordplugin.mcchat -import buttondevteam.core.component.channel.Channel -import buttondevteam.core.component.channel.ChatRoom +import buttondevteam.core.component.channel.{Channel, ChatRoom} +import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast import buttondevteam.discordplugin._ import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC} import buttondevteam.lib.TBMCSystemChatEvent -import buttondevteam.lib.chat.Command2 -import buttondevteam.lib.chat.CommandClass -import buttondevteam.lib.player.TBMCPlayer +import buttondevteam.lib.chat.{Command2, CommandClass} +import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer} import discord4j.core.`object`.entity.Message import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} import discord4j.rest.util.{Permission, PermissionSet} -import lombok.RequiredArgsConstructor import org.bukkit.Bukkit -import org.bukkit.command.CommandSender -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono -import javax.annotation.Nullable import java.lang.reflect.Method import java.util -import java.util.{Objects, Optional} import java.util.function.Supplier import java.util.stream.Collectors +import java.util.{Objects, Optional} +import javax.annotation.Nullable @SuppressWarnings(Array("SimplifyOptionalCallChains")) //Java 11 @CommandClass(helpText = Array(Array("Channel connect", // @@ -37,11 +34,11 @@ import java.util.stream.Collectors class ChannelconCommand(private val module: MinecraftChatModule) extends ICommand2DC { @Command2.Subcommand def remove(sender: Command2DCSender): Boolean = { val message = sender.getMessage - if (checkPerms(message, null)) true + if (checkPerms(message, null)) return true else if (MCChatCustom.removeCustomChat(message.getChannelId)) - DPUtils.reply(message, Mono.empty, "channel connection removed.").subscribe + DPUtils.reply(message, SMono.empty, "channel connection removed.").subscribe else - DPUtils.reply(message, Mono.empty, "this channel isn't connected.").subscribe + DPUtils.reply(message, SMono.empty, "this channel isn't connected.").subscribe true } @@ -54,31 +51,30 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman if (cc == null) { return respond(sender, "this channel isn't connected.") } - val togglesString: Supplier[String] = () => util.Arrays.stream(ChannelconBroadcast.values) - .map((t: ChannelconBroadcast) => - t.toString.toLowerCase + ": " + (if ((cc.toggles & t.flag) == 0) "disabled" else "enabled")) - .collect(Collectors.joining("\n")) + "\n\n" + + val togglesString: Supplier[String] = () => ChannelconBroadcast.values + .map(t => t.toString.toLowerCase + ": " + (if ((cc.toggles & (1 << t.id)) == 0) "disabled" else "enabled")) + .mkString("\n") + "\n\n" + TBMCSystemChatEvent.BroadcastTarget.stream.map((target: TBMCSystemChatEvent.BroadcastTarget) => target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled")) .collect(Collectors.joining("\n")) if (toggle == null) { - DPUtils.reply(message, Mono.empty, "toggles:\n" + togglesString.get).subscribe + DPUtils.reply(message, SMono.empty, "toggles:\n" + togglesString.get).subscribe return true } val arg: String = toggle.toUpperCase - val b: Optional[ChannelconBroadcast] = util.Arrays.stream(ChannelconBroadcast.values).filter((t: ChannelconBroadcast) => t.toString == arg).findAny - if (!b.isPresent) { + val b = ChannelconBroadcast.values.find((t: ChannelconBroadcast) => t.toString == arg) + if (b.isEmpty) { val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg) if (bt == null) { - DPUtils.reply(message, Mono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe + DPUtils.reply(message, SMono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe return true } val add: Boolean = !(cc.brtoggles.contains(bt)) if (add) { - cc.brtoggles.add(bt) + cc.brtoggles += bt } else { - cc.brtoggles.remove(bt) + cc.brtoggles -= bt } return respond(sender, "'" + bt.getName + "' " + (if (add) "en" else "dis") + "abled") } @@ -89,10 +85,10 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman //1 0 | 1 //1 1 | 0 // XOR - cc.toggles ^= b.get.flag - DPUtils.reply(message, Mono.empty, "'" + b.get.toString.toLowerCase + "' " - + (if ((cc.toggles & b.get.flag) == 0) "disabled" else "enabled")).subscribe - return true + cc.toggles ^= (1 << b.get.id) + DPUtils.reply(message, SMono.empty, "'" + b.get.toString.toLowerCase + "' " + + (if ((cc.toggles & (1 << b.get.id)) == 0) "disabled" else "enabled")).subscribe + true } @Command2.Subcommand def `def`(sender: Command2DCSender, channelID: String): Boolean = { @@ -139,14 +135,14 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman return true; }*/ //TODO: "Channel admins" that can connect channels? - MCChatCustom.addCustomChat(channel, groupid, chan.get, author, dcp, 0, new util.HashSet[TBMCSystemChatEvent.BroadcastTarget]) + MCChatCustom.addCustomChat(channel, groupid, chan.get, author, dcp, 0, Set()) if (chan.get.isInstanceOf[ChatRoom]) { DPUtils.reply(message, channel, "alright, connection made to the room!").subscribe } else { DPUtils.reply(message, channel, "alright, connection made to group `" + groupid + "`!").subscribe } - return true + true } @SuppressWarnings(Array("ConstantConditions")) @@ -167,7 +163,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman false } - def getHelpText(method: Method, ann: Command2.Subcommand): Array[String] = + override def getHelpText(method: Method, ann: Command2.Subcommand): Array[String] = Array[String]( "Channel connect", "This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).", @@ -178,6 +174,6 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman "To remove a connection use @ChromaBot channelcon remove in the channel.", "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix + " prefix only works in " + DPUtils.botmention + ".", "Invite link: info.getId.asString).blockOption().getOrElse("Unknown") + "&scope=bot&permissions=268509264>") } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala index c6fbcec..25eb781 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala @@ -18,7 +18,7 @@ object MCChatCustom { */ private[mcchat] val lastmsgCustom = new util.ArrayList[MCChatCustom.CustomLMD] - def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: util.Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = { + def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = { lastmsgCustom synchronized { var gid: String = null mcchannel match { @@ -58,9 +58,9 @@ object MCChatCustom { def getCustomChats: util.List[CustomLMD] = Collections.unmodifiableList(lastmsgCustom) - class CustomLMD private(@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String, - @NonNull val mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int, - var brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]) extends MCChatUtils.LastMsgData(channel, user) { + class CustomLMD private[mcchat](@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String, + @NonNull mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int, + var brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]) extends MCChatUtils.LastMsgData(channel, user, mcchannel) { } } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala index 62eeef6..067d960 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala @@ -21,6 +21,7 @@ import org.bukkit.entity.Player import org.bukkit.event.{EventHandler, Listener} import org.bukkit.scheduler.BukkitTask import reactor.core.publisher.Mono +import reactor.core.scala.publisher.{SFlux, SMono} import java.time.Instant import java.util @@ -244,30 +245,30 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { private var recthread: Thread = null // Discord - def handleDiscord(ev: MessageCreateEvent): Mono[Boolean] = { + def handleDiscord(ev: MessageCreateEvent): SMono[Boolean] = { val timings: Timings = CommonListeners.timings timings.printElapsed("Chat event") - val author: Optional[User] = ev.getMessage.getAuthor - val hasCustomChat: Boolean = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId) - val prefix: Char = DiscordPlugin.getPrefix - return ev.getMessage.getChannel.filter((channel: MessageChannel) => { - def foo(channel: MessageChannel) = { - timings.printElapsed("Filter 1") - return !((ev.getMessage.getChannelId.asLong != module.chatChannel.get.asLong && !((channel.isInstanceOf[PrivateChannel] && author.map((u: User) => MCChatPrivate.isMinecraftChatEnabled(u.getId.asString)).orElse(false))) && !(hasCustomChat))) //Chat isn't enabled on this channel - } + val author = Option(ev.getMessage.getAuthor.orElse(null)) + val hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId) + val prefix = DiscordPlugin.getPrefix + SMono(ev.getMessage.getChannel).filter(channel => { + def hasPrivateChat = channel.isInstanceOf[PrivateChannel] && + author.exists((u: User) => MCChatPrivate.isMinecraftChatEnabled(u.getId.asString)) - foo(channel) - }).filter((channel: MessageChannel) => { - def foo(channel: MessageChannel) = { - timings.printElapsed("Filter 2") - return !((channel.isInstanceOf[PrivateChannel] //Only in private chat && ev.getMessage.getContent.length < "/mcchat<>".length && ev.getMessage.getContent.replace(prefix + "", "").equalsIgnoreCase("mcchat")))//Either mcchat or /mcchat - //Allow disabling the chat if needed - } + def hasPublicChat = ev.getMessage.getChannelId.asLong == module.chatChannel.get.asLong - foo(channel) - }).filterWhen((channel: MessageChannel) => CommandListener.runCommand(ev.getMessage, DiscordPlugin.plugin.commandChannel.get, true)).filter //Allow running commands in chat channels - ((channel: MessageChannel) => { - def foo(channel: MessageChannel) = { + timings.printElapsed("Filter 1") + val chatEnabled = hasPublicChat || hasPrivateChat || hasCustomChat + chatEnabled + }).filter(channel => { + timings.printElapsed("Filter 2") + !(channel.isInstanceOf[PrivateChannel] //Only in private chat + && ev.getMessage.getContent.length < "/mcchat<>".length + && ev.getMessage.getContent.replace(prefix + "", "").equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat + //Allow disabling the chat if needed + }).filterWhen(_ => + CommandListener.runCommand(ev.getMessage, DiscordPlugin.plugin.commandChannel.get, mentionedonly = true)) //Allow running commands in chat channels + .filter(channel => { MCChatUtils.resetLastMessage(channel) recevents.add(ev) timings.printElapsed("Message event added") @@ -275,22 +276,15 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { return true } recrun = () => { - def foo() = { //Don't return in a while loop next time - recthread = Thread.currentThread - processDiscordToMC() - if (DiscordPlugin.plugin.isEnabled && !(stop)) { - rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Continue message processing - } + recthread = Thread.currentThread + processDiscordToMC() + if (DiscordPlugin.plugin.isEnabled && !(stop)) { + rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Continue message processing } - - foo() } rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Start message processing return true - } - - foo(channel) - }).map((b: MessageChannel) => false).defaultIfEmpty(true) + }).map(_ => false).defaultIfEmpty(true) } private def processDiscordToMC(): Unit = { @@ -307,7 +301,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { val dsender: DiscordSenderBase = MCChatUtils.getSender(event.getMessage.getChannelId, sender) val user: DiscordPlayer = dsender.getChromaUser - for (u <- event.getMessage.getUserMentions.toIterable) { //TODO: Role mentions + for (u <- SFlux(event.getMessage.getUserMentions).toIterable()) { //TODO: Role mentions dmessage = dmessage.replace(u.getMention, "@" + u.getUsername) // TODO: IG Formatting val m: Optional[Member] = u.asMember(DiscordPlugin.mainServer.getId).onErrorResume((t: Throwable) => Mono.empty).blockOptional if (m.isPresent) { @@ -317,7 +311,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { } } - for (ch <- event.getGuild.flux.flatMap(_.getChannels).toIterable) { + for (ch <- SFlux(event.getGuild.flux).flatMap(_.getChannels).toIterable()) { dmessage = dmessage.replace(ch.getMention, "#" + ch.getName) } dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE) //Converts emoji to text- TODO: Add option to disable (resource pack?) @@ -388,7 +382,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { } react = true } - return react + react } private def handleIngameCommand(event: MessageCreateEvent, dmessage: String, dsender: DiscordSenderBase, user: DiscordPlayer, clmd: MCChatCustom.CustomLMD, isPrivate: Boolean): Boolean = { diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala index e2e023e..1acd129 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala @@ -1,6 +1,7 @@ package buttondevteam.discordplugin.mcchat import buttondevteam.core.ComponentManager +import buttondevteam.discordplugin.mcchat.MCChatUtils.LastMsgData import buttondevteam.discordplugin.{DiscordConnectedPlayer, DiscordPlayer, DiscordPlugin, DiscordSenderBase} import buttondevteam.lib.player.TBMCPlayer import discord4j.common.util.Snowflake @@ -8,52 +9,53 @@ import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} import org.bukkit.Bukkit -import java.util +import scala.collection.mutable.ListBuffer object MCChatPrivate { /** * Used for messages in PMs (mcchat). */ - private[mcchat] val lastmsgPerUser = new util.ArrayList[MCChatUtils.LastMsgData] + private[mcchat] var lastmsgPerUser: ListBuffer[LastMsgData] = ListBuffer() def privateMCChat(channel: MessageChannel, start: Boolean, user: User, dp: DiscordPlayer): Unit = { - MCChatUtils.ConnectedSenders synchronized - val mcp = dp.getAs(classOf[TBMCPlayer]) - if (mcp != null) { // If the accounts aren't connected, can't make a connected sender - val p = Bukkit.getPlayer(mcp.getUUID) - val op = Bukkit.getOfflinePlayer(mcp.getUUID) - val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule]) - if (start) { - val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID, op.getName, mcm) - MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender) - MCChatUtils.LoggedInPlayers.put(mcp.getUUID, sender) - if (p == null) { // Player is offline - If the player is online, that takes precedence - MCChatUtils.callLoginEvents(sender) + MCChatUtils.ConnectedSenders synchronized { + val mcp = dp.getAs(classOf[TBMCPlayer]) + if (mcp != null) { // If the accounts aren't connected, can't make a connected sender + val p = Bukkit.getPlayer(mcp.getUUID) + val op = Bukkit.getOfflinePlayer(mcp.getUUID) + val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule]) + if (start) { + val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID, op.getName, mcm) + MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender) + MCChatUtils.LoggedInPlayers.put(mcp.getUUID, sender) + if (p == null) { // Player is offline - If the player is online, that takes precedence + MCChatUtils.callLoginEvents(sender) + } } - } - else { - val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId, user) - assert(sender != null) - Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => { - def foo(): Unit = { - if ((p == null || p.isInstanceOf[DiscordSenderBase]) // Player is offline - If the player is online, that takes precedence - && sender.isLoggedIn) { //Don't call the quit event if login failed - MCChatUtils.callLogoutEvent(sender, false) //The next line has to run *after* this one, so can't use the needsSync parameter + else { + val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId, user) + assert(sender != null) + Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => { + def foo(): Unit = { + if ((p == null || p.isInstanceOf[DiscordSenderBase]) // Player is offline - If the player is online, that takes precedence + && sender.isLoggedIn) { //Don't call the quit event if login failed + MCChatUtils.callLogoutEvent(sender, false) //The next line has to run *after* this one, so can't use the needsSync parameter + } + + MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId) + sender.setLoggedIn(false) } - MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId) - sender.setLoggedIn(false) + foo() } - - foo() + ) } - ) + // ---- PermissionsEx warning is normal on logout ---- } - // ---- PermissionsEx warning is normal on logout ---- + if (!start) MCChatUtils.lastmsgfromd.remove(channel.getId.asLong) + if (start) lastmsgPerUser += new MCChatUtils.LastMsgData(channel, user) // Doesn't support group DMs + else lastmsgPerUser.filterInPlace(_.channel.getId.asLong != channel.getId.asLong) //Remove } - if (!start) MCChatUtils.lastmsgfromd.remove(channel.getId.asLong) - if (start) lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs - else lastmsgPerUser.removeIf((lmd: MCChatUtils.LastMsgData) => lmd.channel.getId.asLong == channel.getId.asLong) } def isMinecraftChatEnabled(dp: DiscordPlayer): Boolean = isMinecraftChatEnabled(dp.getDiscordID) @@ -64,11 +66,12 @@ object MCChatPrivate { } def logoutAll(): Unit = { - MCChatUtils.ConnectedSenders synchronized - for (entry <- MCChatUtils.ConnectedSenders.entrySet) { - for (valueEntry <- entry.getValue.entrySet) { - if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out - MCChatUtils.callLogoutEvent(valueEntry.getValue, !Bukkit.isPrimaryThread) + MCChatUtils.ConnectedSenders synchronized { + for (entry <- asScala(MCChatUtils.ConnectedSenders.entrySet)) { + for (valueEntry <- entry.getValue.entrySet) { + if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out + MCChatUtils.callLogoutEvent(valueEntry.getValue, !Bukkit.isPrimaryThread) + } } } } diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala index 7eaae1e..1926cab 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala @@ -30,15 +30,16 @@ import java.util.function.Supplier import java.util.logging.Level import java.util.stream.{Collectors, Stream} import javax.annotation.Nullable +import scala.jdk.javaapi.CollectionConverters.asScala object MCChatUtils { /** * May contain P<DiscordID> as key for public chat */ - val UnconnectedSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordSender]] - val ConnectedSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordConnectedPlayer]] - val OnlineSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordPlayerSender]] - val LoggedInPlayers = new ConcurrentHashMap[UUID, DiscordConnectedPlayer] + val UnconnectedSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordSender]]) + val ConnectedSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordConnectedPlayer]]) + val OnlineSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordPlayerSender]]) + val LoggedInPlayers = asScala(new ConcurrentHashMap[UUID, DiscordConnectedPlayer]) @Nullable private[mcchat] var lastmsgdata: MCChatUtils.LastMsgData = null private[mcchat] val lastmsgfromd = new LongObjectHashMap[Message] // Last message sent by a Discord user, used for clearing checkmarks private var module: MinecraftChatModule = null @@ -361,10 +362,15 @@ object MCChatUtils { private[mcchat] def callEventSync(event: Event) = Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => callEventExcludingSome(event)) class LastMsgData(val channel: MessageChannel, val user: User) { - var message: String = null + var message: Message = null var time = 0L var content: String = null var mcchannel: component.channel.Channel = null + + protected def this(channel: MessageChannel, user: User, mcchannel: component.channel.Channel) = { + this(channel, user) + this.mcchannel = mcchannel + } } } \ 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 deleted file mode 100644 index 6ff856b..0000000 --- a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.java +++ /dev/null @@ -1,66 +0,0 @@ -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; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import javax.annotation.Nullable; - -@RequiredArgsConstructor -public class VCMDWrapper { - @Getter //Needed to mock the player - @Nullable - private final Object listener; - - /** - * This constructor will only send raw vanilla messages to the sender in plain text. - * - * @param player The Discord sender player (the wrapper) - */ - public static > Object createListener(T player, MinecraftChatModule module) { - return createListener(player, null, module); - } - - /** - * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. - * - * @param player The Discord sender player (the wrapper) - * @param bukkitplayer The Bukkit player to send the raw message to - * @param module The Minecraft chat module - */ - public static > Object createListener(T player, Player bukkitplayer, MinecraftChatModule module) { - try { - Object ret; - String mcpackage = Bukkit.getServer().getClass().getPackage().getName(); - if (mcpackage.contains("1_12")) - ret = new VanillaCommandListener<>(player, bukkitplayer); - else if (mcpackage.contains("1_14")) - ret = new VanillaCommandListener14<>(player, bukkitplayer); - else if (mcpackage.contains("1_15") || mcpackage.contains("1_16")) - ret = VanillaCommandListener15.create(player, bukkitplayer); //bukkitplayer may be null but that's fine - else - ret = null; - if (ret == null) - compatWarning(module); - return ret; - } catch (NoClassDefFoundError | Exception e) { - compatWarning(module); - TBMCCoreAPI.SendException("Failed to create vanilla command listener", e, module); - return null; - } - } - - private static void compatWarning(MinecraftChatModule module) { - module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message."); - } - - static boolean compatResponse(DiscordSenderBase dsender) { - dsender.sendMessage("Vanilla commands are not supported on this Minecraft version."); - return true; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.scala b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.scala new file mode 100644 index 0000000..4c96d51 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/playerfaker/VCMDWrapper.scala @@ -0,0 +1,54 @@ +package buttondevteam.discordplugin.playerfaker + +import buttondevteam.discordplugin.{DiscordSenderBase, IMCPlayer} +import buttondevteam.discordplugin.mcchat.MinecraftChatModule +import buttondevteam.lib.TBMCCoreAPI +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +object VCMDWrapper { + /** + * This constructor will only send raw vanilla messages to the sender in plain text. + * + * @param player The Discord sender player (the wrapper) + */ + def createListener[T <: DiscordSenderBase with IMCPlayer[T]](player: T, module: MinecraftChatModule): AnyRef = + createListener(player, null, module) + + /** + * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. + * + * @param player The Discord sender player (the wrapper) + * @param bukkitplayer The Bukkit player to send the raw message to + * @param module The Minecraft chat module + */ + def createListener[T <: DiscordSenderBase with IMCPlayer[T]](player: T, bukkitplayer: Player, module: MinecraftChatModule): AnyRef = try { + var ret: AnyRef = null + val mcpackage = Bukkit.getServer.getClass.getPackage.getName + if (mcpackage.contains("1_12")) ret = new VanillaCommandListener[T](player, bukkitplayer) + else if (mcpackage.contains("1_14")) ret = new VanillaCommandListener14[T](player, bukkitplayer) + else if (mcpackage.contains("1_15") || mcpackage.contains("1_16")) ret = VanillaCommandListener15.create(player, bukkitplayer) //bukkitplayer may be null but that's fine + else ret = null + if (ret == null) compatWarning(module) + ret + } catch { + case e@(_: NoClassDefFoundError | _: Exception) => + compatWarning(module) + TBMCCoreAPI.SendException("Failed to create vanilla command listener", e, module) + null + } + + private def compatWarning(module: MinecraftChatModule): Unit = + module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message.") + + private[playerfaker] def compatResponse(dsender: DiscordSenderBase) = { + dsender.sendMessage("Vanilla commands are not supported on this Minecraft version.") + true + } +} + +class VCMDWrapper(private val listener: AnyRef) { + @javax.annotation.Nullable def getListener: AnyRef = listener + + //Needed to mock the player @Nullable +} \ No newline at end of file