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
This commit is contained in:
Norbi Peti 2023-04-24 04:42:24 +02:00
parent 8d63394b55
commit f909eb4779
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
40 changed files with 274 additions and 1405 deletions

View file

@ -12,25 +12,23 @@ scalaVersion := "3.1.0"
resolvers += "spigot-repo" at "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" resolvers += "spigot-repo" at "https://hub.spigotmc.org/nexus/content/repositories/snapshots/"
resolvers += "jitpack.io" at "https://jitpack.io" resolvers += "jitpack.io" at "https://jitpack.io"
resolvers += "paper-repo" at "https://papermc.io/repo/repository/maven-public/"
resolvers += Resolver.mavenLocal resolvers += Resolver.mavenLocal
// assembly / assemblyOption := (assembly / assemblyOption).value.copy(includeScala = false) // assembly / assemblyOption := (assembly / assemblyOption).value.copy(includeScala = false)
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT" % Provided, "org.spigotmc" % "spigot" % "1.19.4-R0.1-SNAPSHOT" % Provided,
"org.spigotmc" % "spigot" % "1.12.2-R0.1-SNAPSHOT" % Provided, "io.papermc.paper" % "paper-api" % "1.19.4-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,
"com.discord4j" % "discord4j-core" % "3.2.2", "com.discord4j" % "discord4j-core" % "3.2.2",
"org.slf4j" % "slf4j-jdk14" % "1.7.36", "org.slf4j" % "slf4j-jdk14" % "1.7.36",
"com.vdurmont" % "emoji-java" % "5.1.1", "com.vdurmont" % "emoji-java" % "5.1.1",
"org.mockito" % "mockito-core" % "4.6.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 // https://mvnrepository.com/artifact/org.immutables/value
"org.immutables" % "value" % "2.9.0" % "provided", "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.ess3" % "EssentialsX" % "2.17.1" % Provided,
"net.luckperms" % "api" % "5.4" % Provided, "net.luckperms" % "api" % "5.4" % Provided,
) )
@ -141,3 +139,5 @@ saveConfigComments := {
} }
Compile / resourceGenerators += saveConfigComments Compile / resourceGenerators += saveConfigComments
//scalacOptions ++= Seq("-release", "17", "--verbose")
scalacOptions ++= Seq("-release", "17")

View file

@ -1 +0,0 @@
lombok.var.flagUsage = ALLOW

View file

@ -1,2 +1,2 @@
sbt.version=1.5.8 sbt.version=1.8.2
scala.version=3.0.0 scala.version=3.0.0

View file

@ -3,4 +3,4 @@
resolvers += Resolver.mavenLocal 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"

View file

@ -4,7 +4,7 @@ import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin.mcchat.MCChatUtils import buttondevteam.discordplugin.mcchat.MCChatUtils
import discord4j.core.`object`.entity.Message import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import javax.annotation.Nullable import javax.annotation.Nullable
@ -20,7 +20,7 @@ object ChromaBot {
* *
* @param message The message to send, duh (use [[MessageChannel.createMessage]]) * @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() MCChatUtils.forPublicPrivateChat(message).subscribe()
/** /**
@ -29,7 +29,7 @@ object ChromaBot {
* @param message The message to send, duh * @param message The message to send, duh
* @param toggle The toggle type for channelcon * @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() MCChatUtils.forCustomAndAllMCChat(message.apply, toggle, hookmsg = false).subscribe()
def updatePlayerList(): Unit = def updatePlayerList(): Unit =

View file

@ -3,11 +3,11 @@ package buttondevteam.discordplugin
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.{Component, ConfigData, IHaveConfig, ReadOnlyConfigData} import buttondevteam.lib.architecture.{Component, ConfigData, IHaveConfig, ReadOnlyConfigData}
import discord4j.common.util.Snowflake 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.`object`.entity.{Guild, Message, Role}
import discord4j.core.spec.EmbedCreateSpec
import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacySpec} import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacySpec}
import reactor.core.publisher.{Flux, Mono} import reactor.core.publisher.{Flux, Mono}
import reactor.core.scala.publisher.{SFlux, SMono}
import java.util import java.util
import java.util.Comparator import java.util.Comparator
@ -19,8 +19,8 @@ object DPUtils {
private val URL_PATTERN = Pattern.compile("https?://\\S*") private val URL_PATTERN = Pattern.compile("https?://\\S*")
private val FORMAT_PATTERN = Pattern.compile("[*_~]") private val FORMAT_PATTERN = Pattern.compile("[*_~]")
def embedWithHead(ecs: LegacyEmbedCreateSpec, displayname: String, playername: String, profileUrl: String): LegacyEmbedCreateSpec = def embedWithHead(ecs: EmbedCreateSpec.Builder, displayname: String, playername: String, profileUrl: String): EmbedCreateSpec.Builder =
ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png") ecs.author(displayname, profileUrl, s"https://minotar.net/avatar/$playername/32.png")
/** /**
* Removes §[char] colour codes from strings & escapes them for Discord <br> * Removes §[char] colour codes from strings & escapes them for Discord <br>
@ -68,34 +68,26 @@ object DPUtils {
else DiscordPlugin.plugin.getLogger else DiscordPlugin.plugin.getLogger
} }
def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[SMono[MessageChannel]] = def channelData(config: IHaveConfig, key: String): ConfigData[Mono[MessageChannel]] =
config.getReadOnlyDataPrimDef(key, 0L, (id: Any) => config.getData(key, id => getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])),
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) (_: 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]] = def roleData(config: IHaveConfig, key: String, defName: String): ConfigData[Mono[Role]] =
roleData(config, key, defName, SMono.just(DiscordPlugin.mainServer)) roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer))
/** /**
* Needs to be a [[ConfigData]] for checking if it's set * 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 roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): ConfigData[Mono[Role]] = config.getData(key, name => {
def foo(name: Any): SMono[Role] = { if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) Mono.empty[Role]
if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) return SMono.empty[Role] else guild.flatMapMany(_.getRoles).filter(_.getName == name).onErrorResume(e => {
guild.flatMapMany(_.getRoles).filter((r: Role) => r.getName == name).onErrorResume((e: Throwable) => { getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage)
def foo(e: Throwable): SMono[Role] = { Mono.empty[Role]
getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage) }).next
SMono.empty[Role] }, _ => defName, defName, true)
}
foo(e) def snowflakeData(config: IHaveConfig, key: String, defID: Long): ConfigData[Snowflake] =
}).next config.getData(key, id => Snowflake.of(id.asInstanceOf[Long]), _.asLong, defID, true)
}
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)
/** /**
* Mentions the <b>bot channel</b>. Useful for help texts. * Mentions the <b>bot channel</b>. Useful for help texts.
@ -132,7 +124,7 @@ object DPUtils {
*/ */
def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = { def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = {
//noinspection ConstantConditions //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 var path: String = null
try { try {
if (component != null) Component.setComponentEnabled(component, false) 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 original The original message to reply to
* @param channel The channel to send the message in, defaults to the original * @param channel The channel to send the message in, defaults to the original
* @param message The message to send * @param message The message to send
* @return A mono to send the message * @return A mono to send the message
*/ */
def reply(original: Message, @Nullable channel: MessageChannel, message: String): SMono[Message] = { def reply(original: Message, @Nullable channel: MessageChannel, message: String): Mono[Message] = {
val ch = if (channel == null) SMono(original.getChannel) val ch = if (channel == null) original.getChannel
else SMono.just(channel) else Mono.just(channel)
reply(original, ch, message) reply(original, ch, message)
} }
/** /**
* @see #reply(Message, MessageChannel, String) * @see #reply(Message, MessageChannel, String)
*/ */
def reply(original: Message, ch: SMono[MessageChannel], message: String): SMono[Message] = def reply(original: Message, ch: Mono[MessageChannel], message: String): Mono[Message] =
ch.flatMap(channel => SMono(channel.createMessage((if (original.getAuthor.isPresent) ch.flatMap(channel => channel.createMessage((if (original.getAuthor.isPresent)
original.getAuthor.get.getMention + ", " original.getAuthor.get.getMention + ", "
else "") + message))) else "") + message))
def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">" def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">"
@ -184,31 +176,19 @@ object DPUtils {
* @param id The channel ID * @param id The channel ID
* @return A message channel * @return A message channel
*/ */
def getMessageChannel(key: String, id: Snowflake): SMono[MessageChannel] = { def getMessageChannel(key: String, id: Snowflake): Mono[MessageChannel] = {
if (id.asLong == 0L) return SMono.empty[MessageChannel] if (id.asLong == 0L) return Mono.empty[MessageChannel]
SMono(DiscordPlugin.dc.getChannelById(id)).onErrorResume(e => { DiscordPlugin.dc.getChannelById(id).onErrorResume(e => {
def foo(e: Throwable) = { getLogger.warning(s"Failed to get channel data for $key=$id - ${e.getMessage}")
getLogger.warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage) Mono.empty[Channel]
SMono.empty }).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel]])
}
foo(e)
}).filter(ch => ch.isInstanceOf[MessageChannel]).cast[MessageChannel]
} }
def getMessageChannel(config: ConfigData[Snowflake]): SMono[MessageChannel] = def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] =
getMessageChannel(config.getPath, config.get) getMessageChannel(config.getPath, config.get)
def ignoreError[T](mono: SMono[T]): SMono[T] = mono.onErrorResume((_: Throwable) => SMono.empty) def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.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)
}
implicit class SpecExtensions[T <: LegacySpec[_]](spec: T) { implicit class SpecExtensions[T <: LegacySpec[_]](spec: T) {
def ^^(): Unit = () def ^^(): Unit = ()

View file

@ -32,12 +32,12 @@ import org.bukkit.command.CommandSender
import org.bukkit.configuration.file.YamlConfiguration import org.bukkit.configuration.file.YamlConfiguration
import org.mockito.internal.util.MockUtil import org.mockito.internal.util.MockUtil
import reactor.core.Disposable import reactor.core.Disposable
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import reactor.core.scala.publisher.scalaOption2JavaOptional
import java.io.File import java.io.File
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.util.Optional import java.util.Optional
import scala.jdk.OptionConverters.*
@ButtonPlugin.ConfigOpts(disableConfigGen = true) object DiscordPlugin { @ButtonPlugin.ConfigOpts(disableConfigGen = true) object DiscordPlugin {
private[discordplugin] var dc: GatewayDiscordClient = null 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. * 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) => { private def mainServer = getIConfig.getData("mainServer", id => { //It attempts to get the default as well
def foo(id: Any): Option[Guild] = { //It attempts to get the default as well if (id.asInstanceOf[Long] == 0L) Option.empty
if (id.asInstanceOf[Long] == 0L) Option.empty else DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long]))
else SMono.fromPublisher(DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long]))) .onErrorResume(t => {
.onErrorResume((t: Throwable) => { getLogger.warning("Failed to get guild: " + t.getMessage)
getLogger.warning("Failed to get guild: " + t.getMessage); Mono.empty
SMono.empty }).blockOptional().toScala
}).blockOption() }, g => g.map(_.getId.asLong).getOrElse(0L), 0L, true)
}
foo(id)
}, (g: Option[Guild]) => (g.map(_.getId.asLong): Option[Long]).getOrElse(0L))
/** /**
* The (bot) channel to use for Discord commands like /role. * 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. * The role that allows using mod-only Discord commands.
* If empty (&#39;&#39;), then it will only allow for the owner. * If empty (&#39;&#39;), 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. * 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) CommonListeners.register(DiscordPlugin.dc.getEventDispatcher)
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener, this) TBMCCoreAPI.RegisterEventsForExceptions(new MCListener, this)
TBMCCoreAPI.RegisterUserClass(classOf[DiscordPlayer], () => new DiscordPlayer) TBMCCoreAPI.RegisterUserClass(classOf[DiscordPlayer], () => new DiscordPlayer)
ChromaGamerBase.addConverter((sender: CommandSender) => sender match { ChromaGamerBase.addConverter {
case dsender: DiscordSenderBase => Some(dsender.getChromaUser) case dsender: DiscordSenderBase => Some(dsender.getChromaUser).toJava
case _ => None case _ => None.toJava
}) }
IHaveConfig.pregenConfig(this, null)
ChromaBot.enabled = true //Initialize ChromaBot ChromaBot.enabled = true //Initialize ChromaBot
Component.registerComponent(this, new GeneralEventBroadcasterModule) Component.registerComponent(this, new GeneralEventBroadcasterModule)
Component.registerComponent(this, new MinecraftChatModule) Component.registerComponent(this, new MinecraftChatModule)

View file

@ -7,7 +7,7 @@ import buttondevteam.lib.architecture.{Component, ComponentMetadata}
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import com.google.gson.JsonParser import com.google.gson.JsonParser
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.collection.mutable import scala.collection.mutable
@ -86,8 +86,8 @@ import scala.collection.mutable
} }
} }
def sendMsg(ch: SMono[MessageChannel], msg: String) = def sendMsg(ch: Mono[MessageChannel], msg: String) =
ch.asJava().flatMap(c => c.createMessage(msg)).flatMap(_.pin).subscribe() ch.flatMap(c => c.createMessage(msg)).flatMap(_.pin).subscribe()
if (msgsb.nonEmpty) sendMsg(channel.get(), msgsb.toString()) if (msgsb.nonEmpty) sendMsg(channel.get(), msgsb.toString())
if (modmsgsb.nonEmpty) sendMsg(modChannel.get(), modmsgsb.toString()) if (modmsgsb.nonEmpty) sendMsg(modChannel.get(), modmsgsb.toString())

View file

@ -16,9 +16,7 @@ import buttondevteam.lib.architecture.{Component, ComponentMetadata}
@ComponentMetadata(enabledByDefault = false) class GeneralEventBroadcasterModule extends Component[DiscordPlugin] { @ComponentMetadata(enabledByDefault = false) class GeneralEventBroadcasterModule extends Component[DiscordPlugin] {
override protected def enable(): Unit = try { override protected def enable(): Unit = try {
PlayerListWatcher.hookUpDown(true, this) // TODO: Removed for now
log("Finished hooking into the player list")
GeneralEventBroadcasterModule.hooked = true
} catch { } catch {
case e: Exception => case e: Exception =>
TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e, this) 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 { override protected def disable(): Unit = try {
if (!GeneralEventBroadcasterModule.hooked) return () 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 { } catch {
case e: Exception => case e: Exception =>
TBMCCoreAPI.SendException("Error while hacking the player list!", e, this) TBMCCoreAPI.SendException("Error while hacking the player list!", e, this)

View file

@ -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
}
}

View file

@ -8,13 +8,13 @@ import discord4j.discordjson.json.{ApplicationCommandOptionData, ApplicationComm
import java.lang.reflect.Method import java.lang.reflect.Method
class Command2DC extends Command2[ICommand2DC, Command2DCSender] { class Command2DC extends Command2[ICommand2DC, Command2DCSender]('/', false) {
override def registerCommand(command: ICommand2DC): Unit = { override def registerCommand(command: ICommand2DC): Unit = {
registerCommand(command, DiscordPlugin.dc.getApplicationInfo.block().getId.asLong()) registerCommand(command, DiscordPlugin.dc.getApplicationInfo.block().getId.asLong())
} }
def registerCommand(command: ICommand2DC, appId: Long, guildId: Option[Long] = None): Unit = { 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() val greetCmdRequest = ApplicationCommandRequest.builder()
.name(command.getCommandPath) //TODO: Main path .name(command.getCommandPath) //TODO: Main path
.description("Connect your Minecraft account.") //TODO: Description .description("Connect your Minecraft account.") //TODO: Description

View file

@ -7,10 +7,10 @@ import discord4j.core.`object`.entity.{Member, Message, User}
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent import discord4j.core.event.domain.interaction.ChatInputInteractionEvent
import java.util.Optional import java.util.Optional
import reactor.core.scala.publisher.javaOptional2ScalaOption; import scala.jdk.OptionConverters._
class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sender { 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 val author: User = event.getInteraction.getUser
override def sendMessage(message: String): Unit = { override def sendMessage(message: String): Unit = {
if (message.isEmpty) return () 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 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)
} }

View file

@ -5,22 +5,23 @@ import buttondevteam.discordplugin.listeners.CommonListeners
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
import discord4j.common.util.Snowflake import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.{Member, User} 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.")) @CommandClass(helpText = Array("Switches debug mode."))
class DebugCommand extends ICommand2DC { class DebugCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
override def `def`(sender: Command2DCSender): Boolean = { override def `def`(sender: Command2DCSender): Boolean = {
SMono.justOrEmpty(sender.authorAsMember) Mono.justOrEmpty(sender.authorAsMember.orNull)
.switchIfEmpty(Option(sender.author) //Support DMs .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 .flatMap((m: Member) => DiscordPlugin.plugin.modRole.get
.map(mr => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId)) .map(mr => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId))
.switchIfEmpty(SMono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong == m.getId.asLong))) .switchIfEmpty(Mono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong == m.getId.asLong)))
.onErrorResume(_ => SMono.just(false)) //Role not found .onErrorResume(_ => Mono.just(false)) //Role not found
.subscribe(success => { .subscribe(success => {
if (success) { if (success) {
CommonListeners.debug = !CommonListeners.debug; CommonListeners.debug = !CommonListeners.debug
sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" else "disabled")) sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" else "disabled"))
} else } else
sender.sendMessage("you need to be a moderator to use this command.") sender.sendMessage("you need to be a moderator to use this command.")

View file

@ -6,11 +6,11 @@ import buttondevteam.lib.chat.{Command2, CommandClass}
"Shows some info about a command or lists the available commands.")) "Shows some info about a command or lists the available commands."))
class HelpCommand extends ICommand2DC { class HelpCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = { def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg command: String): Boolean = {
if (args == null || args.isEmpty) sender.sendMessage(getManager.getCommandsText) if (command == null || command.isEmpty) sender.sendMessage(getManager.getCommandsText)
else { else {
val ht = getManager.getHelpText(args) val ht = getManager.getCommandNode(command).getData.getHelpText(sender)
if (ht == null) sender.sendMessage("Command not found: " + args) if (ht == null) sender.sendMessage("Command not found: " + command)
else sender.sendMessage(ht) else sender.sendMessage(ht)
} }
true true

View file

@ -6,7 +6,6 @@ import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget import buttondevteam.lib.player.ChromaGamerBase.InfoTarget
import discord4j.core.`object`.entity.{Message, User} import discord4j.core.`object`.entity.{Message, User}
import reactor.core.scala.publisher.SFlux
import scala.jdk.CollectionConverters.ListHasAsScala import scala.jdk.CollectionConverters.ListHasAsScala
@ -34,9 +33,9 @@ class UserinfoCommand extends ICommand2DC {
private def getUsers(message: Message, args: String) = { private def getUsers(message: Message, args: String) = {
val guild = message.getGuild.block val guild = message.getGuild.block
if (guild == null) { //Private channel 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 else
SFlux(guild.getMembers).filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectSeq().block() guild.getMembers.filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectList().block()
} }
} }

View file

@ -5,7 +5,6 @@ import buttondevteam.discordplugin.DiscordPlugin
import buttondevteam.lib.TBMCDebugMessageEvent import buttondevteam.lib.TBMCDebugMessageEvent
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.scala.publisher.SMono
object DebugMessageListener { object DebugMessageListener {
private def SendMessage(message: String): Unit = { private def SendMessage(message: String): Unit = {
@ -17,7 +16,7 @@ object DebugMessageListener {
sb.append("```").append("\n") sb.append("```").append("\n")
sb.append(if (message.length > 2000) message.substring(0, 2000) else message).append("\n") sb.append(if (message.length > 2000) message.substring(0, 2000) else message).append("\n")
sb.append("```") sb.append("```")
mc.flatMap((ch: MessageChannel) => SMono(ch.createMessage(sb.toString))).subscribe() mc.flatMap((ch: MessageChannel) => ch.createMessage(sb.toString)).subscribe()
} catch { } catch {
case ex: Exception => case ex: Exception =>
ex.printStackTrace() ex.printStackTrace()

View file

@ -9,7 +9,7 @@ import discord4j.core.`object`.entity.{Guild, Role}
import org.apache.commons.lang.exception.ExceptionUtils import org.apache.commons.lang.exception.ExceptionUtils
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import java.util import java.util
import java.util.stream.Collectors import java.util.stream.Collectors
@ -22,8 +22,8 @@ object ExceptionListenerModule {
if (instance == null) return () if (instance == null) return ()
try getChannel.flatMap(channel => { try getChannel.flatMap(channel => {
val coderRole = channel match { val coderRole = channel match {
case ch: GuildChannel => instance.pingRole(SMono(ch.getGuild)).get case ch: GuildChannel => instance.pingRole(ch.getGuild).get
case _ => SMono.empty case _ => Mono.empty
} }
coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder
else new StringBuilder(role.getMention).append("\n")) 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) if (sb.length + stackTrace.length >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length)
sb.append(stackTrace).append("\n") sb.append(stackTrace).append("\n")
sb.append("```") sb.append("```")
SMono(channel.createMessage(sb.toString)) channel.createMessage(sb.toString)
}) })
}).subscribe() }).subscribe()
catch { catch {
@ -47,9 +47,9 @@ object ExceptionListenerModule {
private var instance: ExceptionListenerModule = null private var instance: ExceptionListenerModule = null
def getChannel: SMono[MessageChannel] = { def getChannel: Mono[MessageChannel] = {
if (instance != null) return instance.channel.get 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) if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0)
lastthrown.add(e.getException) lastthrown.add(e.getException)
lastsourcemsg.add(e.getSourceMessage) 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. * 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 = { override protected def enable(): Unit = {
if (DPUtils.disableIfConfigError(this, channel)) return () if (DPUtils.disableIfConfigError(this, channel)) return ()

View file

@ -13,7 +13,7 @@ import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageCreateSpe
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.scala.publisher.{SFlux, SMono} import reactor.core.publisher.Mono
import java.util import java.util
import java.util.Calendar import java.util.Calendar
@ -52,7 +52,7 @@ object FunModule {
ListC += 1 ListC += 1
ListC - 1 ListC - 1
} > 2) { // Lowered already } > 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 lastlist = 0
lastlistp = Bukkit.getOnlinePlayers.size.toShort lastlistp = Bukkit.getOnlinePlayers.size.toShort
return true //Handled return true //Handled
@ -62,7 +62,7 @@ object FunModule {
var next = 0 var next = 0
if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings()
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) 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 return false //Still process it as a command/mcchat if needed
} }
false false
@ -78,17 +78,17 @@ object FunModule {
|| event.getCurrent.getStatus == Status.OFFLINE) || event.getCurrent.getStatus == Status.OFFLINE)
return () //If it's not an offline -> online change return () //If it's not an offline -> online change
fm.fullHouseChannel.get.filter((ch: MessageChannel) => ch.isInstanceOf[GuildChannel]) fm.fullHouseChannel.get.filter((ch: MessageChannel) => ch.isInstanceOf[GuildChannel])
.flatMap(channel => fm.fullHouseDevRole(SMono(channel.asInstanceOf[GuildChannel].getGuild)).get .flatMap(channel => fm.fullHouseDevRole(channel.asInstanceOf[GuildChannel].getGuild).get
.filterWhen(devrole => SMono(event.getMember) .filterWhen(devrole => event.getMember
.flatMap(m => SFlux(m.getRoles).any(_.getId.asLong == devrole.getId.asLong))) .flatMap(m => m.getRoles.any(_.getId.asLong == devrole.getId.asLong)))
.filterWhen(devrole => SMono(event.getGuild) .filterWhen(devrole => event.getGuild
.flatMapMany(g => SFlux(g.getMembers).filter(_.getRoleIds.stream.anyMatch(_ == devrole.getId))) .flatMapMany(g => g.getMembers.filter(_.getRoleIds.stream.anyMatch(_ == devrole.getId)))
.flatMap(_.getPresence).all(_.getStatus != Status.OFFLINE)) .flatMap(_.getPresence).all(_.getStatus != Status.OFFLINE))
.filter(_ => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)) //This should stay so it checks this last .filter(_ => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)) //This should stay so it checks this last
.flatMap(_ => { .flatMap(_ => {
lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime) lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime)
SMono(channel.createMessage(_.setContent("Full house!") 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")))) .setEmbed((ecs: LegacyEmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")))
})).subscribe() })).subscribe()
} }
} }
@ -98,7 +98,7 @@ class FunModule extends Component[DiscordPlugin] with Listener {
* Questions that the bot will choose a random answer to give to. * Questions that the bot will choose a random answer to give to.
*/ */
final private val serverReady: ConfigData[Array[String]] = 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 open", "when will the server be ready",
"when will the server be done", "when will the server be complete", "when will the server be done", "when will the server be complete",
"when will the server be finished", "when's the server ready", "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. * Answers for a recognized question. Selected randomly.
*/ */
final private val serverReadyAnswers: ConfigData[util.ArrayList[String]] = 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 = private def createUsableServerReadyStrings(): Unit =
IntStream.range(0, serverReadyAnswers.get.size).forEach((i: Int) => FunModule.usableServerReadyStrings.add(i.toShort)) 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. * 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. * The channel to post the full house to.

View file

@ -17,36 +17,35 @@ import discord4j.core.event.domain.message.MessageCreateEvent
import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent} import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent}
import reactor.core.Disposable import reactor.core.Disposable
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scala.publisher.{SFlux, SMono}
object CommonListeners { object CommonListeners {
val timings = new Timings val timings = new Timings
def register(dispatcher: EventDispatcher): Unit = { def register(dispatcher: EventDispatcher): Unit = {
dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => { 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 => message.getAuthor.filter(!_.isBot).isPresent)
.filter(message => !FunModule.executeMemes(message)) .filter(message => !FunModule.executeMemes(message))
.filterWhen(message => { .filterWhen(message => {
Option(Component.getComponents.get(classOf[MinecraftChatModule])).filter(_.isEnabled) Option(Component.getComponents.get(classOf[MinecraftChatModule])).filter(_.isEnabled)
.map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event)) .map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event).asInstanceOf[Mono[java.lang.Boolean]])
.getOrElse(SMono.just(true)) //Wasn't handled, continue .getOrElse(Mono.just(true)) //Wasn't handled, continue
}) })
}).onErrorContinue((err, _) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe() }).onErrorContinue((err, _) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe()
dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => { dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => {
if (!DiscordPlugin.SafeMode) if (!DiscordPlugin.SafeMode)
FunModule.handleFullHouse(event) FunModule.handleFullHouse(event)
}) })
SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent) dispatcher.on(classOf[RoleCreateEvent]).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent) dispatcher.on(classOf[RoleDeleteEvent]).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[RoleUpdateEvent])).subscribe(GameRoleModule.handleRoleEvent) dispatcher.on(classOf[RoleUpdateEvent]).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[ChatInputInteractionEvent], event => { dispatcher.on(classOf[ChatInputInteractionEvent], event => {
if(event.getCommandName() equals "connect") { if(event.getCommandName() equals "connect") {
val asd = Mono.just(new ConnectCommand().`def`(new Command2DCSender(event), event.getOption("name").get.getValue.get.asString)) val asd = Mono.just(new ConnectCommand().`def`(new Command2DCSender(event), event.getOption("name").get.getValue.get.asString))
asd asd
} else } else
Mono.empty() Mono.empty()
})).subscribe() }).subscribe()
} }
var debug = false var debug = false

View file

@ -11,7 +11,7 @@ import discord4j.common.util.Snowflake
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scala.publisher.javaOptional2ScalaOption import scala.jdk.OptionConverters._
class MCListener extends Listener { class MCListener extends Listener {
@EventHandler def onPlayerJoin(e: PlayerJoinEvent): Unit = @EventHandler def onPlayerJoin(e: PlayerJoinEvent): Unit =
@ -25,18 +25,18 @@ class MCListener extends Listener {
@EventHandler def onGetInfo(e: TBMCPlayerGetInfoEvent): Unit = { @EventHandler def onGetInfo(e: TBMCPlayerGetInfoEvent): Unit = {
Option(DiscordPlugin.SafeMode).filterNot(identity).flatMap(_ => Option(e.getPlayer.getAs(classOf[DiscordPlayer]))) Option(DiscordPlugin.SafeMode).filterNot(identity).flatMap(_ => Option(e.getPlayer.getAs(classOf[DiscordPlayer])))
.flatMap(dp => Option(dp.getDiscordID)).filter(_.nonEmpty) .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 => { .map(user => {
e.addInfo("Discord tag: " + user.getUsername + "#" + user.getDiscriminator) e.addInfo("Discord tag: " + user.getUsername + "#" + user.getDiscriminator)
user user
}) })
.flatMap(user => user.asMember(DiscordPlugin.mainServer.getId).onErrorResume(t => Mono.empty).blockOptional()) .flatMap(user => user.asMember(DiscordPlugin.mainServer.getId).onErrorResume(t => Mono.empty).blockOptional().toScala)
.flatMap(member => member.getPresence.blockOptional()) .flatMap(member => member.getPresence.blockOptional().toScala)
.map(pr => { .map(pr => {
e.addInfo(pr.getStatus.toString) e.addInfo(pr.getStatus.toString)
pr 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 /*@EventHandler

View file

@ -6,13 +6,14 @@ import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC} import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC}
import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayer} import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayer}
import buttondevteam.lib.TBMCSystemChatEvent import buttondevteam.lib.TBMCSystemChatEvent
import buttondevteam.lib.architecture.config.IConfigData
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer} import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer}
import discord4j.core.`object`.entity.Message import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel}
import discord4j.rest.util.{Permission, PermissionSet} import discord4j.rest.util.{Permission, PermissionSet}
import org.bukkit.Bukkit import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import java.lang.reflect.Method import java.lang.reflect.Method
import java.util import java.util
@ -37,9 +38,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
val message: Message = null // TODO val message: Message = null // TODO
if (checkPerms(message, null)) return true if (checkPerms(message, null)) return true
else if (MCChatCustom.removeCustomChat(message.getChannelId)) 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 else
DPUtils.reply(message, SMono.empty, "this channel isn't connected.").subscribe() DPUtils.reply(message, Mono.empty, "this channel isn't connected.").subscribe()
true true
} }
@ -59,7 +60,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled")) target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled"))
.collect(Collectors.joining("\n")) .collect(Collectors.joining("\n"))
if (toggle == null) { 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 return true
} }
val arg: String = toggle.toUpperCase val arg: String = toggle.toUpperCase
@ -67,7 +68,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
if (b.isEmpty) { if (b.isEmpty) {
val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg) val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg)
if (bt == null) { 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 return true
} }
val add: Boolean = !(cc.brtoggles.contains(bt)) val add: Boolean = !(cc.brtoggles.contains(bt))
@ -87,7 +88,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
//1 1 | 0 //1 1 | 0
// XOR // XOR
cc.toggles ^= (1 << b.get.id) 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() + (if ((cc.toggles & (1 << b.get.id)) == 0) "disabled" else "enabled")).subscribe()
true true
} }
@ -105,8 +106,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
if (MCChatCustom.hasCustomChat(message.getChannelId)) { if (MCChatCustom.hasCustomChat(message.getChannelId)) {
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.") 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 val chan: Optional[Channel] = Channel.getChannels.filter(ch => ch.getIdentifier.equalsIgnoreCase(channelID)
if (!(chan.isPresent)) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW) || 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() DPUtils.reply(message, channel, "MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.").subscribe()
return true 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 <MCname>").subscribe() DPUtils.reply(message, channel, "you need to connect your Minecraft account. On the main server in " + DPUtils.botmention + " do " + DiscordPlugin.getPrefix + "connect <MCname>").subscribe()
return true 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 //Using a fake player with no login/logout, should be fine for this event
val groupid: String = chan.get.getGroupID(dcp) 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 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.", "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 + ".", "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix + " prefix only works in " + DPUtils.botmention + ".",
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=" "Invite link: <https://discordapp.com/oauth2/authorize?client_id="
+ SMono(DiscordPlugin.dc.getApplicationInfo).map(info => info.getId.asString).blockOption().getOrElse("Unknown") + DiscordPlugin.dc.getApplicationInfo.map(info => info.getId.asString).blockOptional().orElse("Unknown")
+ "&scope=bot&permissions=268509264>") + "&scope=bot&permissions=268509264>")
} }

View file

@ -3,7 +3,6 @@ package buttondevteam.discordplugin.mcchat
import buttondevteam.core.ComponentManager import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.* import buttondevteam.discordplugin.*
import buttondevteam.discordplugin.DPUtils.SpecExtensions import buttondevteam.discordplugin.DPUtils.SpecExtensions
import buttondevteam.discordplugin.mcchat.playerfaker.{VanillaCommandListener, VanillaCommandListener14, VanillaCommandListener15}
import buttondevteam.discordplugin.mcchat.sender.{DiscordPlayer, DiscordSender, DiscordSenderBase} import buttondevteam.discordplugin.mcchat.sender.{DiscordPlayer, DiscordSender, DiscordSenderBase}
import buttondevteam.lib.* import buttondevteam.lib.*
import buttondevteam.lib.chat.{ChatMessage, TBMCChatAPI} 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.channel.{MessageChannel, PrivateChannel}
import discord4j.core.`object`.entity.{Member, Message, User} import discord4j.core.`object`.entity.{Member, Message, User}
import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.event.domain.message.MessageCreateEvent
import discord4j.core.spec.EmbedCreateSpec
import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageEditSpec} import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageEditSpec}
import discord4j.rest.util.Color import discord4j.rest.util.Color
import org.bukkit.Bukkit import org.bukkit.Bukkit
@ -20,14 +20,13 @@ import org.bukkit.entity.Player
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import org.bukkit.scheduler.BukkitTask import org.bukkit.scheduler.BukkitTask
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scala.publisher.{SFlux, SMono}
import java.time.Instant import java.time.Instant
import java.util import java.util
import java.util.concurrent.{LinkedBlockingQueue, TimeoutException} 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 java.util.stream.Collectors
import scala.jdk.CollectionConverters.{ListHasAsScala, SetHasAsScala} import scala.jdk.CollectionConverters.{IterableHasAsScala, ListHasAsScala, SetHasAsScala}
import scala.jdk.OptionConverters.RichOptional import scala.jdk.OptionConverters.RichOptional
object MCChatListener { 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 val se: util.AbstractMap.SimpleEntry[TBMCChatEvent, Instant] = sendevents.take // Wait until an element is available
e = se.getKey e = se.getKey
time = se.getValue 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) + "]") + (if ("Minecraft" == e.getOrigin) "" else "[" + e.getOrigin.charAt(0) + "]") +
DPUtils.sanitizeStringNoEscape(ChromaUtils.getDisplayName(e.getSender)) DPUtils.sanitizeStringNoEscape(ChromaUtils.getDisplayName(e.getSender))
val color: chat.Color = e.getChannel.Color.get val color: chat.Color = e.getChannel.color.get
val embed: Consumer[LegacyEmbedCreateSpec] = (ecs: LegacyEmbedCreateSpec) => { val embed = () => {
def foo(ecs: LegacyEmbedCreateSpec) = { val ecs = EmbedCreateSpec.builder()
ecs.setDescription(e.getMessage).setColor(Color.of(color.getRed, color.getGreen, color.getBlue)) ecs.description(e.getMessage).color(Color.of(color.getRed, color.getGreen, color.getBlue))
val url: String = module.profileURL.get val url: String = module.profileURL.get
e.getSender match { e.getSender match {
case player: Player => case player: Player =>
DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName,
if (url.nonEmpty) url + "?type=minecraft&id=" + player.getUniqueId else null) if (url.nonEmpty) url + "?type=minecraft&id=" + player.getUniqueId else null)
case dsender: DiscordSenderBase => case dsender: DiscordSenderBase =>
ecs.setAuthor(authorPlayer, ecs.author(authorPlayer,
if (url.nonEmpty) url + "?type=discord&id=" + dsender.getUser.getId.asString else null, if (url.nonEmpty) url + "?type=discord&id=" + dsender.getUser.getId.asString else null,
dsender.getUser.getAvatarUrl) dsender.getUser.getAvatarUrl)
case _ => case _ =>
DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, null) DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, null)
}
ecs.setTimestamp(time)
} }
ecs.timestamp(time)
foo(ecs)
} }
val nanoTime: Long = System.nanoTime val nanoTime: Long = System.nanoTime
val doit = (lastmsgdata: MCChatUtils.LastMsgData) => { val doit = (lastmsgdata: MCChatUtils.LastMsgData) => {
if (lastmsgdata.message == null if (lastmsgdata.message == null
|| authorPlayer != lastmsgdata.message.getEmbeds.get(0).getAuthor.toScala.flatMap(_.getName.toScala).orNull || authorPlayer != lastmsgdata.message.getEmbeds.get(0).getAuthor.toScala.flatMap(_.getName.toScala).orNull
|| lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 || 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.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.time = nanoTime
lastmsgdata.mcchannel = e.getChannel lastmsgdata.mcchannel = e.getChannel
lastmsgdata.content = e.getMessage lastmsgdata.content = e.getMessage
} }
else { else {
lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated 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 // 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 synchronized {
MCChatCustom.lastmsgCustom.filterInPlace(lmd => { MCChatCustom.lastmsgCustom.filterInPlace(lmd => {
if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord 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 && 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 if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions
doit(lmd) doit(lmd)
@ -244,11 +240,11 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
private var recthread: Thread = null private var recthread: Thread = null
// Discord // Discord
def handleDiscord(ev: MessageCreateEvent): SMono[Boolean] = { def handleDiscord(ev: MessageCreateEvent): Mono[Boolean] = {
val author = Option(ev.getMessage.getAuthor.orElse(null)) val author = Option(ev.getMessage.getAuthor.orElse(null))
val hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId) val hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId)
val prefix = DiscordPlugin.getPrefix val prefix = DiscordPlugin.getPrefix
SMono(ev.getMessage.getChannel) ev.getMessage.getChannel
.filter(channel => isChatEnabled(channel, author, hasCustomChat)) .filter(channel => isChatEnabled(channel, author, hasCustomChat))
.filter(channel => !isRunningMCChatCommand(channel, ev.getMessage.getContent, prefix)) .filter(channel => !isRunningMCChatCommand(channel, ev.getMessage.getContent, prefix))
.filter(channel => { .filter(channel => {
@ -313,7 +309,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
replaceUserMentions() replaceUserMentions()
def replaceChannelMentions(): Unit = { 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) dmessage = dmessage.replace(ch.getMention, "#" + ch.getName)
} }
} }
@ -385,9 +381,9 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
else { else {
val cmb = ChatMessage.builder(dsender, user, dmessage + getAttachmentText).fromCommand(false) val cmb = ChatMessage.builder(dsender, user, dmessage + getAttachmentText).fromCommand(false)
if (clmd != null) if (clmd != null)
TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build, clmd.mcchannel) TBMCChatAPI.sendChatMessage(cmb.permCheck(clmd.dcp).build, clmd.mcchannel)
else else
TBMCChatAPI.SendChatMessage(cmb.build) TBMCChatAPI.sendChatMessage(cmb.build)
true true
} }
} }
@ -440,13 +436,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
val mcpackage = Bukkit.getServer.getClass.getPackage.getName val mcpackage = Bukkit.getServer.getClass.getPackage.getName
if (!module.enableVanillaCommands.get) if (!module.enableVanillaCommands.get)
Bukkit.dispatchCommand(dsender, cmd) Bukkit.dispatchCommand(dsender, cmd)
else if (mcpackage.contains("1_12")) else // TODO: Vanilla command handling
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
Bukkit.dispatchCommand(dsender, cmd) Bukkit.dispatchCommand(dsender, cmd)
} catch { } catch {
case e: NoClassDefFoundError => case e: NoClassDefFoundError =>

View file

@ -22,13 +22,13 @@ object MCChatPrivate {
MCChatUtils.ConnectedSenders synchronized { MCChatUtils.ConnectedSenders synchronized {
val mcp = dp.getAs(classOf[TBMCPlayer]) val mcp = dp.getAs(classOf[TBMCPlayer])
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID) val p = Bukkit.getPlayer(mcp.getUniqueId)
val op = Bukkit.getOfflinePlayer(mcp.getUUID) val op = Bukkit.getOfflinePlayer(mcp.getUniqueId)
val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule]) val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule])
if (start) { 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.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 if (p == null) { // Player is offline - If the player is online, that takes precedence
MCChatUtils.callLoginEvents(sender) MCChatUtils.callLoginEvents(sender)
} }

View file

@ -20,8 +20,7 @@ import org.bukkit.entity.Player
import org.bukkit.event.Event import org.bukkit.event.Event
import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent} import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent}
import org.bukkit.plugin.AuthorNagException import org.bukkit.plugin.AuthorNagException
import reactor.core.publisher.{Flux as JFlux, Mono as JMono} import reactor.core.publisher.{Flux, Mono}
import reactor.core.scala.publisher.SMono
import java.net.InetAddress import java.net.InetAddress
import java.util import java.util
@ -36,6 +35,7 @@ import scala.collection.convert.ImplicitConversions.`map AsJavaMap`
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava} import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava}
import scala.jdk.javaapi.CollectionConverters.asScala import scala.jdk.javaapi.CollectionConverters.asScala
import scala.jdk.javaapi.OptionConverters._
object MCChatUtils { object MCChatUtils {
/** /**
@ -93,8 +93,7 @@ object MCChatUtils {
.filter(_ => C.incrementAndGet > 0) //Always true .filter(_ => C.incrementAndGet > 0) //Always true
.map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", ")) .map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", "))
s(0) = s"$C player${if (C.get != 1) "s" else ""} online" s(0) = s"$C player${if (C.get != 1) "s" else ""} online"
lmd.channel.asInstanceOf[TextChannel].edit((tce: LegacyTextChannelEditSpec) => lmd.channel.asInstanceOf[TextChannel].edit().withTopic(String.join("\n----\n", s: _*)).withReason("Player list update").subscribe() //Don't wait
tce.setTopic(String.join("\n----\n", s: _*)).setReason("Player list update").^^()).subscribe //Don't wait
} }
private[mcchat] def checkEssentials(p: Player): Boolean = { private[mcchat] def checkEssentials(p: Player): Boolean = {
@ -126,11 +125,11 @@ object MCChatUtils {
else null.asInstanceOf else null.asInstanceOf
} }
def forPublicPrivateChat(action: SMono[MessageChannel] => SMono[_]): SMono[_] = { def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = {
if (notEnabled) return SMono.empty if (notEnabled) return Mono.empty
val list = MCChatPrivate.lastmsgPerUser.map(data => action(SMono.just(data.channel))) val list = MCChatPrivate.lastmsgPerUser.map(data => action(Mono.just(data.channel)))
.prepend(action(module.chatChannelMono)) .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 toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook * @param hookmsg Whether the message is also sent from the hook
*/ */
def forCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = { def forCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = {
if (notEnabled) return SMono.empty if (notEnabled) return Mono.empty
val list = List(if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) val list = List(if (!GeneralEventBroadcasterModule.isHooked || !hookmsg)
forPublicPrivateChat(action) else SMono.empty) ++ forPublicPrivateChat(action) else Mono.empty) ++
(if (toggle == null) MCChatCustom.lastmsgCustom (if (toggle == null) MCChatCustom.lastmsgCustom
else MCChatCustom.lastmsgCustom.filter(cc => (cc.toggles & (1 << toggle.id)) != 0)) else MCChatCustom.lastmsgCustom.filter(cc => (cc.toggles & (1 << toggle.id)) != 0))
.map(_.channel).map(SMono.just).map(action) .map(_.channel).map(Mono.just).map(action)
SMono(JMono.whenDelayError(list.asJava)) 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 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 * @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[_] = { def forAllowedCustomMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): Mono[_] = {
if (notEnabled) return SMono.empty 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 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 if (toggle != null && ((clmd.toggles & (1 << toggle.id)) == 0)) false //If null then allow
else if (sender == null) true else if (sender == null) true
else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)) 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 //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 toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook * @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[_] = { def forAllowedCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = {
if (notEnabled) return SMono.empty if (notEnabled) return Mono.empty
val cc = forAllowedCustomMCChat(action, sender, toggle) val cc = forAllowedCustomMCChat(action, sender, toggle)
if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return SMono.whenDelayError(Array(forPublicPrivateChat(action), cc)) if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc)
SMono.whenDelayError(Array(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) resetLastMessage(mc)
SMono(mc.createMessage(DPUtils.sanitizeString(message))) mc.createMessage(DPUtils.sanitizeString(message))
}) })
def forAllowedMCChat(action: SMono[MessageChannel] => SMono[_], event: TBMCSystemChatEvent): SMono[_] = { def forAllowedMCChat(action: Mono[MessageChannel] => Mono[_], event: TBMCSystemChatEvent): Mono[_] = {
if (notEnabled) return SMono.empty if (notEnabled) return Mono.empty
val list = new ListBuffer[SMono[_]] val list = new ListBuffer[Mono[_]]
if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono)) if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono))
for (data <- MCChatPrivate.lastmsgPerUser) for (data <- MCChatPrivate.lastmsgPerUser)
if (event.shouldSendTo(getSender(data.channel.getId, data.user))) 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 => MCChatCustom.lastmsgCustom.filter(clmd =>
clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp)) 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) 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(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(getSender(UnconnectedSenders, channel, author))) //
.orElse(Option(addSender(UnconnectedSenders, 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 .get
} }
@ -322,7 +321,7 @@ object MCChatUtils {
callEventExcludingSome(new PlayerJoinEvent(dcp, "")) callEventExcludingSome(new PlayerJoinEvent(dcp, ""))
dcp.setLoggedIn(true) dcp.setLoggedIn(true)
if (module != null) { if (module != null) {
if (module.serverWatcher != null) module.serverWatcher.fakePlayers.add(dcp) // TODO: ServerWatcher
module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged in from Discord") module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged in from Discord")
} }
} }
@ -345,7 +344,7 @@ object MCChatUtils {
dcp.setLoggedIn(false) dcp.setLoggedIn(false)
if (module != null) { if (module != null) {
module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged out from Discord") module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged out from Discord")
if (module.serverWatcher != null) module.serverWatcher.fakePlayers.remove(dcp) // TODO: ServerWatcher
} }
} }

View file

@ -16,7 +16,7 @@ import org.bukkit.event.player.*
import org.bukkit.event.player.PlayerLoginEvent.Result import org.bukkit.event.player.PlayerLoginEvent.Result
import org.bukkit.event.server.{BroadcastMessageEvent, TabCompleteEvent} import org.bukkit.event.server.{BroadcastMessageEvent, TabCompleteEvent}
import org.bukkit.event.{EventHandler, EventPriority, Listener} 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 { class MCListener(val module: MinecraftChatModule) extends Listener {
final private val muteRole = DPUtils.roleData(module.getConfig, "muteRole", "Muted") 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 p = e.getPlayer
val dp = TBMCPlayerBase.getPlayer(p.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer]) val dp = TBMCPlayerBase.getPlayer(p.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer])
if (dp != null) if (dp != null)
DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).^^().flatMap(user => DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).flatMap(user =>
user.getPrivateChannel.^^().flatMap(chan => module.chatChannelMono.flatMap(cc => { 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, chan, p, module))
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, cc, p, module)) //Stored per-channel MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, cc, p, module)) //Stored per-channel
SMono.empty Mono.empty
}))).subscribe() }))).subscribe()
val message = e.getJoinMessage val message = e.getJoinMessage
sendJoinLeaveMessage(message, e.getPlayer) sendJoinLeaveMessage(message, e.getPlayer)
@ -87,18 +87,17 @@ class MCListener(val module: MinecraftChatModule) extends Listener {
if (!source.isPlayer) return () if (!source.isPlayer) return ()
val p = TBMCPlayerBase.getPlayer(source.getPlayer.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer]) val p = TBMCPlayerBase.getPlayer(source.getPlayer.getUniqueId, classOf[TBMCPlayer]).getAs(classOf[DiscordPlayer])
if (p == null) return () if (p == null) return ()
DPUtils.ignoreError(SMono(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID))) DPUtils.ignoreError(DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID))
.flatMap(user => SMono(user.asMember(DiscordPlugin.mainServer.getId))) .flatMap(user => user.asMember(DiscordPlugin.mainServer.getId))
.flatMap(user => role.flatMap((r: Role) => { .flatMap(user => role.flatMap((r: Role) => {
def foo(r: Role): SMono[_] = { def foo(r: Role): Mono[_] = {
if (e.getValue) user.addRole(r.getId) if (e.getValue) user.addRole(r.getId)
else user.removeRole(r.getId) else user.removeRole(r.getId)
val modlog = module.modlogChannel.get val modlog = module.modlogChannel.get
val msg = (if (e.getValue) "M" val msg = s"${(if (e.getValue) "M" else "Unm")}uted user: ${user.getUsername}#${user.getDiscriminator}"
else "Unm") + "uted user: " + user.getUsername + "#" + user.getDiscriminator
module.log(msg) module.log(msg)
if (modlog != null) return modlog.flatMap((ch: MessageChannel) => SMono(ch.createMessage(msg))) if (modlog != null) return modlog.flatMap((ch: MessageChannel) => ch.createMessage(msg))
SMono.empty Mono.empty
} }
foo(r) foo(r)
@ -119,7 +118,7 @@ class MCListener(val module: MinecraftChatModule) extends Listener {
case _ => event.getSender.getName case _ => event.getSender.getName
} }
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO //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) .map(Option.apply).defaultIfEmpty(Option.empty)
.flatMap(yeehaw => MCChatUtils.forPublicPrivateChat(MCChatUtils.send(name + .flatMap(yeehaw => MCChatUtils.forPublicPrivateChat(MCChatUtils.send(name +
yeehaw.map(guildEmoji => " <:YEEHAW:" + guildEmoji.getId.asString + ">s").getOrElse(" YEEHAWs")))).subscribe() 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 val t = event.getBuffer.substring(i + 1) //0 if not found
if (!t.startsWith("@")) return () if (!t.startsWith("@")) return ()
val token = t.substring(1) 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() .filter(_.startsWith(token)).map("@" + _).doOnNext(event.getCompletions.add(_)).blockLast()
} }

View file

@ -2,8 +2,6 @@ package buttondevteam.discordplugin.mcchat
import buttondevteam.core.component.channel.Channel import buttondevteam.core.component.channel.Channel
import buttondevteam.discordplugin.DPUtils.{MonoExtensions, SpecExtensions} 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.mcchat.sender.DiscordConnectedPlayer
import buttondevteam.discordplugin.util.DPState import buttondevteam.discordplugin.util.DPState
import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordPlugin} import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordPlugin}
@ -14,7 +12,7 @@ import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.rest.util.Color import discord4j.rest.util.Color
import org.bukkit.Bukkit import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import java.util import java.util
import java.util.stream.Collectors import java.util.stream.Collectors
@ -33,27 +31,27 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
def getListener: MCChatListener = this.listener def getListener: MCChatListener = this.listener
private var listener: MCChatListener = null private var listener: MCChatListener = null
private[mcchat] var serverWatcher: ServerWatcher = null private[mcchat] var serverWatcher = null
private var lpInjector: LPInjector = null private var lpInjector = null
private[mcchat] var disabling = false 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! * 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", 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 * 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 * 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 * 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) val chconkeys = chcons.getKeys(false)
for (chconkey <- chconkeys.asScala) { for (chconkey <- chconkeys.asScala) {
val chcon = chcons.getConfigurationSection(chconkey) 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 ch = DiscordPlugin.dc.getChannelById(Snowflake.of(chcon.getLong("chid"))).block
val did = chcon.getLong("did") val did = chcon.getLong("did")
val user = DiscordPlugin.dc.getUserById(Snowflake.of(did)).block 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) // TODO: LPInjector
catch {
case e: Exception =>
TBMCCoreAPI.SendException("Failed to init LuckPerms injector", e, this)
case e: NoClassDefFoundError =>
log("No LuckPerms, not injecting")
//e.printStackTrace();
}
if (addFakePlayersToBukkit.get) try { if (addFakePlayersToBukkit.get) try {
serverWatcher = new ServerWatcher // TODO: Fake players
serverWatcher.enableDisable(true)
log("Finished hooking into the server")
} catch { } catch {
case e: Exception => case e: Exception =>
TBMCCoreAPI.SendException("Failed to hack the server (object)! Disable addFakePlayersToBukkit in the config.", e, this) 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 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 try //If it's not enabled it won't do anything
if (serverWatcher != null) { if (serverWatcher != null) {
serverWatcher.enableDisable(false) // TODO: ServerWatcher
log("Finished unhooking the server")
} }
catch { catch {
case e: Exception => case e: Exception =>
@ -200,7 +188,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
val chconsc = getConfig.getConfig.createSection("chcons") val chconsc = getConfig.getConfig.createSection("chcons")
for (chcon <- chcons) { for (chcon <- chcons) {
val chconc = chconsc.createSection(chcon.channel.getId.asString) 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("chid", chcon.channel.getId.asLong)
chconc.set("did", chcon.user.getId.asLong) chconc.set("did", chcon.user.getId.asLong)
chconc.set("mcuid", chcon.dcp.getUniqueId.toString) chconc.set("mcuid", chcon.dcp.getUniqueId.toString)
@ -222,13 +210,13 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
*/ */
private def sendStateMessage(color: Color, message: String) = private def sendStateMessage(color: Color, message: String) =
MCChatUtils.forCustomAndAllMCChat(_.flatMap( MCChatUtils.forCustomAndAllMCChat(_.flatMap(
_.createEmbed(_.setColor(color).setTitle(message).^^()).^^() _.createEmbed(_.setColor(color).setTitle(message))
.onErrorResume(_ => SMono.empty) .onErrorResume(_ => Mono.empty)
), ChannelconBroadcast.RESTART, hookmsg = false).block() ), ChannelconBroadcast.RESTART, hookmsg = false).block()
private def sendStateMessage(color: Color, message: String, extra: String) = private def sendStateMessage(color: Color, message: String, extra: String) =
MCChatUtils.forCustomAndAllMCChat(_.flatMap( MCChatUtils.forCustomAndAllMCChat(_.flatMap(
_.createEmbed(_.setColor(color).setTitle(message).setDescription(extra).^^()).^^() _.createEmbed(_.setColor(color).setTitle(message).setDescription(extra))
.onErrorResume(_ => SMono.empty) .onErrorResume(_ => Mono.empty)
), ChannelconBroadcast.RESTART, hookmsg = false).block() ), ChannelconBroadcast.RESTART, hookmsg = false).block()
} }

View file

@ -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
}

View file

@ -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<ItemStack> 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<Integer, ItemStack> 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<Integer, ItemStack> 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<Integer, ? extends ItemStack> all(int materialId) {
return new HashMap<>();
}
@Override
public HashMap<Integer, ? extends ItemStack> all(Material material) throws IllegalArgumentException {
return new HashMap<>();
}
@Override
public HashMap<Integer, ? extends ItemStack> 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<HumanEntity> 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<ItemStack> iterator() {
return itemStacks.listIterator();
}
@Override
public ListIterator<ItemStack> iterator(int index) {
return itemStacks.listIterator(index);
}
@Override
public Location getLocation() {
return null;
}
}

View file

@ -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<Player>) method.invoke(origServer, invocation.getArguments());
playerList = new AppendListView<>(list, fakePlayers);
} - Your scientists were so preoccupied with whether or not they could, they didnt 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)
}
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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<UUID> 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<UUID>) 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<PermissionAttachment> attachments = (List<PermissionAttachment>) 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);
}
}*/
}

View file

@ -1,12 +1,12 @@
package buttondevteam.discordplugin.mcchat.sender package buttondevteam.discordplugin.mcchat.sender
import buttondevteam.discordplugin.mcchat.MinecraftChatModule import buttondevteam.discordplugin.mcchat.MinecraftChatModule
import buttondevteam.discordplugin.mcchat.playerfaker.{DiscordInventory, VCMDWrapper}
import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.* import org.bukkit.*
import org.bukkit.attribute.{Attribute, AttributeInstance, AttributeModifier} import org.bukkit.attribute.{Attribute, AttributeInstance, AttributeModifier}
import org.bukkit.entity.{Entity, Player} import org.bukkit.entity.{Entity, Player}
import org.bukkit.event.inventory.InventoryType
import org.bukkit.event.player.{AsyncPlayerChatEvent, PlayerTeleportEvent} import org.bukkit.event.player.{AsyncPlayerChatEvent, PlayerTeleportEvent}
import org.bukkit.inventory.{Inventory, PlayerInventory} import org.bukkit.inventory.{Inventory, PlayerInventory}
import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo} import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
@ -31,10 +31,6 @@ object DiscordConnectedPlayer {
try { try {
if (!Modifier.isAbstract(invocation.getMethod.getModifiers)) if (!Modifier.isAbstract(invocation.getMethod.getModifiers))
invocation.callRealMethod 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 else
RETURNS_DEFAULTS.answer(invocation) RETURNS_DEFAULTS.answer(invocation)
} catch { } 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 val basePlayer: OfflinePlayer = if (module == null) null else Bukkit.getOfflinePlayer(uniqueId)
private var perm: PermissibleBase = if (module == null) null else new PermissibleBase(basePlayer) private var perm: PermissibleBase = if (module == null) null else new PermissibleBase(basePlayer)
private val origPerm: PermissibleBase = perm 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) 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 setDisplayName(displayName: String): Unit = this.displayName = displayName
override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener override def getVanillaCmdListener = this.vanillaCmdListener
def isLoggedIn: Boolean = this.loggedIn 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 getGameMode = GameMode.SPECTATOR
override def getInventory: PlayerInventory = inventory
//noinspection ScalaDeprecation //noinspection ScalaDeprecation
/*@SuppressWarnings(Array("deprecation")) override def spigot: super.Spigot = new super.Spigot() { /*@SuppressWarnings(Array("deprecation")) override def spigot: super.Spigot = new super.Spigot() {
override def getRawAddress: InetSocketAddress = null override def getRawAddress: InetSocketAddress = null

View file

@ -1,7 +1,6 @@
package buttondevteam.discordplugin.mcchat.sender package buttondevteam.discordplugin.mcchat.sender
import buttondevteam.discordplugin.mcchat.MinecraftChatModule import buttondevteam.discordplugin.mcchat.MinecraftChatModule
import buttondevteam.discordplugin.mcchat.playerfaker.VCMDWrapper
import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.entity.Player 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] { 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 = { override def sendMessage(message: String): Unit = {
player.sendMessage(message) player.sendMessage(message)
@ -35,7 +33,7 @@ abstract class DiscordPlayerSender(user: User, channel: MessageChannel, var play
} }
override def sendMessage(messages: Array[String]): Unit = { override def sendMessage(messages: Array[String]): Unit = {
player.sendMessage(messages) player.sendMessage(messages*)
super.sendMessage(messages) super.sendMessage(messages)
} }
} }

View file

@ -7,15 +7,16 @@ import org.bukkit.command.CommandSender
import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo} import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
import org.bukkit.plugin.Plugin import org.bukkit.plugin.Plugin
import org.bukkit.{Bukkit, Server} import org.bukkit.{Bukkit, Server}
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import scala.jdk.OptionConverters._
import java.util import java.util
class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender { class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender {
private val perm = new PermissibleBase(this) private val perm = new PermissibleBase(this)
private val name: String = Option(pname) private val name: String = Option(pname)
.orElse(Option(user).flatMap(u => SMono(u.asMember(DiscordPlugin.mainServer.getId)) .orElse(Option(user).flatMap(u => u.asMember(DiscordPlugin.mainServer.getId)
.onErrorResume(_ => SMono.empty).blockOption() .onErrorResume(_ => Mono.empty).blockOptional().toScala
.map(u => u.getDisplayName))) .map(u => u.getDisplayName)))
.getOrElse("Discord user") .getOrElse("Discord user")

View file

@ -1,8 +1,7 @@
package buttondevteam.discordplugin.mcchat.sender package buttondevteam.discordplugin.mcchat.sender
import buttondevteam.discordplugin.mcchat.playerfaker.VCMDWrapper
import org.bukkit.entity.Player import org.bukkit.entity.Player
trait IMCPlayer[T] extends Player { trait IMCPlayer[T] extends Player {
def getVanillaCmdListener: VCMDWrapper def getVanillaCmdListener: Null // TODO
} }

View file

@ -5,6 +5,7 @@ import buttondevteam.discordplugin.mcchat.sender.{DiscordPlayer, DiscordSenderBa
import buttondevteam.discordplugin.mcchat.{MCChatUtils, MinecraftChatModule} import buttondevteam.discordplugin.mcchat.{MCChatUtils, MinecraftChatModule}
import buttondevteam.discordplugin.util.DPState import buttondevteam.discordplugin.util.DPState
import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.chat.commands.MCCommandSettings
import buttondevteam.lib.chat.{Command2, CommandClass, ICommand2MC} import buttondevteam.lib.chat.{Command2, CommandClass, ICommand2MC}
import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer, TBMCPlayerBase} import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer, TBMCPlayerBase}
import discord4j.core.`object`.ExtendedInvite import discord4j.core.`object`.ExtendedInvite
@ -19,7 +20,8 @@ import java.lang.reflect.Method
"Discord", "Discord",
"This command allows performing Discord-related actions." "This command allows performing Discord-related actions."
)) class DiscordMCCommand extends ICommand2MC { )) class DiscordMCCommand extends ICommand2MC {
@Command2.Subcommand def accept(player: Player): Boolean = { @Command2.Subcommand
def accept(player: Player): Boolean = {
if (checkSafeMode(player)) return true if (checkSafeMode(player)) return true
val did = ConnectCommand.WaitingToConnect.get(player.getName) val did = ConnectCommand.WaitingToConnect.get(player.getName)
if (did == null) { if (did == null) {
@ -35,7 +37,8 @@ import java.lang.reflect.Method
true true
} }
@Command2.Subcommand def decline(player: Player): Boolean = { @Command2.Subcommand
def decline(player: Player): Boolean = {
if (checkSafeMode(player)) return true if (checkSafeMode(player)) return true
val did = ConnectCommand.WaitingToConnect.remove(player.getName) val did = ConnectCommand.WaitingToConnect.remove(player.getName)
if (did == null) { if (did == null) {
@ -46,17 +49,21 @@ import java.lang.reflect.Method
true true
} }
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = Array( @Command2.Subcommand(helpText = Array(
"Reload Discord plugin", "Reload Discord plugin",
"Reloads the config. To apply some changes, you may need to also run /discord restart." "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.") if (DiscordPlugin.plugin.tryReloadConfig) sender.sendMessage("§bConfig reloaded.")
else sender.sendMessage("§cFailed to reload config.") else sender.sendMessage("§cFailed to reload config.")
@Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = Array( @Command2.Subcommand(helpText = Array(
"Restart the plugin", // "Restart the plugin", //
"This command disables and then enables 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 = () => { val task: Runnable = () => {
def foo(): Unit = { def foo(): Unit = {
if (!DiscordPlugin.plugin.tryReloadConfig) { if (!DiscordPlugin.plugin.tryReloadConfig) {
@ -87,14 +94,16 @@ import java.lang.reflect.Method
@Command2.Subcommand(helpText = Array( @Command2.Subcommand(helpText = Array(
"Version command", "Version command",
"Prints the plugin version")) def version(sender: CommandSender): Unit = { "Prints the plugin version"))
sender.sendMessage(VersionCommand.getVersion) def version(sender: CommandSender): Unit = {
sender.sendMessage(VersionCommand.getVersion*)
} }
@Command2.Subcommand(helpText = Array( @Command2.Subcommand(helpText = Array(
"Invite", "Invite",
"Shows an invite link to the server" "Shows an invite link to the server"
)) def invite(sender: CommandSender): Unit = { ))
def invite(sender: CommandSender): Unit = {
if (checkSafeMode(sender)) { if (checkSafeMode(sender)) {
return () return ()
} }

View file

@ -1,7 +1,6 @@
package buttondevteam.discordplugin.role package buttondevteam.discordplugin.role
import buttondevteam.core.ComponentManager import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.DPUtils.{FluxExtensions, MonoExtensions}
import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.architecture.{Component, ComponentMetadata} import buttondevteam.lib.architecture.{Component, ComponentMetadata}
import discord4j.core.`object`.entity.Role 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.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleEvent, RoleUpdateEvent}
import discord4j.rest.util.Color import discord4j.rest.util.Color
import org.bukkit.Bukkit import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono import reactor.core.publisher.Mono
import java.util.Collections import java.util.Collections
import scala.jdk.CollectionConverters.SeqHasAsJava import scala.jdk.CollectionConverters.SeqHasAsJava
@ -30,14 +29,14 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
val role = roleCreateEvent.getRole val role = roleCreateEvent.getRole
if (!notMainServer(role)) { if (!notMainServer(role)) {
grm.isGameRole(role).flatMap(b => { 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 { else {
GameRoles.add(role.getName) GameRoles.add(role.getName)
if (logChannel != null) if (logChannel != null)
logChannel.flatMap(_.createMessage("Added " + role.getName + " as game role." + 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 else
SMono.empty Mono.empty
} }
}).subscribe() }).subscribe()
() ()
@ -48,7 +47,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
if (role == null) return () if (role == null) return ()
if (notMainServer(role)) return () if (notMainServer(role)) return ()
if (GameRoles.remove(role.getName) && logChannel != null) 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 => case roleUpdateEvent: RoleUpdateEvent =>
if (!roleUpdateEvent.getOld.isPresent) { if (!roleUpdateEvent.getOld.isPresent) {
grm.logWarn("Old role not stored, cannot update game role!") grm.logWarn("Old role not stored, cannot update game role!")
@ -60,21 +59,21 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
grm.isGameRole(cr).flatMap(isGameRole => { grm.isGameRole(cr).flatMap(isGameRole => {
if (!isGameRole) if (!isGameRole)
if (GameRoles.remove(or.getName) && logChannel != null) 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 else
SMono.empty Mono.empty
else if (GameRoles.contains(or.getName) && or.getName == cr.getName) else if (GameRoles.contains(or.getName) && or.getName == cr.getName)
SMono.empty Mono.empty
else { else {
val removed = GameRoles.remove(or.getName) //Regardless of whether it was a game role 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 GameRoles.add(cr.getName) //Add it because it has no color
if (logChannel != null) if (logChannel != null)
if (removed) 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 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 else
SMono.empty Mono.empty
} }
}).subscribe() }).subscribe()
case _ => case _ =>
@ -88,7 +87,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
override protected def enable(): Unit = { override protected def enable(): Unit = {
getPlugin.manager.registerCommand(command) 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) 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. * The role color that is used by game roles.
* Defaults to the second to last in the upper row - #95a5a6. * 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] = { private def isGameRole(r: Role): Mono[java.lang.Boolean] = {
if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return SMono.just(false) //Only allow on the main server if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return Mono.just(false) //Only allow on the main server
val rc = roleColor.get val rc = roleColor.get
if (r.getColor equals rc) if (r.getColor equals rc)
DiscordPlugin.dc.getSelf.flatMap(u => u.asMember(DiscordPlugin.mainServer.getId)).^^() DiscordPlugin.dc.getSelf.flatMap(u => u.asMember(DiscordPlugin.mainServer.getId))
.flatMap(_.hasHigherRoles(Collections.singleton(r.getId)).^^().asInstanceOf).defaultIfEmpty(false) //Below one of our roles .flatMap(_.hasHigherRoles(Collections.singleton(r.getId))).defaultIfEmpty(false) //Below one of our roles
else SMono.just(false) else Mono.just(false)
} }
} }