From f909eb47794a27249996960b754cf2ba0c84007f Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Mon, 24 Apr 2023 04:42:24 +0200 Subject: [PATCH] Attempted to update to latest API, lots of errors (9 remains) - Removed Reactor Scala extensions in an attempt to get this thing to compile but it doesn't seem to help - Removed heavily version-dependent stuff --- build.sbt | 12 +- lombok.config | 1 - project/build.properties | 2 +- project/build.sbt | 2 +- .../discordplugin/ChromaBot.scala | 6 +- .../buttondevteam/discordplugin/DPUtils.scala | 88 +++---- .../discordplugin/DiscordPlugin.scala | 37 ++- .../announcer/AnnouncerModule.scala | 6 +- .../GeneralEventBroadcasterModule.scala | 7 +- .../broadcaster/PlayerListWatcher.scala | 168 -------------- .../discordplugin/commands/Command2DC.scala | 4 +- .../commands/Command2DCSender.scala | 6 +- .../discordplugin/commands/DebugCommand.scala | 13 +- .../discordplugin/commands/HelpCommand.scala | 8 +- .../commands/UserinfoCommand.scala | 5 +- .../exceptions/DebugMessageListener.scala | 3 +- .../exceptions/ExceptionListenerModule.scala | 16 +- .../discordplugin/fun/FunModule.scala | 26 +-- .../listeners/CommonListeners.scala | 17 +- .../discordplugin/listeners/MCListener.scala | 10 +- .../mcchat/ChannelconCommand.scala | 22 +- .../discordplugin/mcchat/MCChatListener.scala | 70 +++--- .../discordplugin/mcchat/MCChatPrivate.scala | 8 +- .../discordplugin/mcchat/MCChatUtils.scala | 63 +++-- .../discordplugin/mcchat/MCListener.scala | 25 +- .../mcchat/MinecraftChatModule.scala | 44 ++-- .../playerfaker/DelegatingMockMaker.scala | 51 ---- .../mcchat/playerfaker/DiscordInventory.java | 218 ------------------ .../mcchat/playerfaker/ServerWatcher.scala | 94 -------- .../mcchat/playerfaker/VCMDWrapper.scala | 54 ----- .../playerfaker/VanillaCommandListener.scala | 84 ------- .../VanillaCommandListener14.scala | 84 ------- .../VanillaCommandListener15.scala | 123 ---------- .../mcchat/playerfaker/perm/LPInjector.java | 207 ----------------- .../sender/DiscordConnectedPlayer.scala | 13 +- .../mcchat/sender/DiscordPlayerSender.scala | 6 +- .../mcchat/sender/DiscordSender.scala | 7 +- .../mcchat/sender/IMCPlayer.scala | 3 +- .../mccommands/DiscordMCCommand.scala | 27 ++- .../discordplugin/role/GameRoleModule.scala | 39 ++-- 40 files changed, 274 insertions(+), 1405 deletions(-) delete mode 100644 lombok.config delete mode 100644 src/main/scala/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DelegatingMockMaker.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DiscordInventory.java delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/ServerWatcher.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VCMDWrapper.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener14.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener15.scala delete mode 100644 src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/perm/LPInjector.java diff --git a/build.sbt b/build.sbt index 1ed281e..37b2629 100644 --- a/build.sbt +++ b/build.sbt @@ -12,25 +12,23 @@ scalaVersion := "3.1.0" resolvers += "spigot-repo" at "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" resolvers += "jitpack.io" at "https://jitpack.io" +resolvers += "paper-repo" at "https://papermc.io/repo/repository/maven-public/" resolvers += Resolver.mavenLocal // assembly / assemblyOption := (assembly / assemblyOption).value.copy(includeScala = false) libraryDependencies ++= Seq( - "org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT" % Provided, - "org.spigotmc" % "spigot" % "1.12.2-R0.1-SNAPSHOT" % Provided, - "org.spigotmc." % "spigot" % "1.14.4-R0.1-SNAPSHOT" % Provided, - "com.destroystokyo.paper" % "paper" % "1.16.3-R0.1-SNAPSHOT" % Provided, + "org.spigotmc" % "spigot" % "1.19.4-R0.1-SNAPSHOT" % Provided, + "io.papermc.paper" % "paper-api" % "1.19.4-R0.1-SNAPSHOT" % Provided, "com.discord4j" % "discord4j-core" % "3.2.2", "org.slf4j" % "slf4j-jdk14" % "1.7.36", "com.vdurmont" % "emoji-java" % "5.1.1", "org.mockito" % "mockito-core" % "4.6.1", - "io.projectreactor" % "reactor-scala-extensions_2.13" % "0.8.0", // https://mvnrepository.com/artifact/org.immutables/value "org.immutables" % "value" % "2.9.0" % "provided", - "com.github.TBMCPlugins.ChromaCore" % "Chroma-Core" % "v1.0.0" % Provided, + "com.github.TBMCPlugins.ChromaCore" % "Chroma-Core" % "v2.0.0-SNAPSHOT" % Provided, "net.ess3" % "EssentialsX" % "2.17.1" % Provided, "net.luckperms" % "api" % "5.4" % Provided, ) @@ -141,3 +139,5 @@ saveConfigComments := { } Compile / resourceGenerators += saveConfigComments +//scalacOptions ++= Seq("-release", "17", "--verbose") +scalacOptions ++= Seq("-release", "17") diff --git a/lombok.config b/lombok.config deleted file mode 100644 index 4f55f03..0000000 --- a/lombok.config +++ /dev/null @@ -1 +0,0 @@ -lombok.var.flagUsage = ALLOW \ No newline at end of file diff --git a/project/build.properties b/project/build.properties index 7cde0be..fb0fc7f 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1,2 +1,2 @@ -sbt.version=1.5.8 +sbt.version=1.8.2 scala.version=3.0.0 \ No newline at end of file diff --git a/project/build.sbt b/project/build.sbt index c98cb35..95ec632 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -3,4 +3,4 @@ resolvers += Resolver.mavenLocal -libraryDependencies += "org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT" +libraryDependencies += "org.spigotmc" % "spigot-api" % "1.19.4-R0.1-SNAPSHOT" diff --git a/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala b/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala index af6ae9f..86a230c 100644 --- a/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala +++ b/src/main/scala/buttondevteam/discordplugin/ChromaBot.scala @@ -4,7 +4,7 @@ import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast import buttondevteam.discordplugin.mcchat.MCChatUtils import discord4j.core.`object`.entity.Message import discord4j.core.`object`.entity.channel.MessageChannel -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono import javax.annotation.Nullable @@ -20,7 +20,7 @@ object ChromaBot { * * @param message The message to send, duh (use [[MessageChannel.createMessage]]) */ - def sendMessage(message: SMono[MessageChannel] => SMono[Message]): Unit = + def sendMessage(message: Mono[MessageChannel] => Mono[Message]): Unit = MCChatUtils.forPublicPrivateChat(message).subscribe() /** @@ -29,7 +29,7 @@ object ChromaBot { * @param message The message to send, duh * @param toggle The toggle type for channelcon */ - def sendMessageCustomAsWell(message: SMono[MessageChannel] => SMono[Message], @Nullable toggle: ChannelconBroadcast): Unit = + def sendMessageCustomAsWell(message: Mono[MessageChannel] => Mono[Message], @Nullable toggle: ChannelconBroadcast): Unit = MCChatUtils.forCustomAndAllMCChat(message.apply, toggle, hookmsg = false).subscribe() def updatePlayerList(): Unit = diff --git a/src/main/scala/buttondevteam/discordplugin/DPUtils.scala b/src/main/scala/buttondevteam/discordplugin/DPUtils.scala index be512b0..e0576c3 100644 --- a/src/main/scala/buttondevteam/discordplugin/DPUtils.scala +++ b/src/main/scala/buttondevteam/discordplugin/DPUtils.scala @@ -3,11 +3,11 @@ 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.channel.{Channel, MessageChannel} import discord4j.core.`object`.entity.{Guild, Message, Role} +import discord4j.core.spec.EmbedCreateSpec import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacySpec} import reactor.core.publisher.{Flux, Mono} -import reactor.core.scala.publisher.{SFlux, SMono} import java.util import java.util.Comparator @@ -19,8 +19,8 @@ object DPUtils { private val URL_PATTERN = Pattern.compile("https?://\\S*") private val FORMAT_PATTERN = Pattern.compile("[*_~]") - def embedWithHead(ecs: LegacyEmbedCreateSpec, displayname: String, playername: String, profileUrl: String): LegacyEmbedCreateSpec = - ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png") + def embedWithHead(ecs: EmbedCreateSpec.Builder, displayname: String, playername: String, profileUrl: String): EmbedCreateSpec.Builder = + ecs.author(displayname, profileUrl, s"https://minotar.net/avatar/$playername/32.png") /** * Removes §[char] colour codes from strings & escapes them for Discord
@@ -68,34 +68,26 @@ object DPUtils { else DiscordPlugin.plugin.getLogger } - def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[SMono[MessageChannel]] = - config.getReadOnlyDataPrimDef(key, 0L, (id: Any) => - 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 channelData(config: IHaveConfig, key: String): ConfigData[Mono[MessageChannel]] = + config.getData(key, id => getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), + (_: Mono[MessageChannel]) => 0L, 0L, true) //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)) + def roleData(config: IHaveConfig, key: String, defName: String): ConfigData[Mono[Role]] = + roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer)) /** * Needs to be a [[ConfigData]] for checking if it's set */ - 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): SMono[Role] = { - getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage) - SMono.empty[Role] - } + def roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): ConfigData[Mono[Role]] = config.getData(key, name => { + if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) Mono.empty[Role] + else guild.flatMapMany(_.getRoles).filter(_.getName == name).onErrorResume(e => { + getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage) + Mono.empty[Role] + }).next + }, _ => defName, defName, true) - foo(e) - }).next - } - - foo(name) - }, (_: 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) + def snowflakeData(config: IHaveConfig, key: String, defID: Long): ConfigData[Snowflake] = + config.getData(key, id => Snowflake.of(id.asInstanceOf[Long]), _.asLong, defID, true) /** * Mentions the bot channel. Useful for help texts. @@ -132,7 +124,7 @@ object DPUtils { */ def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = { //noinspection ConstantConditions - if (result == null || (result.isInstanceOf[SMono[_]] && !result.asInstanceOf[SMono[_]].hasElement.block())) { + if (result == null || (result.isInstanceOf[Mono[_]] && !result.asInstanceOf[Mono[_]].hasElement.block())) { var path: String = null try { if (component != null) Component.setComponentEnabled(component, false) @@ -152,26 +144,26 @@ object DPUtils { } /** - * Send a response in the form of "@User, message". Use SMono.empty() if you don't have a channel object. + * 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): SMono[Message] = { - val ch = if (channel == null) SMono(original.getChannel) - else SMono.just(channel) + 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: SMono[MessageChannel], message: String): SMono[Message] = - ch.flatMap(channel => SMono(channel.createMessage((if (original.getAuthor.isPresent) + def reply(original: Message, ch: Mono[MessageChannel], message: String): Mono[Message] = + ch.flatMap(channel => channel.createMessage((if (original.getAuthor.isPresent) original.getAuthor.get.getMention + ", " - else "") + message))) + else "") + message)) def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">" @@ -184,31 +176,19 @@ object DPUtils { * @param id The channel ID * @return A message channel */ - def getMessageChannel(key: String, id: Snowflake): SMono[MessageChannel] = { - if (id.asLong == 0L) return SMono.empty[MessageChannel] + def getMessageChannel(key: String, id: Snowflake): Mono[MessageChannel] = { + if (id.asLong == 0L) return Mono.empty[MessageChannel] - SMono(DiscordPlugin.dc.getChannelById(id)).onErrorResume(e => { - def foo(e: Throwable) = { - getLogger.warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage) - SMono.empty - } - - foo(e) - }).filter(ch => ch.isInstanceOf[MessageChannel]).cast[MessageChannel] + DiscordPlugin.dc.getChannelById(id).onErrorResume(e => { + getLogger.warning(s"Failed to get channel data for $key=$id - ${e.getMessage}") + Mono.empty[Channel] + }).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel]]) } - def getMessageChannel(config: ConfigData[Snowflake]): SMono[MessageChannel] = + def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] = getMessageChannel(config.getPath, config.get) - def ignoreError[T](mono: SMono[T]): SMono[T] = mono.onErrorResume((_: Throwable) => SMono.empty) - - implicit class MonoExtensions[T](mono: Mono[T]) { - def ^^(): SMono[T] = SMono(mono) - } - - implicit class FluxExtensions[T](flux: Flux[T]) { - def ^^(): SFlux[T] = SFlux(flux) - } + def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.empty) implicit class SpecExtensions[T <: LegacySpec[_]](spec: T) { def ^^(): Unit = () diff --git a/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala b/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala index 9e767e0..54d0125 100644 --- a/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala +++ b/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala @@ -32,12 +32,12 @@ import org.bukkit.command.CommandSender import org.bukkit.configuration.file.YamlConfiguration import org.mockito.internal.util.MockUtil import reactor.core.Disposable -import reactor.core.scala.publisher.SMono -import reactor.core.scala.publisher.scalaOption2JavaOptional +import reactor.core.publisher.Mono import java.io.File import java.nio.charset.StandardCharsets import java.util.Optional +import scala.jdk.OptionConverters.* @ButtonPlugin.ConfigOpts(disableConfigGen = true) object DiscordPlugin { private[discordplugin] var dc: GatewayDiscordClient = null @@ -68,28 +68,24 @@ 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): 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: Option[Guild]) => (g.map(_.getId.asLong): Option[Long]).getOrElse(0L)) + private def mainServer = getIConfig.getData("mainServer", id => { //It attempts to get the default as well + if (id.asInstanceOf[Long] == 0L) Option.empty + else DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long])) + .onErrorResume(t => { + getLogger.warning("Failed to get guild: " + t.getMessage) + Mono.empty + }).blockOptional().toScala + }, g => g.map(_.getId.asLong).getOrElse(0L), 0L, true) /** * The (bot) channel to use for Discord commands like /role. */ - var commandChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L) + var commandChannel: ConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L) /** * The role that allows using mod-only Discord commands. * If empty (''), then it will only allow for the owner. */ - var modRole: ReadOnlyConfigData[SMono[Role]] = null + var modRole: ConfigData[Mono[Role]] = null /** * The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access. */ @@ -189,11 +185,10 @@ import java.util.Optional CommonListeners.register(DiscordPlugin.dc.getEventDispatcher) TBMCCoreAPI.RegisterEventsForExceptions(new MCListener, this) TBMCCoreAPI.RegisterUserClass(classOf[DiscordPlayer], () => new DiscordPlayer) - ChromaGamerBase.addConverter((sender: CommandSender) => sender match { - case dsender: DiscordSenderBase => Some(dsender.getChromaUser) - case _ => None - }) - IHaveConfig.pregenConfig(this, null) + ChromaGamerBase.addConverter { + case dsender: DiscordSenderBase => Some(dsender.getChromaUser).toJava + case _ => None.toJava + } ChromaBot.enabled = true //Initialize ChromaBot Component.registerComponent(this, new GeneralEventBroadcasterModule) Component.registerComponent(this, new MinecraftChatModule) diff --git a/src/main/scala/buttondevteam/discordplugin/announcer/AnnouncerModule.scala b/src/main/scala/buttondevteam/discordplugin/announcer/AnnouncerModule.scala index 0472894..bf5cdf7 100644 --- a/src/main/scala/buttondevteam/discordplugin/announcer/AnnouncerModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/announcer/AnnouncerModule.scala @@ -7,7 +7,7 @@ import buttondevteam.lib.architecture.{Component, ComponentMetadata} import buttondevteam.lib.player.ChromaGamerBase import com.google.gson.JsonParser import discord4j.core.`object`.entity.channel.MessageChannel -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono import scala.annotation.tailrec import scala.collection.mutable @@ -86,8 +86,8 @@ import scala.collection.mutable } } - def sendMsg(ch: SMono[MessageChannel], msg: String) = - ch.asJava().flatMap(c => c.createMessage(msg)).flatMap(_.pin).subscribe() + def sendMsg(ch: Mono[MessageChannel], msg: String) = + ch.flatMap(c => c.createMessage(msg)).flatMap(_.pin).subscribe() if (msgsb.nonEmpty) sendMsg(channel.get(), msgsb.toString()) if (modmsgsb.nonEmpty) sendMsg(modChannel.get(), modmsgsb.toString()) diff --git a/src/main/scala/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala b/src/main/scala/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala index 0072c77..881a44e 100644 --- a/src/main/scala/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala @@ -16,9 +16,7 @@ import buttondevteam.lib.architecture.{Component, ComponentMetadata} @ComponentMetadata(enabledByDefault = false) class GeneralEventBroadcasterModule extends Component[DiscordPlugin] { override protected def enable(): Unit = try { - PlayerListWatcher.hookUpDown(true, this) - log("Finished hooking into the player list") - GeneralEventBroadcasterModule.hooked = true + // TODO: Removed for now } catch { case e: Exception => TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e, this) @@ -28,9 +26,6 @@ import buttondevteam.lib.architecture.{Component, ComponentMetadata} override protected def disable(): Unit = try { if (!GeneralEventBroadcasterModule.hooked) return () - if (PlayerListWatcher.hookUpDown(false, this)) log("Finished unhooking the player list!") - else log("Didn't have the player list hooked.") - GeneralEventBroadcasterModule.hooked = false } catch { case e: Exception => TBMCCoreAPI.SendException("Error while hacking the player list!", e, this) diff --git a/src/main/scala/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala b/src/main/scala/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala deleted file mode 100644 index 4357944..0000000 --- a/src/main/scala/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala +++ /dev/null @@ -1,168 +0,0 @@ -package buttondevteam.discordplugin.broadcaster - -import buttondevteam.discordplugin.mcchat.MCChatUtils -import buttondevteam.discordplugin.mcchat.playerfaker.DelegatingMockMaker -import buttondevteam.lib.TBMCCoreAPI -import org.bukkit.Bukkit -import org.mockito.Mockito -import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker -import org.mockito.invocation.InvocationOnMock -import org.mockito.stubbing.Answer - -import java.lang.invoke.{MethodHandle, MethodHandles} -import java.lang.reflect.{Constructor, Method, Modifier} -import java.util.UUID - -object PlayerListWatcher { - private var plist: AnyRef = null - private var mock: AnyRef = null - private var fHandle: MethodHandle = null //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16 - @throws[Exception] - private[broadcaster] def hookUpDown(up: Boolean, module: GeneralEventBroadcasterModule): Boolean = { - val csc = Bukkit.getServer.getClass - val conf = csc.getDeclaredField("console") - conf.setAccessible(true) - val server = conf.get(Bukkit.getServer) - val nms = server.getClass.getPackage.getName - val dplc = Class.forName(nms + ".DedicatedPlayerList") - val currentPL = server.getClass.getMethod("getPlayerList").invoke(server) - if (up) { - if (currentPL eq mock) { - module.logWarn("Player list already mocked!") - return false - } - DelegatingMockMaker.getInstance.setMockMaker(new SubclassByteBuddyMockMaker) - val icbcl = Class.forName(nms + ".IChatBaseComponent") - var sendMessageTemp: Method = null - try sendMessageTemp = server.getClass.getMethod("sendMessage", icbcl, classOf[UUID]) - catch { - case e: NoSuchMethodException => - sendMessageTemp = server.getClass.getMethod("sendMessage", icbcl) - } - val sendMessageMethod = sendMessageTemp - val cmtcl = Class.forName(nms + ".ChatMessageType") - val systemType = cmtcl.getDeclaredField("SYSTEM").get(null) - val chatType = cmtcl.getDeclaredField("CHAT").get(null) - val obc = csc.getPackage.getName - val ccmcl = Class.forName(obc + ".util.CraftChatMessage") - val fixComponent = ccmcl.getMethod("fixComponent", icbcl) - val ppoc = Class.forName(nms + ".PacketPlayOutChat") - var ppocCTemp: Constructor[_] = null - try ppocCTemp = ppoc.getConstructor(icbcl, cmtcl, classOf[UUID]) - catch { - case _: Exception => - ppocCTemp = ppoc.getConstructor(icbcl, cmtcl) - } - val ppocC = ppocCTemp - val sendAllMethod = dplc.getMethod("sendAll", Class.forName(nms + ".Packet")) - var tpt: Method = null - try tpt = icbcl.getMethod("toPlainText") - catch { - case _: NoSuchMethodException => - tpt = icbcl.getMethod("getString") - } - val toPlainText = tpt - val sysb = Class.forName(nms + ".SystemUtils").getField("b") - //Find the original method without overrides - var lookupConstructor: Constructor[MethodHandles.Lookup] = null - if (nms.contains("1_16")) { - lookupConstructor = classOf[MethodHandles.Lookup].getDeclaredConstructor(classOf[Class[_]]) - lookupConstructor.setAccessible(true) //Create lookup with a given class instead of caller - } - else lookupConstructor = null - mock = Mockito.mock(dplc, Mockito.withSettings.defaultAnswer(new Answer[AnyRef]() { // Cannot call super constructor - @throws[Throwable] - override def answer(invocation: InvocationOnMock): AnyRef = { - val method = invocation.getMethod - if (!(method.getName == "sendMessage")) { - if (method.getName == "sendAll") { - sendAll(invocation.getArgument(0)) - return null - } - //In 1.16 it passes a reference to the player list to advancement data for each player - if (nms.contains("1_16") && method.getName == "f" && method.getParameterCount > 0 && method.getParameterTypes()(0).getSimpleName == "EntityPlayer") { - method.setAccessible(true) - if (fHandle == null) { - assert(lookupConstructor != null) - val lookup = lookupConstructor.newInstance(mock.getClass) - fHandle = lookup.unreflectSpecial(method, mock.getClass) //Special: super.method() - } - return fHandle.invoke(mock, invocation.getArgument(0)) //Invoke with our instance, so it passes that to advancement data, we have the fields as well - } - return method.invoke(plist, invocation.getArguments) - } - val args = invocation.getArguments - val params = method.getParameterTypes - if (params.isEmpty) { - TBMCCoreAPI.SendException("Found a strange method", new Exception("Found a sendMessage() method without arguments."), module) - return null - } - if (params(0).getSimpleName == "IChatBaseComponent[]") for (arg <- args(0).asInstanceOf[Array[AnyRef]]) { - sendMessage(arg, system = true) - } - else if (params(0).getSimpleName == "IChatBaseComponent") if (params.length > 1 && params(1).getSimpleName.equalsIgnoreCase("boolean")) sendMessage(args(0), args(1).asInstanceOf[Boolean]) - else sendMessage(args(0), system = true) - else TBMCCoreAPI.SendException("Found a method with interesting params", new Exception("Found a sendMessage(" + params(0).getSimpleName + ") method"), module) - null - } - - private - - def sendMessage(chatComponent: Any, system: Boolean) = try { //Converted to use reflection - if (sendMessageMethod.getParameterCount == 2) sendMessageMethod.invoke(server, chatComponent, sysb.get(null)) - else sendMessageMethod.invoke(server, chatComponent) - val chatmessagetype = if (system) systemType - else chatType - // CraftBukkit start - we run this through our processor first so we can get web links etc - val comp = fixComponent.invoke(null, chatComponent) - val packet = if (ppocC.getParameterCount == 3) ppocC.newInstance(comp, chatmessagetype, sysb.get(null)) - else ppocC.newInstance(comp, chatmessagetype) - this.sendAll(packet) - } catch { - case e: Exception => - TBMCCoreAPI.SendException("An error occurred while passing a vanilla message through the player list", e, module) - } - - private - - def sendAll(packet: Any) = try { // Some messages get sent by directly constructing a packet - sendAllMethod.invoke(plist, packet) - if (packet.getClass eq ppoc) { - val msgf = ppoc.getDeclaredField("a") - msgf.setAccessible(true) - MCChatUtils.forPublicPrivateChat(MCChatUtils.send(toPlainText.invoke(msgf.get(packet)).asInstanceOf[String])).subscribe() - } - } catch { - case e: Exception => - TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e, module) - } - }).stubOnly).asInstanceOf - plist = currentPL - var plc = dplc - while ( { - plc != null - }) { //Set all fields - for (f <- plc.getDeclaredFields) { - f.setAccessible(true) - val modf = f.getClass.getDeclaredField("modifiers") - modf.setAccessible(true) - modf.set(f, f.getModifiers & ~Modifier.FINAL) - f.set(mock, f.get(plist)) - } - plc = plc.getSuperclass - } - } - try server.getClass.getMethod("a", dplc).invoke(server, if (up) mock - else plist) - catch { - case e: NoSuchMethodException => - server.getClass.getMethod("a", Class.forName(server.getClass.getPackage.getName + ".PlayerList")).invoke(server, if (up) mock - else plist) - } - val pllf = csc.getDeclaredField("playerList") - pllf.setAccessible(true) - pllf.set(Bukkit.getServer, if (up) mock - else plist) - true - } -} \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala b/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala index 941c290..78ccce0 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala @@ -8,13 +8,13 @@ import discord4j.discordjson.json.{ApplicationCommandOptionData, ApplicationComm import java.lang.reflect.Method -class Command2DC extends Command2[ICommand2DC, Command2DCSender] { +class Command2DC extends Command2[ICommand2DC, Command2DCSender]('/', false) { override def registerCommand(command: ICommand2DC): Unit = { registerCommand(command, DiscordPlugin.dc.getApplicationInfo.block().getId.asLong()) } def registerCommand(command: ICommand2DC, appId: Long, guildId: Option[Long] = None): Unit = { - super.registerCommand(command, DiscordPlugin.getPrefix) //Needs to be configurable for the helps + super.registerCommand(command) //Needs to be configurable for the helps val greetCmdRequest = ApplicationCommandRequest.builder() .name(command.getCommandPath) //TODO: Main path .description("Connect your Minecraft account.") //TODO: Description diff --git a/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala b/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala index 598e3b8..b9d9887 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala @@ -7,10 +7,10 @@ import discord4j.core.`object`.entity.{Member, Message, User} import discord4j.core.event.domain.interaction.ChatInputInteractionEvent import java.util.Optional -import reactor.core.scala.publisher.javaOptional2ScalaOption; +import scala.jdk.OptionConverters._ class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sender { - val authorAsMember: Option[Member] = event.getInteraction.getMember + val authorAsMember: Option[Member] = event.getInteraction.getMember.toScala val author: User = event.getInteraction.getUser override def sendMessage(message: String): Unit = { if (message.isEmpty) return () @@ -20,5 +20,5 @@ class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sen override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message: _*)) - override def getName: String = authorAsMember.flatMap(_.getNickname).getOrElse(author.getUsername) + override def getName: String = authorAsMember.flatMap(_.getNickname.toScala).getOrElse(author.getUsername) } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/commands/DebugCommand.scala b/src/main/scala/buttondevteam/discordplugin/commands/DebugCommand.scala index e92b266..bc00edd 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/DebugCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/DebugCommand.scala @@ -5,22 +5,23 @@ import buttondevteam.discordplugin.listeners.CommonListeners import buttondevteam.lib.chat.{Command2, CommandClass} import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.{Member, User} -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono +import scala.jdk.OptionConverters._ @CommandClass(helpText = Array("Switches debug mode.")) class DebugCommand extends ICommand2DC { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { - SMono.justOrEmpty(sender.authorAsMember) + Mono.justOrEmpty(sender.authorAsMember.orNull) .switchIfEmpty(Option(sender.author) //Support DMs - .map((u: User) => SMono(u.asMember(DiscordPlugin.mainServer.getId))).getOrElse(SMono.empty)) + .map((u: User) => u.asMember(DiscordPlugin.mainServer.getId)).toJava.orElse(Mono.empty[Member])) .flatMap((m: Member) => DiscordPlugin.plugin.modRole.get .map(mr => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId)) - .switchIfEmpty(SMono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong == m.getId.asLong))) - .onErrorResume(_ => SMono.just(false)) //Role not found + .switchIfEmpty(Mono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong == m.getId.asLong))) + .onErrorResume(_ => Mono.just(false)) //Role not found .subscribe(success => { if (success) { - CommonListeners.debug = !CommonListeners.debug; + CommonListeners.debug = !CommonListeners.debug sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" else "disabled")) } else sender.sendMessage("you need to be a moderator to use this command.") diff --git a/src/main/scala/buttondevteam/discordplugin/commands/HelpCommand.scala b/src/main/scala/buttondevteam/discordplugin/commands/HelpCommand.scala index 77d51df..1a4260e 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/HelpCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/HelpCommand.scala @@ -6,11 +6,11 @@ import buttondevteam.lib.chat.{Command2, CommandClass} "Shows some info about a command or lists the available commands.")) class HelpCommand extends ICommand2DC { @Command2.Subcommand - def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = { - if (args == null || args.isEmpty) sender.sendMessage(getManager.getCommandsText) + def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg command: String): Boolean = { + if (command == null || command.isEmpty) sender.sendMessage(getManager.getCommandsText) else { - val ht = getManager.getHelpText(args) - if (ht == null) sender.sendMessage("Command not found: " + args) + val ht = getManager.getCommandNode(command).getData.getHelpText(sender) + if (ht == null) sender.sendMessage("Command not found: " + command) else sender.sendMessage(ht) } true diff --git a/src/main/scala/buttondevteam/discordplugin/commands/UserinfoCommand.scala b/src/main/scala/buttondevteam/discordplugin/commands/UserinfoCommand.scala index 839d665..06ba56d 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/UserinfoCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/UserinfoCommand.scala @@ -6,7 +6,6 @@ import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase.InfoTarget import discord4j.core.`object`.entity.{Message, User} -import reactor.core.scala.publisher.SFlux import scala.jdk.CollectionConverters.ListHasAsScala @@ -34,9 +33,9 @@ class UserinfoCommand extends ICommand2DC { private def getUsers(message: Message, args: String) = { val guild = message.getGuild.block if (guild == null) { //Private channel - SFlux(DiscordPlugin.dc.getUsers).filter(u => u.getUsername.equalsIgnoreCase(args)).collectSeq().block() + DiscordPlugin.dc.getUsers.filter(u => u.getUsername.equalsIgnoreCase(args)).collectList().block() } else - SFlux(guild.getMembers).filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectSeq().block() + guild.getMembers.filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectList().block() } } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala b/src/main/scala/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala index d6bf7aa..cca550d 100644 --- a/src/main/scala/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala @@ -5,7 +5,6 @@ import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.lib.TBMCDebugMessageEvent import discord4j.core.`object`.entity.channel.MessageChannel import org.bukkit.event.{EventHandler, Listener} -import reactor.core.scala.publisher.SMono object DebugMessageListener { private def SendMessage(message: String): Unit = { @@ -17,7 +16,7 @@ object DebugMessageListener { sb.append("```").append("\n") sb.append(if (message.length > 2000) message.substring(0, 2000) else message).append("\n") sb.append("```") - mc.flatMap((ch: MessageChannel) => SMono(ch.createMessage(sb.toString))).subscribe() + mc.flatMap((ch: MessageChannel) => ch.createMessage(sb.toString)).subscribe() } catch { case ex: Exception => ex.printStackTrace() diff --git a/src/main/scala/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala b/src/main/scala/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala index bd5826f..3bc2db0 100644 --- a/src/main/scala/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala @@ -9,7 +9,7 @@ import discord4j.core.`object`.entity.{Guild, Role} import org.apache.commons.lang.exception.ExceptionUtils import org.bukkit.Bukkit import org.bukkit.event.{EventHandler, Listener} -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono import java.util import java.util.stream.Collectors @@ -22,8 +22,8 @@ object ExceptionListenerModule { if (instance == null) return () try getChannel.flatMap(channel => { val coderRole = channel match { - case ch: GuildChannel => instance.pingRole(SMono(ch.getGuild)).get - case _ => SMono.empty + case ch: GuildChannel => instance.pingRole(ch.getGuild).get + case _ => Mono.empty } coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder else new StringBuilder(role.getMention).append("\n")) @@ -36,7 +36,7 @@ object ExceptionListenerModule { if (sb.length + stackTrace.length >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length) sb.append(stackTrace).append("\n") sb.append("```") - SMono(channel.createMessage(sb.toString)) + channel.createMessage(sb.toString) }) }).subscribe() catch { @@ -47,9 +47,9 @@ object ExceptionListenerModule { private var instance: ExceptionListenerModule = null - def getChannel: SMono[MessageChannel] = { + def getChannel: Mono[MessageChannel] = { if (instance != null) return instance.channel.get - SMono.empty + Mono.empty } } @@ -69,7 +69,7 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0) lastthrown.add(e.getException) lastsourcemsg.add(e.getSourceMessage) - e.setHandled() + e.setHandled(true) } /** @@ -80,7 +80,7 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { /** * The role to ping if an error occurs. Set to empty ('') to disable. */ - private def pingRole(guild: SMono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild) + private def pingRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild) override protected def enable(): Unit = { if (DPUtils.disableIfConfigError(this, channel)) return () diff --git a/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala b/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala index d924c09..8527c0e 100644 --- a/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala @@ -13,7 +13,7 @@ import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageCreateSpe import org.bukkit.Bukkit import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.{EventHandler, Listener} -import reactor.core.scala.publisher.{SFlux, SMono} +import reactor.core.publisher.Mono import java.util import java.util.Calendar @@ -52,7 +52,7 @@ object FunModule { ListC += 1 ListC - 1 } > 2) { // Lowered already - DPUtils.reply(message, SMono.empty, "stop it. You know the answer.").subscribe() + DPUtils.reply(message, Mono.empty, "stop it. You know the answer.").subscribe() lastlist = 0 lastlistp = Bukkit.getOnlinePlayers.size.toShort return true //Handled @@ -62,7 +62,7 @@ object FunModule { var next = 0 if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) - DPUtils.reply(message, SMono.empty, fm.serverReadyAnswers.get.get(next)).subscribe() + DPUtils.reply(message, Mono.empty, fm.serverReadyAnswers.get.get(next)).subscribe() return false //Still process it as a command/mcchat if needed } false @@ -78,17 +78,17 @@ object FunModule { || event.getCurrent.getStatus == Status.OFFLINE) return () //If it's not an offline -> online change fm.fullHouseChannel.get.filter((ch: MessageChannel) => ch.isInstanceOf[GuildChannel]) - .flatMap(channel => fm.fullHouseDevRole(SMono(channel.asInstanceOf[GuildChannel].getGuild)).get - .filterWhen(devrole => SMono(event.getMember) - .flatMap(m => SFlux(m.getRoles).any(_.getId.asLong == devrole.getId.asLong))) - .filterWhen(devrole => SMono(event.getGuild) - .flatMapMany(g => SFlux(g.getMembers).filter(_.getRoleIds.stream.anyMatch(_ == devrole.getId))) + .flatMap(channel => fm.fullHouseDevRole(channel.asInstanceOf[GuildChannel].getGuild).get + .filterWhen(devrole => event.getMember + .flatMap(m => m.getRoles.any(_.getId.asLong == devrole.getId.asLong))) + .filterWhen(devrole => event.getGuild + .flatMapMany(g => g.getMembers.filter(_.getRoleIds.stream.anyMatch(_ == devrole.getId))) .flatMap(_.getPresence).all(_.getStatus != Status.OFFLINE)) .filter(_ => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)) //This should stay so it checks this last .flatMap(_ => { lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime) - SMono(channel.createMessage(_.setContent("Full house!") - .setEmbed((ecs: LegacyEmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")))) + channel.createMessage(_.setContent("Full house!") + .setEmbed((ecs: LegacyEmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png"))) })).subscribe() } } @@ -98,7 +98,7 @@ class FunModule extends Component[DiscordPlugin] with Listener { * Questions that the bot will choose a random answer to give to. */ final private val serverReady: ConfigData[Array[String]] = - getConfig.getData("serverReady", () => Array[String]( + getConfig.getData("serverReady", Array[String]( "when will the server be open", "when will the server be ready", "when will the server be done", "when will the server be complete", "when will the server be finished", "when's the server ready", @@ -107,7 +107,7 @@ class FunModule extends Component[DiscordPlugin] with Listener { * Answers for a recognized question. Selected randomly. */ final private val serverReadyAnswers: ConfigData[util.ArrayList[String]] = - getConfig.getData("serverReadyAnswers", () => Lists.newArrayList(FunModule.serverReadyStrings: _*)) + getConfig.getData("serverReadyAnswers", Lists.newArrayList(FunModule.serverReadyStrings: _*)) private def createUsableServerReadyStrings(): Unit = IntStream.range(0, serverReadyAnswers.get.size).forEach((i: Int) => FunModule.usableServerReadyStrings.add(i.toShort)) @@ -125,7 +125,7 @@ class FunModule extends Component[DiscordPlugin] with Listener { /** * If all of the people who have this role are online, the bot will post a full house. */ - private def fullHouseDevRole(guild: SMono[Guild]) = DPUtils.roleData(getConfig, "fullHouseDevRole", "Developer", guild) + private def fullHouseDevRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "fullHouseDevRole", "Developer", guild) /** * The channel to post the full house to. diff --git a/src/main/scala/buttondevteam/discordplugin/listeners/CommonListeners.scala b/src/main/scala/buttondevteam/discordplugin/listeners/CommonListeners.scala index b038045..d2c6a6f 100644 --- a/src/main/scala/buttondevteam/discordplugin/listeners/CommonListeners.scala +++ b/src/main/scala/buttondevteam/discordplugin/listeners/CommonListeners.scala @@ -17,36 +17,35 @@ import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent} import reactor.core.Disposable import reactor.core.publisher.Mono -import reactor.core.scala.publisher.{SFlux, SMono} object CommonListeners { val timings = new Timings def register(dispatcher: EventDispatcher): Unit = { dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => { - SMono.just(event.getMessage).filter(_ => !DiscordPlugin.SafeMode) + Mono.just(event.getMessage).filter(_ => !DiscordPlugin.SafeMode) .filter(message => message.getAuthor.filter(!_.isBot).isPresent) .filter(message => !FunModule.executeMemes(message)) .filterWhen(message => { Option(Component.getComponents.get(classOf[MinecraftChatModule])).filter(_.isEnabled) - .map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event)) - .getOrElse(SMono.just(true)) //Wasn't handled, continue + .map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event).asInstanceOf[Mono[java.lang.Boolean]]) + .getOrElse(Mono.just(true)) //Wasn't handled, continue }) }).onErrorContinue((err, _) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe() dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => { if (!DiscordPlugin.SafeMode) FunModule.handleFullHouse(event) }) - SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent) - SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent) - SFlux(dispatcher.on(classOf[RoleUpdateEvent])).subscribe(GameRoleModule.handleRoleEvent) - SFlux(dispatcher.on(classOf[ChatInputInteractionEvent], event => { + dispatcher.on(classOf[RoleCreateEvent]).subscribe(GameRoleModule.handleRoleEvent) + dispatcher.on(classOf[RoleDeleteEvent]).subscribe(GameRoleModule.handleRoleEvent) + dispatcher.on(classOf[RoleUpdateEvent]).subscribe(GameRoleModule.handleRoleEvent) + dispatcher.on(classOf[ChatInputInteractionEvent], event => { if(event.getCommandName() equals "connect") { val asd = Mono.just(new ConnectCommand().`def`(new Command2DCSender(event), event.getOption("name").get.getValue.get.asString)) asd } else Mono.empty() - })).subscribe() + }).subscribe() } var debug = false diff --git a/src/main/scala/buttondevteam/discordplugin/listeners/MCListener.scala b/src/main/scala/buttondevteam/discordplugin/listeners/MCListener.scala index 84e50ae..c71d945 100644 --- a/src/main/scala/buttondevteam/discordplugin/listeners/MCListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/listeners/MCListener.scala @@ -11,7 +11,7 @@ import discord4j.common.util.Snowflake import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.{EventHandler, Listener} import reactor.core.publisher.Mono -import reactor.core.scala.publisher.javaOptional2ScalaOption +import scala.jdk.OptionConverters._ class MCListener extends Listener { @EventHandler def onPlayerJoin(e: PlayerJoinEvent): Unit = @@ -25,18 +25,18 @@ class MCListener extends Listener { @EventHandler def onGetInfo(e: TBMCPlayerGetInfoEvent): Unit = { Option(DiscordPlugin.SafeMode).filterNot(identity).flatMap(_ => Option(e.getPlayer.getAs(classOf[DiscordPlayer]))) .flatMap(dp => Option(dp.getDiscordID)).filter(_.nonEmpty) - .map(Snowflake.of).flatMap(id => DiscordPlugin.dc.getUserById(id).onErrorResume(_ => Mono.empty).blockOptional()) + .map(Snowflake.of).flatMap(id => DiscordPlugin.dc.getUserById(id).onErrorResume(_ => Mono.empty).blockOptional().toScala) .map(user => { e.addInfo("Discord tag: " + user.getUsername + "#" + user.getDiscriminator) user }) - .flatMap(user => user.asMember(DiscordPlugin.mainServer.getId).onErrorResume(t => Mono.empty).blockOptional()) - .flatMap(member => member.getPresence.blockOptional()) + .flatMap(user => user.asMember(DiscordPlugin.mainServer.getId).onErrorResume(t => Mono.empty).blockOptional().toScala) + .flatMap(member => member.getPresence.blockOptional().toScala) .map(pr => { e.addInfo(pr.getStatus.toString) pr }) - .flatMap(_.getActivity).foreach(activity => e.addInfo(s"${activity.getType}: ${activity.getName}")) + .flatMap(_.getActivity.toScala).foreach(activity => e.addInfo(s"${activity.getType}: ${activity.getName}")) } /*@EventHandler diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala index 8657c8d..3cfb921 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala @@ -6,13 +6,14 @@ import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC} import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayer} import buttondevteam.lib.TBMCSystemChatEvent +import buttondevteam.lib.architecture.config.IConfigData 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 org.bukkit.Bukkit -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono import java.lang.reflect.Method import java.util @@ -37,9 +38,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman val message: Message = null // TODO if (checkPerms(message, null)) return true else if (MCChatCustom.removeCustomChat(message.getChannelId)) - DPUtils.reply(message, SMono.empty, "channel connection removed.").subscribe() + DPUtils.reply(message, Mono.empty, "channel connection removed.").subscribe() else - DPUtils.reply(message, SMono.empty, "this channel isn't connected.").subscribe() + DPUtils.reply(message, Mono.empty, "this channel isn't connected.").subscribe() true } @@ -59,7 +60,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled")) .collect(Collectors.joining("\n")) if (toggle == null) { - DPUtils.reply(message, SMono.empty, "toggles:\n" + togglesString.get).subscribe() + DPUtils.reply(message, Mono.empty, "toggles:\n" + togglesString.get).subscribe() return true } val arg: String = toggle.toUpperCase @@ -67,7 +68,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman if (b.isEmpty) { val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg) if (bt == null) { - DPUtils.reply(message, SMono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe() + DPUtils.reply(message, Mono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe() return true } val add: Boolean = !(cc.brtoggles.contains(bt)) @@ -87,7 +88,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman //1 1 | 0 // XOR cc.toggles ^= (1 << b.get.id) - DPUtils.reply(message, SMono.empty, "'" + b.get.toString.toLowerCase + "' " + DPUtils.reply(message, Mono.empty, "'" + b.get.toString.toLowerCase + "' " + (if ((cc.toggles & (1 << b.get.id)) == 0) "disabled" else "enabled")).subscribe() true } @@ -105,8 +106,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman if (MCChatCustom.hasCustomChat(message.getChannelId)) { return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.") } - val chan: Optional[Channel] = Channel.getChannels.filter((ch: Channel) => ch.ID.equalsIgnoreCase(channelID) || (util.Arrays.stream(ch.IDs.get).anyMatch((cid: String) => cid.equalsIgnoreCase(channelID)))).findAny - if (!(chan.isPresent)) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW) + val chan: Optional[Channel] = Channel.getChannels.filter(ch => ch.getIdentifier.equalsIgnoreCase(channelID) + || util.Arrays.stream(ch.extraIdentifiers.get).anyMatch(cid => cid.equalsIgnoreCase(channelID))).findAny + if (!chan.isPresent) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW) DPUtils.reply(message, channel, "MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.").subscribe() return true } @@ -120,7 +122,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman DPUtils.reply(message, channel, "you need to connect your Minecraft account. On the main server in " + DPUtils.botmention + " do " + DiscordPlugin.getPrefix + "connect ").subscribe() return true } - val dcp: DiscordConnectedPlayer = DiscordConnectedPlayer.create(message.getAuthor.get, channel, chp.getUUID, Bukkit.getOfflinePlayer(chp.getUUID).getName, module) + val dcp: DiscordConnectedPlayer = DiscordConnectedPlayer.create(message.getAuthor.get, channel, chp.getUniqueId, Bukkit.getOfflinePlayer(chp.getUniqueId).getName, module) //Using a fake player with no login/logout, should be fine for this event val groupid: String = chan.get.getGroupID(dcp) if (groupid == null && !((chan.get.isInstanceOf[ChatRoom]))) { //ChatRooms don't allow it unless the user joins, which happens later @@ -175,6 +177,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).blockOptional().orElse("Unknown") + "&scope=bot&permissions=268509264>") } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala index 6f843d4..326e8af 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala @@ -3,7 +3,6 @@ package buttondevteam.discordplugin.mcchat import buttondevteam.core.ComponentManager import buttondevteam.discordplugin.* import buttondevteam.discordplugin.DPUtils.SpecExtensions -import buttondevteam.discordplugin.mcchat.playerfaker.{VanillaCommandListener, VanillaCommandListener14, VanillaCommandListener15} import buttondevteam.discordplugin.mcchat.sender.{DiscordPlayer, DiscordSender, DiscordSenderBase} import buttondevteam.lib.* import buttondevteam.lib.chat.{ChatMessage, TBMCChatAPI} @@ -13,6 +12,7 @@ import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} import discord4j.core.`object`.entity.{Member, Message, User} import discord4j.core.event.domain.message.MessageCreateEvent +import discord4j.core.spec.EmbedCreateSpec import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageEditSpec} import discord4j.rest.util.Color import org.bukkit.Bukkit @@ -20,14 +20,13 @@ 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 import java.util.concurrent.{LinkedBlockingQueue, TimeoutException} -import java.util.function.{Consumer, Predicate} +import java.util.function.{Consumer, Predicate, Supplier} import java.util.stream.Collectors -import scala.jdk.CollectionConverters.{ListHasAsScala, SetHasAsScala} +import scala.jdk.CollectionConverters.{IterableHasAsScala, ListHasAsScala, SetHasAsScala} import scala.jdk.OptionConverters.RichOptional object MCChatListener { @@ -87,45 +86,42 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { val se: util.AbstractMap.SimpleEntry[TBMCChatEvent, Instant] = sendevents.take // Wait until an element is available e = se.getKey time = se.getValue - val authorPlayer: String = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel.DisplayName.get) + "] " + // + val authorPlayer: String = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel.displayName.get) + "] " + // (if ("Minecraft" == e.getOrigin) "" else "[" + e.getOrigin.charAt(0) + "]") + DPUtils.sanitizeStringNoEscape(ChromaUtils.getDisplayName(e.getSender)) - val color: chat.Color = e.getChannel.Color.get - val embed: Consumer[LegacyEmbedCreateSpec] = (ecs: LegacyEmbedCreateSpec) => { - def foo(ecs: LegacyEmbedCreateSpec) = { - ecs.setDescription(e.getMessage).setColor(Color.of(color.getRed, color.getGreen, color.getBlue)) - val url: String = module.profileURL.get - e.getSender match { - case player: Player => - DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, - if (url.nonEmpty) url + "?type=minecraft&id=" + player.getUniqueId else null) - case dsender: DiscordSenderBase => - ecs.setAuthor(authorPlayer, - if (url.nonEmpty) url + "?type=discord&id=" + dsender.getUser.getId.asString else null, - dsender.getUser.getAvatarUrl) - case _ => - DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, null) - } - ecs.setTimestamp(time) + val color: chat.Color = e.getChannel.color.get + val embed = () => { + val ecs = EmbedCreateSpec.builder() + ecs.description(e.getMessage).color(Color.of(color.getRed, color.getGreen, color.getBlue)) + val url: String = module.profileURL.get + e.getSender match { + case player: Player => + DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, + if (url.nonEmpty) url + "?type=minecraft&id=" + player.getUniqueId else null) + case dsender: DiscordSenderBase => + ecs.author(authorPlayer, + if (url.nonEmpty) url + "?type=discord&id=" + dsender.getUser.getId.asString else null, + dsender.getUser.getAvatarUrl) + case _ => + DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, null) } - - foo(ecs) + ecs.timestamp(time) } val nanoTime: Long = System.nanoTime val doit = (lastmsgdata: MCChatUtils.LastMsgData) => { if (lastmsgdata.message == null || authorPlayer != lastmsgdata.message.getEmbeds.get(0).getAuthor.toScala.flatMap(_.getName.toScala).orNull || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 - || !(lastmsgdata.mcchannel.ID == e.getChannel.ID) + || !(lastmsgdata.mcchannel.getIdentifier == e.getChannel.getIdentifier) || lastmsgdata.content.length + e.getMessage.length + 1 > 2048) { - lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block + lastmsgdata.message = lastmsgdata.channel.createMessage(embed().build()).block lastmsgdata.time = nanoTime lastmsgdata.mcchannel = e.getChannel lastmsgdata.content = e.getMessage } else { lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated - lastmsgdata.message.edit((mes: LegacyMessageEditSpec) => mes.setEmbed(embed.andThen((ecs: LegacyEmbedCreateSpec) => ecs.setDescription(lastmsgdata.content))).^^()).block + lastmsgdata.message.edit().withEmbeds(embed().description(lastmsgdata.content).build()).block } } // Checks if the given channel is different than where the message was sent from @@ -145,7 +141,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { MCChatCustom.lastmsgCustom synchronized { MCChatCustom.lastmsgCustom.filterInPlace(lmd => { if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord - && e.getChannel.ID == lmd.mcchannel.ID //If it's from a command, the command msg has been deleted, so we need to send it + && e.getChannel.getIdentifier == lmd.mcchannel.getIdentifier //If it's from a command, the command msg has been deleted, so we need to send it && e.getGroupID() == lmd.groupID) { //Check if this is the group we want to test - #58 if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions doit(lmd) @@ -244,11 +240,11 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { private var recthread: Thread = null // Discord - def handleDiscord(ev: MessageCreateEvent): SMono[Boolean] = { + def handleDiscord(ev: MessageCreateEvent): Mono[Boolean] = { val author = Option(ev.getMessage.getAuthor.orElse(null)) val hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId) val prefix = DiscordPlugin.getPrefix - SMono(ev.getMessage.getChannel) + ev.getMessage.getChannel .filter(channel => isChatEnabled(channel, author, hasCustomChat)) .filter(channel => !isRunningMCChatCommand(channel, ev.getMessage.getContent, prefix)) .filter(channel => { @@ -313,7 +309,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { replaceUserMentions() def replaceChannelMentions(): Unit = { - for (ch <- SFlux(event.getGuild.flux).flatMap(_.getChannels).toIterable()) { + for (ch <- event.getGuild.flux.flatMap(_.getChannels).toIterable().asScala) { dmessage = dmessage.replace(ch.getMention, "#" + ch.getName) } } @@ -385,9 +381,9 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { else { val cmb = ChatMessage.builder(dsender, user, dmessage + getAttachmentText).fromCommand(false) if (clmd != null) - TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build, clmd.mcchannel) + TBMCChatAPI.sendChatMessage(cmb.permCheck(clmd.dcp).build, clmd.mcchannel) else - TBMCChatAPI.SendChatMessage(cmb.build) + TBMCChatAPI.sendChatMessage(cmb.build) true } } @@ -440,13 +436,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { val mcpackage = Bukkit.getServer.getClass.getPackage.getName if (!module.enableVanillaCommands.get) Bukkit.dispatchCommand(dsender, cmd) - else if (mcpackage.contains("1_12")) - VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd) - else if (mcpackage.contains("1_14")) - VanillaCommandListener14.runBukkitOrVanillaCommand(dsender, cmd) - else if (mcpackage.contains("1_15") || mcpackage.contains("1_16")) - VanillaCommandListener15.runBukkitOrVanillaCommand(dsender, cmd) - else + else // TODO: Vanilla command handling Bukkit.dispatchCommand(dsender, cmd) } catch { case e: NoClassDefFoundError => diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala index 1b6783f..38ab3ff 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala @@ -22,13 +22,13 @@ object MCChatPrivate { 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 p = Bukkit.getPlayer(mcp.getUniqueId) + val op = Bukkit.getOfflinePlayer(mcp.getUniqueId) val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule]) if (start) { - val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID, op.getName, mcm) + val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUniqueId, op.getName, mcm) MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender) - MCChatUtils.LoggedInPlayers.put(mcp.getUUID, sender) + MCChatUtils.LoggedInPlayers.put(mcp.getUniqueId, sender) if (p == null) { // Player is offline - If the player is online, that takes precedence MCChatUtils.callLoginEvents(sender) } diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala index 1cf1ae8..c077da2 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala @@ -20,8 +20,7 @@ import org.bukkit.entity.Player import org.bukkit.event.Event import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent} import org.bukkit.plugin.AuthorNagException -import reactor.core.publisher.{Flux as JFlux, Mono as JMono} -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.{Flux, Mono} import java.net.InetAddress import java.util @@ -36,6 +35,7 @@ import scala.collection.convert.ImplicitConversions.`map AsJavaMap` import scala.collection.mutable.ListBuffer import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava} import scala.jdk.javaapi.CollectionConverters.asScala +import scala.jdk.javaapi.OptionConverters._ object MCChatUtils { /** @@ -93,8 +93,7 @@ object MCChatUtils { .filter(_ => C.incrementAndGet > 0) //Always true .map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", ")) s(0) = s"$C player${if (C.get != 1) "s" else ""} online" - lmd.channel.asInstanceOf[TextChannel].edit((tce: LegacyTextChannelEditSpec) => - tce.setTopic(String.join("\n----\n", s: _*)).setReason("Player list update").^^()).subscribe //Don't wait + lmd.channel.asInstanceOf[TextChannel].edit().withTopic(String.join("\n----\n", s: _*)).withReason("Player list update").subscribe() //Don't wait } private[mcchat] def checkEssentials(p: Player): Boolean = { @@ -126,11 +125,11 @@ object MCChatUtils { else null.asInstanceOf } - def forPublicPrivateChat(action: SMono[MessageChannel] => SMono[_]): SMono[_] = { - if (notEnabled) return SMono.empty - val list = MCChatPrivate.lastmsgPerUser.map(data => action(SMono.just(data.channel))) + def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = { + if (notEnabled) return Mono.empty + val list = MCChatPrivate.lastmsgPerUser.map(data => action(Mono.just(data.channel))) .prepend(action(module.chatChannelMono)) - SMono(JMono.whenDelayError(list.asJava)) + Mono.whenDelayError(list.asJava) } /** @@ -140,14 +139,14 @@ object MCChatUtils { * @param toggle The toggle to check * @param hookmsg Whether the message is also sent from the hook */ - def forCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = { - if (notEnabled) return SMono.empty + def forCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { + if (notEnabled) return Mono.empty val list = List(if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) - forPublicPrivateChat(action) else SMono.empty) ++ + forPublicPrivateChat(action) else Mono.empty) ++ (if (toggle == null) MCChatCustom.lastmsgCustom else MCChatCustom.lastmsgCustom.filter(cc => (cc.toggles & (1 << toggle.id)) != 0)) - .map(_.channel).map(SMono.just).map(action) - SMono(JMono.whenDelayError(list.asJava)) + .map(_.channel).map(Mono.just).map(action) + Mono.whenDelayError(list.asJava) } /** @@ -157,15 +156,15 @@ object MCChatUtils { * @param sender The sender to check perms of or null to send to all that has it toggled * @param toggle The toggle to check or null to send to all allowed */ - def forAllowedCustomMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): SMono[_] = { - if (notEnabled) return SMono.empty + def forAllowedCustomMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): Mono[_] = { + if (notEnabled) return Mono.empty val st = MCChatCustom.lastmsgCustom.filter(clmd => { //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 & (1 << toggle.id)) == 0)) false //If null then allow else if (sender == null) true else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)) - }).map(cc => action.apply(SMono.just(cc.channel))) //TODO: Send error messages on channel connect + }).map(cc => action.apply(Mono.just(cc.channel))) //TODO: Send error messages on channel connect //Mono.whenDelayError((() => st.iterator).asInstanceOf[java.lang.Iterable[Mono[_]]]) //Can't convert as an iterator or inside the stream, but I can convert it as a stream - SMono.whenDelayError(st) + Mono.whenDelayError(st.asJava) } /** @@ -176,32 +175,32 @@ object MCChatUtils { * @param toggle The toggle to check or null to send to all allowed * @param hookmsg Whether the message is also sent from the hook */ - def forAllowedCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = { - if (notEnabled) return SMono.empty + def forAllowedCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { + if (notEnabled) return Mono.empty val cc = forAllowedCustomMCChat(action, sender, toggle) - if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return SMono.whenDelayError(Array(forPublicPrivateChat(action), cc)) - SMono.whenDelayError(Array(cc)) + if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc) + Mono.whenDelayError(cc) } - def send(message: String): SMono[MessageChannel] => SMono[_] = _.flatMap((mc: MessageChannel) => { + def send(message: String): Mono[MessageChannel] => Mono[_] = _.flatMap((mc: MessageChannel) => { resetLastMessage(mc) - SMono(mc.createMessage(DPUtils.sanitizeString(message))) + mc.createMessage(DPUtils.sanitizeString(message)) }) - def forAllowedMCChat(action: SMono[MessageChannel] => SMono[_], event: TBMCSystemChatEvent): SMono[_] = { - if (notEnabled) return SMono.empty - val list = new ListBuffer[SMono[_]] + def forAllowedMCChat(action: Mono[MessageChannel] => Mono[_], event: TBMCSystemChatEvent): Mono[_] = { + if (notEnabled) return Mono.empty + val list = new ListBuffer[Mono[_]] if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono)) for (data <- MCChatPrivate.lastmsgPerUser) if (event.shouldSendTo(getSender(data.channel.getId, data.user))) - list.append(action(SMono.just(data.channel))) //TODO: Only store ID? + list.append(action(Mono.just(data.channel))) //TODO: Only store ID? MCChatCustom.lastmsgCustom.filter(clmd => clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp)) - .map(clmd => action(SMono.just(clmd.channel))).foreach(elem => { + .map(clmd => action(Mono.just(clmd.channel))).foreach(elem => { list.append(elem) () }) - SMono.whenDelayError(list) + Mono.whenDelayError(list.asJava) } /** @@ -212,7 +211,7 @@ object MCChatUtils { .orElse(Option(getSender(ConnectedSenders, channel, author))) // This doesn't support the public chat, but it'll always return null for it .orElse(Option(getSender(UnconnectedSenders, channel, author))) // .orElse(Option(addSender(UnconnectedSenders, author, - new DiscordSender(author, SMono(DiscordPlugin.dc.getChannelById(channel)).block().asInstanceOf[MessageChannel])))) + new DiscordSender(author, DiscordPlugin.dc.getChannelById(channel).block().asInstanceOf[MessageChannel])))) .get } @@ -322,7 +321,7 @@ object MCChatUtils { callEventExcludingSome(new PlayerJoinEvent(dcp, "")) dcp.setLoggedIn(true) if (module != null) { - if (module.serverWatcher != null) module.serverWatcher.fakePlayers.add(dcp) + // TODO: ServerWatcher module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged in from Discord") } } @@ -345,7 +344,7 @@ object MCChatUtils { dcp.setLoggedIn(false) if (module != null) { module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged out from Discord") - if (module.serverWatcher != null) module.serverWatcher.fakePlayers.remove(dcp) + // TODO: ServerWatcher } } diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala index c0a6666..2a86078 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala @@ -16,7 +16,7 @@ import org.bukkit.event.player.* import org.bukkit.event.player.PlayerLoginEvent.Result import org.bukkit.event.server.{BroadcastMessageEvent, TabCompleteEvent} import org.bukkit.event.{EventHandler, EventPriority, Listener} -import reactor.core.scala.publisher.{SFlux, SMono} +import reactor.core.publisher.{Flux, Mono} class MCListener(val module: MinecraftChatModule) extends Listener { final private val muteRole = DPUtils.roleData(module.getConfig, "muteRole", "Muted") @@ -35,11 +35,11 @@ class MCListener(val module: MinecraftChatModule) extends Listener { val p = e.getPlayer val dp = TBMCPlayerBase.getPlayer(p.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer]) if (dp != null) - DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).^^().flatMap(user => - user.getPrivateChannel.^^().flatMap(chan => module.chatChannelMono.flatMap(cc => { + DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).flatMap(user => + user.getPrivateChannel.flatMap(chan => module.chatChannelMono.flatMap(cc => { MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, chan, p, module)) MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, cc, p, module)) //Stored per-channel - SMono.empty + Mono.empty }))).subscribe() val message = e.getJoinMessage sendJoinLeaveMessage(message, e.getPlayer) @@ -87,18 +87,17 @@ class MCListener(val module: MinecraftChatModule) extends Listener { if (!source.isPlayer) return () val p = TBMCPlayerBase.getPlayer(source.getPlayer.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer]) if (p == null) return () - DPUtils.ignoreError(SMono(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID))) - .flatMap(user => SMono(user.asMember(DiscordPlugin.mainServer.getId))) + DPUtils.ignoreError(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID)) + .flatMap(user => user.asMember(DiscordPlugin.mainServer.getId)) .flatMap(user => role.flatMap((r: Role) => { - def foo(r: Role): SMono[_] = { + def foo(r: Role): Mono[_] = { if (e.getValue) user.addRole(r.getId) else user.removeRole(r.getId) val modlog = module.modlogChannel.get - val msg = (if (e.getValue) "M" - else "Unm") + "uted user: " + user.getUsername + "#" + user.getDiscriminator + val msg = s"${(if (e.getValue) "M" else "Unm")}uted user: ${user.getUsername}#${user.getDiscriminator}" module.log(msg) - if (modlog != null) return modlog.flatMap((ch: MessageChannel) => SMono(ch.createMessage(msg))) - SMono.empty + if (modlog != null) return modlog.flatMap((ch: MessageChannel) => ch.createMessage(msg)) + Mono.empty } foo(r) @@ -119,7 +118,7 @@ class MCListener(val module: MinecraftChatModule) extends Listener { case _ => event.getSender.getName } //Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO - DiscordPlugin.mainServer.getEmojis.^^().filter(e => "YEEHAW" == e.getName).take(1).singleOrEmpty + DiscordPlugin.mainServer.getEmojis.filter(e => "YEEHAW" == e.getName).take(1).singleOrEmpty .map(Option.apply).defaultIfEmpty(Option.empty) .flatMap(yeehaw => MCChatUtils.forPublicPrivateChat(MCChatUtils.send(name + yeehaw.map(guildEmoji => " <:YEEHAW:" + guildEmoji.getId.asString + ">s").getOrElse(" YEEHAWs")))).subscribe() @@ -132,7 +131,7 @@ class MCListener(val module: MinecraftChatModule) extends Listener { val t = event.getBuffer.substring(i + 1) //0 if not found if (!t.startsWith("@")) return () val token = t.substring(1) - val x = DiscordPlugin.mainServer.getMembers.^^().flatMap(m => SFlux.just(m.getUsername, m.getNickname.orElse(""))) + val x = DiscordPlugin.mainServer.getMembers.flatMap(m => Flux.just(m.getUsername, m.getNickname.orElse(""))) .filter(_.startsWith(token)).map("@" + _).doOnNext(event.getCompletions.add(_)).blockLast() } diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala index 2a8c7d3..8e88d65 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala @@ -2,8 +2,6 @@ package buttondevteam.discordplugin.mcchat import buttondevteam.core.component.channel.Channel import buttondevteam.discordplugin.DPUtils.{MonoExtensions, SpecExtensions} -import buttondevteam.discordplugin.mcchat.playerfaker.ServerWatcher -import buttondevteam.discordplugin.mcchat.playerfaker.perm.LPInjector import buttondevteam.discordplugin.mcchat.sender.DiscordConnectedPlayer import buttondevteam.discordplugin.util.DPState import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordPlugin} @@ -14,7 +12,7 @@ import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.rest.util.Color import org.bukkit.Bukkit -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono import java.util import java.util.stream.Collectors @@ -33,27 +31,27 @@ class MinecraftChatModule extends Component[DiscordPlugin] { def getListener: MCChatListener = this.listener private var listener: MCChatListener = null - private[mcchat] var serverWatcher: ServerWatcher = null - private var lpInjector: LPInjector = null + private[mcchat] var serverWatcher = null + private var lpInjector = null private[mcchat] var disabling = false /** * A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command! */ val whitelistedCommands: ConfigData[util.ArrayList[String]] = getConfig.getData("whitelistedCommands", - () => Lists.newArrayList("list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins")) + Lists.newArrayList("list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins")) /** * The channel to use as the public Minecraft chat - everything public gets broadcasted here */ - val chatChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L) + val chatChannel: ConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L) - def chatChannelMono: SMono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get) + def chatChannelMono: Mono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get) /** * The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute */ - val modlogChannel: ReadOnlyConfigData[SMono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel") + val modlogChannel: ConfigData[Mono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel") /** * The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here */ @@ -117,7 +115,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] { val chconkeys = chcons.getKeys(false) for (chconkey <- chconkeys.asScala) { val chcon = chcons.getConfigurationSection(chconkey) - val mcch = Channel.getChannels.filter((ch: Channel) => ch.ID == chcon.getString("mcchid")).findAny + val mcch = Channel.getChannels.filter((ch: Channel) => ch.getIdentifier == chcon.getString("mcchid")).findAny val ch = DiscordPlugin.dc.getChannelById(Snowflake.of(chcon.getLong("chid"))).block val did = chcon.getLong("did") val user = DiscordPlugin.dc.getUserById(Snowflake.of(did)).block @@ -135,18 +133,9 @@ class MinecraftChatModule extends Component[DiscordPlugin] { } } } - try if (lpInjector == null) lpInjector = new LPInjector //new LPInjector(DiscordPlugin.plugin) - catch { - case e: Exception => - TBMCCoreAPI.SendException("Failed to init LuckPerms injector", e, this) - case e: NoClassDefFoundError => - log("No LuckPerms, not injecting") - //e.printStackTrace(); - } + // TODO: LPInjector if (addFakePlayersToBukkit.get) try { - serverWatcher = new ServerWatcher - serverWatcher.enableDisable(true) - log("Finished hooking into the server") + // TODO: Fake players } catch { case e: Exception => TBMCCoreAPI.SendException("Failed to hack the server (object)! Disable addFakePlayersToBukkit in the config.", e, this) @@ -189,8 +178,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] { serverUp.set(false) //Disable even if just the component is disabled because that way it won't falsely report crashes try //If it's not enabled it won't do anything if (serverWatcher != null) { - serverWatcher.enableDisable(false) - log("Finished unhooking the server") + // TODO: ServerWatcher } catch { case e: Exception => @@ -200,7 +188,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] { val chconsc = getConfig.getConfig.createSection("chcons") for (chcon <- chcons) { val chconc = chconsc.createSection(chcon.channel.getId.asString) - chconc.set("mcchid", chcon.mcchannel.ID) + chconc.set("mcchid", chcon.mcchannel.getIdentifier) chconc.set("chid", chcon.channel.getId.asLong) chconc.set("did", chcon.user.getId.asLong) chconc.set("mcuid", chcon.dcp.getUniqueId.toString) @@ -222,13 +210,13 @@ class MinecraftChatModule extends Component[DiscordPlugin] { */ private def sendStateMessage(color: Color, message: String) = MCChatUtils.forCustomAndAllMCChat(_.flatMap( - _.createEmbed(_.setColor(color).setTitle(message).^^()).^^() - .onErrorResume(_ => SMono.empty) + _.createEmbed(_.setColor(color).setTitle(message)) + .onErrorResume(_ => Mono.empty) ), ChannelconBroadcast.RESTART, hookmsg = false).block() private def sendStateMessage(color: Color, message: String, extra: String) = MCChatUtils.forCustomAndAllMCChat(_.flatMap( - _.createEmbed(_.setColor(color).setTitle(message).setDescription(extra).^^()).^^() - .onErrorResume(_ => SMono.empty) + _.createEmbed(_.setColor(color).setTitle(message).setDescription(extra)) + .onErrorResume(_ => Mono.empty) ), ChannelconBroadcast.RESTART, hookmsg = false).block() } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DelegatingMockMaker.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DelegatingMockMaker.scala deleted file mode 100644 index 0383b52..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DelegatingMockMaker.scala +++ /dev/null @@ -1,51 +0,0 @@ -package buttondevteam.discordplugin.mcchat.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: java.util.function.Function[MockedConstruction.Context, - MockCreationSettings[T]], handlerFactory: java.util.function.Function[MockedConstruction.Context, - MockHandler[T]], mockInitializer: MockedConstruction.MockInitializer[T]): MockMaker.ConstructionMockControl[T] = - this.mockMaker.createConstructionMock[T](`type`, settingsFactory, 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/scala/buttondevteam/discordplugin/mcchat/playerfaker/DiscordInventory.java b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DiscordInventory.java deleted file mode 100644 index 54cbd02..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/DiscordInventory.java +++ /dev/null @@ -1,218 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker; - -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.entity.HumanEntity; -import org.bukkit.event.inventory.InventoryType; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; - -import java.util.*; -import java.util.stream.IntStream; - -public class DiscordInventory implements Inventory { - private ItemStack[] items = new ItemStack[27]; - private List itemStacks = Arrays.asList(items); - - public int maxStackSize; - private static ItemStack emptyStack = new ItemStack(Material.AIR, 0); - - @Override - public int getSize() { - return items.length; - } - - @Override - public int getMaxStackSize() { - return maxStackSize; - } - - @Override - public void setMaxStackSize(int maxStackSize) { - this.maxStackSize = maxStackSize; - } - - @Override - public String getName() { - return "Discord inventory"; - } - - @Override - public ItemStack getItem(int index) { - if (index >= items.length) - return emptyStack; - else - return items[index]; - } - - @Override - public void setItem(int index, ItemStack item) { - if (index < items.length) - items[index] = item; - } - - @Override - public HashMap addItem(ItemStack... items) throws IllegalArgumentException { - return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything - } - - @Override - public HashMap removeItem(ItemStack... items) throws IllegalArgumentException { - return IntStream.range(0, items.length).collect(HashMap::new, (map, i) -> map.put(i, items[i]), HashMap::putAll); //Pretend that we can't add anything - } - - @Override - public ItemStack[] getContents() { - return items; - } - - @Override - public void setContents(ItemStack[] items) throws IllegalArgumentException { - this.items = items; - } - - @Override - public ItemStack[] getStorageContents() { - return items; - } - - @Override - public void setStorageContents(ItemStack[] items) throws IllegalArgumentException { - this.items = items; - } - - @SuppressWarnings("deprecation") - @Override - public boolean contains(int materialId) { - return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId); - } - - @Override - public boolean contains(Material material) throws IllegalArgumentException { - return itemStacks.stream().anyMatch(is -> is.getType() == material); - } - - @Override - public boolean contains(ItemStack item) { - return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == item.getAmount()); - } - - @SuppressWarnings("deprecation") - @Override - public boolean contains(int materialId, int amount) { - return itemStacks.stream().anyMatch(is -> is.getType().getId() == materialId && is.getAmount() == amount); - } - - @Override - public boolean contains(Material material, int amount) throws IllegalArgumentException { - return itemStacks.stream().anyMatch(is -> is.getType() == material && is.getAmount() == amount); - } - - @Override - public boolean contains(ItemStack item, int amount) { //Not correct implementation but whatever - return itemStacks.stream().anyMatch(is -> is.getType() == item.getType() && is.getAmount() == amount); - } - - @Override - public boolean containsAtLeast(ItemStack item, int amount) { - return false; - } - - @Override - @Deprecated - public HashMap all(int materialId) { - return new HashMap<>(); - } - - @Override - public HashMap all(Material material) throws IllegalArgumentException { - return new HashMap<>(); - } - - @Override - public HashMap all(ItemStack item) { - return new HashMap<>(); - } - - @Override - @Deprecated - public int first(int materialId) { - return -1; - } - - @Override - public int first(Material material) throws IllegalArgumentException { - return -1; - } - - @Override - public int first(ItemStack item) { - return -1; - } - - @Override - public int firstEmpty() { - return -1; - } - - @Override - @Deprecated - public void remove(int materialId) { - } - - @Override - public void remove(Material material) throws IllegalArgumentException { - } - - @Override - public void remove(ItemStack item) { - } - - @Override - public void clear(int index) { - if (index < items.length) - items[index] = null; - } - - @Override - public void clear() { - Arrays.fill(items, null); - } - - @Override - public List getViewers() { - return Collections.emptyList(); - } - - @Override - public String getTitle() { - return "Discord inventory"; - } - - @Override - public InventoryType getType() { - return InventoryType.CHEST; - } - - @Override - public InventoryHolder getHolder() { - return null; - } - - @SuppressWarnings("NullableProblems") - @Override - public ListIterator iterator() { - return itemStacks.listIterator(); - } - - @Override - public ListIterator iterator(int index) { - return itemStacks.listIterator(index); - } - - @Override - public Location getLocation() { - return null; - } -} diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/ServerWatcher.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/ServerWatcher.scala deleted file mode 100644 index 94cb475..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/ServerWatcher.scala +++ /dev/null @@ -1,94 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker - -import buttondevteam.discordplugin.mcchat.MCChatUtils -import buttondevteam.discordplugin.mcchat.sender.DiscordConnectedPlayer -import com.destroystokyo.paper.profile.CraftPlayerProfile -import net.bytebuddy.implementation.bind.annotation.IgnoreForBinding -import org.bukkit.entity.Player -import org.bukkit.{Bukkit, Server} -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 = Option.empty[DiscordConnectedPlayer] - method.getName match { - case "getPlayer" => - if (pc == 1 && (method.getParameterTypes()(0) == classOf[UUID])) - player = MCChatUtils.LoggedInPlayers.get(invocation.getArgument[UUID](0)) - case "getPlayerExact" => - if (pc == 1) { - val argument = invocation.getArgument(0) - player = MCChatUtils.LoggedInPlayers.values.find(_.getName.equalsIgnoreCase(argument)) - } - - /*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 Option.empty - if (player.isEmpty && name != null) - player = MCChatUtils.LoggedInPlayers.values.find(_.getName.equalsIgnoreCase(name)) - if (player.nonEmpty) - return new CraftPlayerProfile(player.get.getUniqueId, player.get.getName) - } - } - if (player.nonEmpty) return player.get - 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/scala/buttondevteam/discordplugin/mcchat/playerfaker/VCMDWrapper.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VCMDWrapper.scala deleted file mode 100644 index e49478b..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VCMDWrapper.scala +++ /dev/null @@ -1,54 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker - -import buttondevteam.discordplugin.mcchat.MinecraftChatModule -import buttondevteam.discordplugin.mcchat.sender.{DiscordSenderBase, IMCPlayer} -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 diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener.scala deleted file mode 100644 index 396aafd..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener.scala +++ /dev/null @@ -1,84 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker - -import buttondevteam.discordplugin.mcchat.sender.{DiscordSenderBase, IMCPlayer} -import net.minecraft.server.v1_12_R1.* -import org.bukkit.Bukkit -import org.bukkit.craftbukkit.v1_12_R1.command.VanillaCommandWrapper -import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer -import org.bukkit.craftbukkit.v1_12_R1.{CraftServer, CraftWorld} -import org.bukkit.entity.Player - -import java.util - -object VanillaCommandListener { - def runBukkitOrVanillaCommand(dsender: DiscordSenderBase, cmdstr: String): Boolean = { - val cmd = Bukkit.getServer.asInstanceOf[CraftServer].getCommandMap.getCommand(cmdstr.split(" ")(0).toLowerCase) - if (!dsender.isInstanceOf[Player] || !cmd.isInstanceOf[VanillaCommandWrapper]) - return Bukkit.dispatchCommand(dsender, cmdstr) // Unconnected users are treated well in vanilla cmds - if (!dsender.isInstanceOf[IMCPlayer[_]]) - throw new ClassCastException("dsender needs to implement IMCPlayer to use vanilla commands as it implements Player.") - val sender = dsender.asInstanceOf[IMCPlayer[_]] - val vcmd = cmd.asInstanceOf[VanillaCommandWrapper] - if (!vcmd.testPermission(sender)) return true - val icommandlistener = sender.getVanillaCmdListener.getListener.asInstanceOf[ICommandListener] - if (icommandlistener == null) return VCMDWrapper.compatResponse(dsender) - var args = cmdstr.split(" ") - args = util.Arrays.copyOfRange(args, 1, args.length) - try vcmd.dispatchVanillaCommand(sender, icommandlistener, args) - catch { - case commandexception: CommandException => - // Taken from CommandHandler - val chatmessage = new ChatMessage(commandexception.getMessage, commandexception.getArgs) - chatmessage.getChatModifier.setColor(EnumChatFormat.RED) - icommandlistener.sendMessage(chatmessage) - } - true - } -} - -class VanillaCommandListener[T <: DiscordSenderBase with IMCPlayer[T]] extends ICommandListener { - def getPlayer: T = this.player - - private var player: T = null.asInstanceOf - private var bukkitplayer: Player = null - - /** - * This constructor will only send raw vanilla messages to the sender in plain text. - * - * @param player The Discord sender player (the wrapper) - */ - def this(player: T) = { - this() - this.player = player - this.bukkitplayer = null - } - - /** - * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. - * - * @param player The Discord sender player (the wrapper) - * @param bukkitplayer The Bukkit player to send the raw message to - */ - def this(player: T, bukkitplayer: Player) = { - this() - this.player = player - this.bukkitplayer = bukkitplayer - if (bukkitplayer != null && !bukkitplayer.isInstanceOf[CraftPlayer]) - throw new ClassCastException("bukkitplayer must be a Bukkit player!") - } - - override def C_(): MinecraftServer = Bukkit.getServer.asInstanceOf[CraftServer].getServer - - override def a(oplevel: Int, cmd: String): Boolean = { //return oplevel <= 2; // Value from CommandBlockListenerAbstract, found what it is in EntityPlayer - Wait, that'd always allow OP commands - oplevel == 0 || player.isOp - } - - override def getName: String = player.getName - - override def getWorld: World = player.getWorld.asInstanceOf[CraftWorld].getHandle - - override def sendMessage(arg0: IChatBaseComponent): Unit = { - player.sendMessage(arg0.toPlainText) - if (bukkitplayer != null) bukkitplayer.asInstanceOf[CraftPlayer].getHandle.sendMessage(arg0) - } -} \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener14.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener14.scala deleted file mode 100644 index 3427718..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener14.scala +++ /dev/null @@ -1,84 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker - -import buttondevteam.discordplugin.mcchat.sender.{DiscordSenderBase, IMCPlayer} -import net.minecraft.server.v1_14_R1.* -import org.bukkit.Bukkit -import org.bukkit.command.CommandSender -import org.bukkit.craftbukkit.v1_14_R1.command.{ProxiedNativeCommandSender, VanillaCommandWrapper} -import org.bukkit.craftbukkit.v1_14_R1.entity.CraftPlayer -import org.bukkit.craftbukkit.v1_14_R1.{CraftServer, CraftWorld} -import org.bukkit.entity.Player - -import java.util - -object VanillaCommandListener14 { - def runBukkitOrVanillaCommand(dsender: DiscordSenderBase, cmdstr: String): Boolean = { - val cmd = Bukkit.getServer.asInstanceOf[CraftServer].getCommandMap.getCommand(cmdstr.split(" ")(0).toLowerCase) - if (!dsender.isInstanceOf[Player] || !cmd.isInstanceOf[VanillaCommandWrapper]) - return Bukkit.dispatchCommand(dsender, cmdstr) // Unconnected users are treated well in vanilla cmds - if (!dsender.isInstanceOf[IMCPlayer[_]]) - throw new ClassCastException("dsender needs to implement IMCPlayer to use vanilla commands as it implements Player.") - val sender = dsender.asInstanceOf[IMCPlayer[_]] // Don't use val on recursive interfaces :P - val vcmd = cmd.asInstanceOf[VanillaCommandWrapper] - if (!vcmd.testPermission(sender)) return true - val world = Bukkit.getWorlds.get(0).asInstanceOf[CraftWorld].getHandle - val icommandlistener = sender.getVanillaCmdListener.getListener.asInstanceOf[ICommandListener] - if (icommandlistener == null) return VCMDWrapper.compatResponse(dsender) - val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0), new Vec2F(0, 0), world, 0, sender.getName, new ChatComponentText(sender.getName), world.getMinecraftServer, null) - val pncs = new ProxiedNativeCommandSender(wrapper, sender, sender) - var args = cmdstr.split(" ") - args = util.Arrays.copyOfRange(args, 1, args.length) - try return vcmd.execute(pncs, cmd.getLabel, args) - catch { - case commandexception: CommandException => - // Taken from CommandHandler - val chatmessage = new ChatMessage(commandexception.getMessage, commandexception.a) - chatmessage.getChatModifier.setColor(EnumChatFormat.RED) - icommandlistener.sendMessage(chatmessage) - } - true - } -} - -class VanillaCommandListener14[T <: DiscordSenderBase with IMCPlayer[T]] extends ICommandListener { - def getPlayer: T = this.player - - private var player: T = null.asInstanceOf - private var bukkitplayer: Player = null - - /** - * This constructor will only send raw vanilla messages to the sender in plain text. - * - * @param player The Discord sender player (the wrapper) - */ - def this(player: T) = { - this() - this.player = player - this.bukkitplayer = null - } - - /** - * This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player. - * - * @param player The Discord sender player (the wrapper) - * @param bukkitplayer The Bukkit player to send the raw message to - */ - def this(player: T, bukkitplayer: Player) = { - this() - this.player = player - this.bukkitplayer = bukkitplayer - if (bukkitplayer != null && !bukkitplayer.isInstanceOf[CraftPlayer]) throw new ClassCastException("bukkitplayer must be a Bukkit player!") - } - - override def sendMessage(arg0: IChatBaseComponent): scala.Unit = { - player.sendMessage(arg0.getString) - if (bukkitplayer != null) bukkitplayer.asInstanceOf[CraftPlayer].getHandle.sendMessage(arg0) - } - - override def shouldSendSuccess = true - - override def shouldSendFailure = true - - override def shouldBroadcastCommands = true //Broadcast to in-game admins - override def getBukkitSender(commandListenerWrapper: CommandListenerWrapper): CommandSender = player -} \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener15.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener15.scala deleted file mode 100644 index 7650792..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/VanillaCommandListener15.scala +++ /dev/null @@ -1,123 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker - -import buttondevteam.discordplugin.mcchat.sender.{DiscordSenderBase, IMCPlayer} -import org.bukkit.Bukkit -import org.bukkit.command.{CommandSender, SimpleCommandMap} -import org.bukkit.entity.Player -import org.mockito.{Answers, Mockito} - -import java.lang.reflect.Modifier -import java.util - -/** - * Same as {@link VanillaCommandListener14} but with reflection - */ -object VanillaCommandListener15 { - private var vcwcl: Class[_] = null - private var nms: String = null - - /** - * This method will only send raw vanilla messages to the sender in plain text. - * - * @param player The Discord sender player (the wrapper) - */ - @throws[Exception] - def create[T <: DiscordSenderBase with IMCPlayer[T]](player: T): VanillaCommandListener15[T] = create(player, null) - - /** - * This method 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 - */ - @SuppressWarnings(Array("unchecked")) - @throws[Exception] - def create[T <: DiscordSenderBase with IMCPlayer[T]](player: T, bukkitplayer: Player): VanillaCommandListener15[T] = { - if (vcwcl == null) { - val pkg = Bukkit.getServer.getClass.getPackage.getName - vcwcl = Class.forName(pkg + ".command.VanillaCommandWrapper") - } - if (nms == null) { - val server = Bukkit.getServer - nms = server.getClass.getMethod("getServer").invoke(server).getClass.getPackage.getName //org.mockito.codegen - } - val iclcl = Class.forName(nms + ".ICommandListener") - Mockito.mock(classOf[VanillaCommandListener15[T]], - Mockito.withSettings.stubOnly.useConstructor(player, bukkitplayer) - .extraInterfaces(iclcl).defaultAnswer(invocation => { - if (invocation.getMethod.getName == "sendMessage") { - val icbc = invocation.getArgument(0) - player.sendMessage(icbc.getClass.getMethod("getString").invoke(icbc).asInstanceOf[String]) - if (bukkitplayer != null) { - val handle = bukkitplayer.getClass.getMethod("getHandle").invoke(bukkitplayer) - handle.getClass.getMethod("sendMessage", icbc.getClass).invoke(handle, icbc) - } - null - } - else if (!Modifier.isAbstract(invocation.getMethod.getModifiers)) invocation.callRealMethod - else if (invocation.getMethod.getReturnType eq classOf[Boolean]) true //shouldSend... shouldBroadcast... - else if (invocation.getMethod.getReturnType eq classOf[CommandSender]) player - else Answers.RETURNS_DEFAULTS.answer(invocation) - })) - } - - @throws[Exception] - def runBukkitOrVanillaCommand(dsender: DiscordSenderBase, cmdstr: String): Boolean = { - val server = Bukkit.getServer - val cmap = server.getClass.getMethod("getCommandMap").invoke(server).asInstanceOf[SimpleCommandMap] - val cmd = cmap.getCommand(cmdstr.split(" ")(0).toLowerCase) - if (!dsender.isInstanceOf[Player] || cmd == null || !vcwcl.isAssignableFrom(cmd.getClass)) - return Bukkit.dispatchCommand(dsender, cmdstr) // Unconnected users are treated well in vanilla cmds - if (!dsender.isInstanceOf[IMCPlayer[_]]) - throw new ClassCastException("dsender needs to implement IMCPlayer to use vanilla commands as it implements Player.") - val sender = dsender.asInstanceOf[IMCPlayer[_]] // Don't use val on recursive interfaces :P - if (!vcwcl.getMethod("testPermission", classOf[CommandSender]).invoke(cmd, sender).asInstanceOf[Boolean]) - return true - val cworld = Bukkit.getWorlds.get(0) - val world = cworld.getClass.getMethod("getHandle").invoke(cworld) - val icommandlistener = sender.getVanillaCmdListener.getListener - if (icommandlistener == null) return VCMDWrapper.compatResponse(dsender) - val clwcl = Class.forName(nms + ".CommandListenerWrapper") - val v3dcl = Class.forName(nms + ".Vec3D") - val v2fcl = Class.forName(nms + ".Vec2F") - val icbcl = Class.forName(nms + ".IChatBaseComponent") - val mcscl = Class.forName(nms + ".MinecraftServer") - val ecl = Class.forName(nms + ".Entity") - val cctcl = Class.forName(nms + ".ChatComponentText") - val iclcl = Class.forName(nms + ".ICommandListener") - val wrapper = clwcl.getConstructor(iclcl, v3dcl, v2fcl, world.getClass, classOf[Int], classOf[String], icbcl, mcscl, ecl) - .newInstance(icommandlistener, v3dcl.getConstructor(classOf[Double], classOf[Double], classOf[Double]) - .newInstance(0, 0, 0), v2fcl.getConstructor(classOf[Float], classOf[Float]) - .newInstance(0, 0), world, 0, sender.getName, cctcl.getConstructor(classOf[String]) - .newInstance(sender.getName), world.getClass.getMethod("getMinecraftServer").invoke(world), null) - /*val wrapper = new CommandListenerWrapper(icommandlistener, new Vec3D(0, 0, 0), - new Vec2F(0, 0), world, 0, sender.getName(), - new ChatComponentText(sender.getName()), world.getMinecraftServer(), null);*/ - val pncscl = Class.forName(vcwcl.getPackage.getName + ".ProxiedNativeCommandSender") - val pncs = pncscl.getConstructor(clwcl, classOf[CommandSender], classOf[CommandSender]) - .newInstance(wrapper, sender, sender) - var args = cmdstr.split(" ") - args = util.Arrays.copyOfRange(args, 1, args.length) - try return cmd.execute(pncs.asInstanceOf[CommandSender], cmd.getLabel, args) - catch { - case commandexception: Exception => - if (!(commandexception.getClass.getSimpleName == "CommandException")) throw commandexception - // Taken from CommandHandler - val cmcl = Class.forName(nms + ".ChatMessage") - val chatmessage = cmcl.getConstructor(classOf[String], classOf[Array[AnyRef]]) - .newInstance(commandexception.getMessage, Array[AnyRef](commandexception.getClass.getMethod("a").invoke(commandexception))) - val modifier = cmcl.getMethod("getChatModifier").invoke(chatmessage) - val ecfcl = Class.forName(nms + ".EnumChatFormat") - modifier.getClass.getMethod("setColor", ecfcl).invoke(modifier, ecfcl.getField("RED").get(null)) - icommandlistener.getClass.getMethod("sendMessage", icbcl).invoke(icommandlistener, chatmessage) - } - true - } -} - -class VanillaCommandListener15[T <: DiscordSenderBase with IMCPlayer[T]] protected(var player: T, val bukkitplayer: Player) { - if (bukkitplayer != null && !bukkitplayer.getClass.getSimpleName.endsWith("CraftPlayer")) - throw new ClassCastException("bukkitplayer must be a Bukkit player!") - - def getPlayer: T = this.player -} \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/perm/LPInjector.java b/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/perm/LPInjector.java deleted file mode 100644 index 43d824c..0000000 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/playerfaker/perm/LPInjector.java +++ /dev/null @@ -1,207 +0,0 @@ -package buttondevteam.discordplugin.mcchat.playerfaker.perm; - -import org.bukkit.event.Listener; - -public final class LPInjector implements Listener { //Disable login event for LuckPerms - /*private final LPBukkitPlugin plugin; - private final BukkitConnectionListener connectionListener; - private final Set deniedLogin; - private final Field detectedCraftBukkitOfflineMode; - private final Method printCraftBukkitOfflineModeError; - private final Field PERMISSIBLE_BASE_ATTACHMENTS_FIELD; - private final Method convertAndAddAttachments; - private final Method getActive; - private final Method setOldPermissible; - private final Method getOldPermissible; - - public LPInjector(DiscordPlugin dp) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException { - LPBukkitBootstrap bs = (LPBukkitBootstrap) Bukkit.getPluginManager().getPlugin("LuckPerms"); - Field field = LPBukkitBootstrap.class.getDeclaredField("plugin"); - field.setAccessible(true); - plugin = (LPBukkitPlugin) field.get(bs); - MCChatUtils.addStaticExcludedPlugin(PlayerLoginEvent.class, "LuckPerms"); - MCChatUtils.addStaticExcludedPlugin(PlayerQuitEvent.class, "LuckPerms"); - - field = LPBukkitPlugin.class.getDeclaredField("connectionListener"); - field.setAccessible(true); - connectionListener = (BukkitConnectionListener) field.get(plugin); - field = connectionListener.getClass().getDeclaredField("deniedLogin"); - field.setAccessible(true); - //noinspection unchecked - deniedLogin = (Set) field.get(connectionListener); - field = connectionListener.getClass().getDeclaredField("detectedCraftBukkitOfflineMode"); - field.setAccessible(true); - detectedCraftBukkitOfflineMode = field; - printCraftBukkitOfflineModeError = connectionListener.getClass().getDeclaredMethod("printCraftBukkitOfflineModeError"); - printCraftBukkitOfflineModeError.setAccessible(true); - - //PERMISSIBLE_FIELD = DiscordFakePlayer.class.getDeclaredField("perm"); - //PERMISSIBLE_FIELD.setAccessible(true); //Hacking my own plugin, while we're at it - PERMISSIBLE_BASE_ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments"); - PERMISSIBLE_BASE_ATTACHMENTS_FIELD.setAccessible(true); - - convertAndAddAttachments = LuckPermsPermissible.class.getDeclaredMethod("convertAndAddAttachments", Collection.class); - convertAndAddAttachments.setAccessible(true); - getActive = LuckPermsPermissible.class.getDeclaredMethod("getActive"); - getActive.setAccessible(true); - setOldPermissible = LuckPermsPermissible.class.getDeclaredMethod("setOldPermissible", PermissibleBase.class); - setOldPermissible.setAccessible(true); - getOldPermissible = LuckPermsPermissible.class.getDeclaredMethod("getOldPermissible"); - getOldPermissible.setAccessible(true); - - TBMCCoreAPI.RegisterEventsForExceptions(this, dp); - } - - - //Code copied from LuckPerms - me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener - @EventHandler(priority = EventPriority.LOWEST) - public void onPlayerLogin(PlayerLoginEvent e) {*/ - /* Called when the player starts logging into the server. - At this point, the users data should be present and loaded. */ - - /*if (!(e.getPlayer() instanceof DiscordConnectedPlayer)) - return; //Normal players must be handled by the plugin - - final DiscordConnectedPlayer player = (DiscordConnectedPlayer) e.getPlayer(); - - if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) { - plugin.getLogger().info("Processing login for " + player.getUniqueId() + " - " + player.getName()); - } - - final User user = plugin.getUserManager().getIfLoaded(player.getUniqueId()); - - /* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */ - /*if (user == null) { - deniedLogin.add(player.getUniqueId()); - - if (!plugin.getConnectionListener().getUniqueConnections().contains(player.getUniqueId())) { - - plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() + - " doesn't have data pre-loaded, they have never been processed during pre-login in this session." + - " - denying login."); - - try { - if ((Boolean) detectedCraftBukkitOfflineMode.get(connectionListener)) { - printCraftBukkitOfflineModeError.invoke(connectionListener); - - Component reason = TranslationManager.render(Message.LOADING_STATE_ERROR_CB_OFFLINE_MODE.build(), player.getLocale()); - e.disallow(PlayerLoginEvent.Result.KICK_OTHER, LegacyComponentSerializer.legacySection().serialize(reason)); - return; - } - } catch (IllegalAccessException | InvocationTargetException ex) { - ex.printStackTrace(); - } - - } else { - plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() + - " doesn't currently have data pre-loaded, but they have been processed before in this session." + - " - denying login."); - } - - Component reason = TranslationManager.render(Message.LOADING_STATE_ERROR.build(), player.getLocale()); - e.disallow(PlayerLoginEvent.Result.KICK_OTHER, LegacyComponentSerializer.legacySection().serialize(reason)); - return; - } - - // User instance is there, now we can inject our custom Permissible into the player. - // Care should be taken at this stage to ensure that async tasks which manipulate bukkit data check that the player is still online. - try { - // get the existing PermissibleBase held by the player - PermissibleBase oldPermissible = player.getPerm(); - - // Make a new permissible for the user - LuckPermsPermissible lpPermissible = new LuckPermsPermissible(player, user, this.plugin); - - // Inject into the player - inject(player, lpPermissible, oldPermissible); - - } catch (Throwable t) { - plugin.getLogger().warn("Exception thrown when setting up permissions for " + - player.getUniqueId() + " - " + player.getName() + " - denying login."); - t.printStackTrace(); - - Component reason = TranslationManager.render(Message.LOADING_SETUP_ERROR.build(), player.getLocale()); - e.disallow(PlayerLoginEvent.Result.KICK_OTHER, LegacyComponentSerializer.legacySection().serialize(reason)); - return; - } - - this.plugin.getContextManager().signalContextUpdate(player); - } - - // Wait until the last priority to unload, so plugins can still perform permission checks on this event - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerQuit(PlayerQuitEvent e) { - if (!(e.getPlayer() instanceof DiscordConnectedPlayer)) - return; - - final DiscordConnectedPlayer player = (DiscordConnectedPlayer) e.getPlayer(); - - connectionListener.handleDisconnect(player.getUniqueId()); - - // perform unhooking from bukkit objects 1 tick later. - // this allows plugins listening after us on MONITOR to still have intact permissions data - this.plugin.getBootstrap().getServer().getScheduler().runTaskLater(this.plugin.getBootstrap(), () -> { - // Remove the custom permissible - try { - uninject(player); - } catch (Exception ex) { - ex.printStackTrace(); - } - - // Handle auto op - if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) { - player.setOp(false); - } - - // remove their contexts cache - this.plugin.getContextManager().onPlayerQuit(player); - }, 1L); - } - - //me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector - private void inject(DiscordConnectedPlayer player, LuckPermsPermissible newPermissible, PermissibleBase oldPermissible) throws IllegalAccessException, InvocationTargetException { - - // seems we have already injected into this player. - if (oldPermissible instanceof LuckPermsPermissible) { - throw new IllegalStateException("LPPermissible already injected into player " + player.toString()); - } - - // Move attachments over from the old permissible - - //noinspection unchecked - List attachments = (List) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible); - - convertAndAddAttachments.invoke(newPermissible, attachments); - attachments.clear(); - oldPermissible.clearPermissions(); - - // Setup the new permissible - ((AtomicBoolean) getActive.invoke(newPermissible)).set(true); - setOldPermissible.invoke(newPermissible, oldPermissible); - - // inject the new instance - player.setPerm(newPermissible); - } - - private void uninject(DiscordConnectedPlayer player) throws Exception { - - // gets the players current permissible. - PermissibleBase permissible = player.getPerm(); - - // only uninject if the permissible was a luckperms one. - if (permissible instanceof LuckPermsPermissible) { - LuckPermsPermissible lpPermissible = ((LuckPermsPermissible) permissible); - - // clear all permissions - lpPermissible.clearPermissions(); - - // set to inactive - ((AtomicBoolean) getActive.invoke(lpPermissible)).set(false); - - // handle the replacement permissible. - // just inject a dummy class. this is used when we know the player is about to quit the server. - player.setPerm(DummyPermissibleBase.INSTANCE); - - } - }*/ -} diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordConnectedPlayer.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordConnectedPlayer.scala index abfa522..3f29872 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordConnectedPlayer.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordConnectedPlayer.scala @@ -1,12 +1,12 @@ package buttondevteam.discordplugin.mcchat.sender import buttondevteam.discordplugin.mcchat.MinecraftChatModule -import buttondevteam.discordplugin.mcchat.playerfaker.{DiscordInventory, VCMDWrapper} import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.channel.MessageChannel import org.bukkit.* import org.bukkit.attribute.{Attribute, AttributeInstance, AttributeModifier} import org.bukkit.entity.{Entity, Player} +import org.bukkit.event.inventory.InventoryType import org.bukkit.event.player.{AsyncPlayerChatEvent, PlayerTeleportEvent} import org.bukkit.inventory.{Inventory, PlayerInventory} import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo} @@ -31,10 +31,6 @@ object DiscordConnectedPlayer { 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 { @@ -64,7 +60,8 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u 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)) + private val vanillaCmdListener = null // TODO + private val inventory: PlayerInventory = if (module == null) null else Bukkit.createInventory(this, InventoryType.PLAYER).asInstanceOf override def isPermissionSet(name: String): Boolean = this.origPerm.isPermissionSet(name) @@ -97,7 +94,7 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u override def setDisplayName(displayName: String): Unit = this.displayName = displayName - override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener + override def getVanillaCmdListener = this.vanillaCmdListener def isLoggedIn: Boolean = this.loggedIn @@ -197,6 +194,8 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u override def getGameMode = GameMode.SPECTATOR + override def getInventory: PlayerInventory = inventory + //noinspection ScalaDeprecation /*@SuppressWarnings(Array("deprecation")) override def spigot: super.Spigot = new super.Spigot() { override def getRawAddress: InetSocketAddress = null diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayerSender.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayerSender.scala index 6683c02..f5132f3 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayerSender.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayerSender.scala @@ -1,7 +1,6 @@ package buttondevteam.discordplugin.mcchat.sender import buttondevteam.discordplugin.mcchat.MinecraftChatModule -import buttondevteam.discordplugin.mcchat.playerfaker.VCMDWrapper import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.channel.MessageChannel import org.bukkit.entity.Player @@ -25,9 +24,8 @@ object DiscordPlayerSender { } abstract class DiscordPlayerSender(user: User, 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 getVanillaCmdListener = null override def sendMessage(message: String): Unit = { player.sendMessage(message) @@ -35,7 +33,7 @@ abstract class DiscordPlayerSender(user: User, channel: MessageChannel, var play } override def sendMessage(messages: Array[String]): Unit = { - player.sendMessage(messages) + player.sendMessage(messages*) super.sendMessage(messages) } } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSender.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSender.scala index 1006618..866b845 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSender.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSender.scala @@ -7,15 +7,16 @@ import org.bukkit.command.CommandSender import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo} import org.bukkit.plugin.Plugin import org.bukkit.{Bukkit, Server} -import reactor.core.scala.publisher.SMono +import reactor.core.publisher.Mono +import scala.jdk.OptionConverters._ import java.util class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender { private val perm = new PermissibleBase(this) private val name: String = Option(pname) - .orElse(Option(user).flatMap(u => SMono(u.asMember(DiscordPlugin.mainServer.getId)) - .onErrorResume(_ => SMono.empty).blockOption() + .orElse(Option(user).flatMap(u => u.asMember(DiscordPlugin.mainServer.getId) + .onErrorResume(_ => Mono.empty).blockOptional().toScala .map(u => u.getDisplayName))) .getOrElse("Discord user") diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/IMCPlayer.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/IMCPlayer.scala index 09ecda2..7c40ed3 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/IMCPlayer.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/IMCPlayer.scala @@ -1,8 +1,7 @@ package buttondevteam.discordplugin.mcchat.sender -import buttondevteam.discordplugin.mcchat.playerfaker.VCMDWrapper import org.bukkit.entity.Player trait IMCPlayer[T] extends Player { - def getVanillaCmdListener: VCMDWrapper + def getVanillaCmdListener: Null // TODO } \ No newline at end of file diff --git a/src/main/scala/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala b/src/main/scala/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala index a115918..68b993f 100644 --- a/src/main/scala/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/mccommands/DiscordMCCommand.scala @@ -5,6 +5,7 @@ import buttondevteam.discordplugin.mcchat.sender.{DiscordPlayer, DiscordSenderBa import buttondevteam.discordplugin.mcchat.{MCChatUtils, MinecraftChatModule} import buttondevteam.discordplugin.util.DPState import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} +import buttondevteam.lib.chat.commands.MCCommandSettings import buttondevteam.lib.chat.{Command2, CommandClass, ICommand2MC} import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer, TBMCPlayerBase} import discord4j.core.`object`.ExtendedInvite @@ -19,7 +20,8 @@ import java.lang.reflect.Method "Discord", "This command allows performing Discord-related actions." )) class DiscordMCCommand extends ICommand2MC { - @Command2.Subcommand def accept(player: Player): Boolean = { + @Command2.Subcommand + def accept(player: Player): Boolean = { if (checkSafeMode(player)) return true val did = ConnectCommand.WaitingToConnect.get(player.getName) if (did == null) { @@ -35,7 +37,8 @@ import java.lang.reflect.Method true } - @Command2.Subcommand def decline(player: Player): Boolean = { + @Command2.Subcommand + def decline(player: Player): Boolean = { if (checkSafeMode(player)) return true val did = ConnectCommand.WaitingToConnect.remove(player.getName) if (did == null) { @@ -46,17 +49,21 @@ import java.lang.reflect.Method true } - @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = Array( + @Command2.Subcommand(helpText = Array( "Reload Discord plugin", "Reloads the config. To apply some changes, you may need to also run /discord restart." - )) def reload(sender: CommandSender): Unit = + )) + @MCCommandSettings(permGroup = MCCommandSettings.MOD_GROUP) + 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( + @Command2.Subcommand(helpText = Array( "Restart the plugin", // "This command disables and then enables the plugin." // - )) def restart(sender: CommandSender): Unit = { + )) + @MCCommandSettings(permGroup = MCCommandSettings.MOD_GROUP) + def restart(sender: CommandSender): Unit = { val task: Runnable = () => { def foo(): Unit = { if (!DiscordPlugin.plugin.tryReloadConfig) { @@ -87,14 +94,16 @@ import java.lang.reflect.Method @Command2.Subcommand(helpText = Array( "Version command", - "Prints the plugin version")) def version(sender: CommandSender): Unit = { - sender.sendMessage(VersionCommand.getVersion) + "Prints the plugin version")) + def version(sender: CommandSender): Unit = { + sender.sendMessage(VersionCommand.getVersion*) } @Command2.Subcommand(helpText = Array( "Invite", "Shows an invite link to the server" - )) def invite(sender: CommandSender): Unit = { + )) + def invite(sender: CommandSender): Unit = { if (checkSafeMode(sender)) { return () } diff --git a/src/main/scala/buttondevteam/discordplugin/role/GameRoleModule.scala b/src/main/scala/buttondevteam/discordplugin/role/GameRoleModule.scala index ad8faa1..e5e4749 100644 --- a/src/main/scala/buttondevteam/discordplugin/role/GameRoleModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/role/GameRoleModule.scala @@ -1,7 +1,6 @@ package buttondevteam.discordplugin.role import buttondevteam.core.ComponentManager -import buttondevteam.discordplugin.DPUtils.{FluxExtensions, MonoExtensions} import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.lib.architecture.{Component, ComponentMetadata} import discord4j.core.`object`.entity.Role @@ -9,7 +8,7 @@ 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.scala.publisher.SMono +import reactor.core.publisher.Mono import java.util.Collections import scala.jdk.CollectionConverters.SeqHasAsJava @@ -30,14 +29,14 @@ import scala.jdk.CollectionConverters.SeqHasAsJava val role = roleCreateEvent.getRole if (!notMainServer(role)) { grm.isGameRole(role).flatMap(b => { - if (!b) SMono.empty //Deleted or not a game role + if (!b) Mono.empty //Deleted or not a game role else { GameRoles.add(role.getName) if (logChannel != null) logChannel.flatMap(_.createMessage("Added " + role.getName + " as game role." + - " If you don't want this, change the role's color from the game role color.").^^()) + " If you don't want this, change the role's color from the game role color.")) else - SMono.empty + Mono.empty } }).subscribe() () @@ -48,7 +47,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava if (role == null) return () if (notMainServer(role)) return () if (GameRoles.remove(role.getName) && logChannel != null) - logChannel.flatMap(_.createMessage("Removed " + role.getName + " as a game role.").^^()).subscribe() + logChannel.flatMap(_.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!") @@ -60,21 +59,21 @@ import scala.jdk.CollectionConverters.SeqHasAsJava grm.isGameRole(cr).flatMap(isGameRole => { if (!isGameRole) if (GameRoles.remove(or.getName) && logChannel != null) - logChannel.flatMap(_.createMessage("Removed " + or.getName + " as a game role because its color changed.").^^()) + logChannel.flatMap(_.createMessage("Removed " + or.getName + " as a game role because its color changed.")) else - SMono.empty + Mono.empty else if (GameRoles.contains(or.getName) && or.getName == cr.getName) - SMono.empty + Mono.empty else { 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) - logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Changed game role from " + or.getName + " to " + cr.getName + ".").^^()) + logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Changed game role from " + or.getName + " to " + cr.getName + ".")) else - logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + cr.getName + " as game role because it has the color of one.").^^()) + logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + cr.getName + " as game role because it has the color of one.")) else - SMono.empty + Mono.empty } }).subscribe() case _ => @@ -88,7 +87,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava override protected def enable(): Unit = { getPlugin.manager.registerCommand(command) - GameRoles = DiscordPlugin.mainServer.getRoles.^^().filterWhen(this.isGameRole).map(_.getName).collectSeq().block().asJava + GameRoles = DiscordPlugin.mainServer.getRoles.filterWhen(this.isGameRole).map(_.getName).collectList().block() } override protected def disable(): Unit = getPlugin.manager.unregisterCommand(command) @@ -101,14 +100,16 @@ import scala.jdk.CollectionConverters.SeqHasAsJava * 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 + final private val roleColor = getConfig.getData("roleColor", Color.of(149, 165, 166), + rgb => Color.of(Integer.parseInt(rgb.asInstanceOf[String].substring(1), 16)), + color => String.format("#%08x", color.getRGB), true) - private def isGameRole(r: Role): SMono[Boolean] = { - if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return SMono.just(false) //Only allow on the main server + private def isGameRole(r: Role): Mono[java.lang.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(_.hasHigherRoles(Collections.singleton(r.getId)).^^().asInstanceOf).defaultIfEmpty(false) //Below one of our roles - else SMono.just(false) + DiscordPlugin.dc.getSelf.flatMap(u => u.asMember(DiscordPlugin.mainServer.getId)) + .flatMap(_.hasHigherRoles(Collections.singleton(r.getId))).defaultIfEmpty(false) //Below one of our roles + else Mono.just(false) } } \ No newline at end of file