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 += "jitpack.io" at "https://jitpack.io"
resolvers += "paper-repo" at "https://papermc.io/repo/repository/maven-public/"
resolvers += Resolver.mavenLocal
// assembly / assemblyOption := (assembly / assemblyOption).value.copy(includeScala = false)
libraryDependencies ++= Seq(
"org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT" % Provided,
"org.spigotmc" % "spigot" % "1.12.2-R0.1-SNAPSHOT" % Provided,
"org.spigotmc." % "spigot" % "1.14.4-R0.1-SNAPSHOT" % Provided,
"com.destroystokyo.paper" % "paper" % "1.16.3-R0.1-SNAPSHOT" % Provided,
"org.spigotmc" % "spigot" % "1.19.4-R0.1-SNAPSHOT" % Provided,
"io.papermc.paper" % "paper-api" % "1.19.4-R0.1-SNAPSHOT" % Provided,
"com.discord4j" % "discord4j-core" % "3.2.2",
"org.slf4j" % "slf4j-jdk14" % "1.7.36",
"com.vdurmont" % "emoji-java" % "5.1.1",
"org.mockito" % "mockito-core" % "4.6.1",
"io.projectreactor" % "reactor-scala-extensions_2.13" % "0.8.0",
// https://mvnrepository.com/artifact/org.immutables/value
"org.immutables" % "value" % "2.9.0" % "provided",
"com.github.TBMCPlugins.ChromaCore" % "Chroma-Core" % "v1.0.0" % Provided,
"com.github.TBMCPlugins.ChromaCore" % "Chroma-Core" % "v2.0.0-SNAPSHOT" % Provided,
"net.ess3" % "EssentialsX" % "2.17.1" % Provided,
"net.luckperms" % "api" % "5.4" % Provided,
)
@ -141,3 +139,5 @@ saveConfigComments := {
}
Compile / resourceGenerators += saveConfigComments
//scalacOptions ++= Seq("-release", "17", "--verbose")
scalacOptions ++= Seq("-release", "17")

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

View file

@ -3,4 +3,4 @@
resolvers += Resolver.mavenLocal
libraryDependencies += "org.spigotmc" % "spigot-api" % "1.12.2-R0.1-SNAPSHOT"
libraryDependencies += "org.spigotmc" % "spigot-api" % "1.19.4-R0.1-SNAPSHOT"

View file

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

View file

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

View file

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

View file

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

View file

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

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
class Command2DC extends Command2[ICommand2DC, Command2DCSender] {
class Command2DC extends Command2[ICommand2DC, Command2DCSender]('/', false) {
override def registerCommand(command: ICommand2DC): Unit = {
registerCommand(command, DiscordPlugin.dc.getApplicationInfo.block().getId.asLong())
}
def registerCommand(command: ICommand2DC, appId: Long, guildId: Option[Long] = None): Unit = {
super.registerCommand(command, DiscordPlugin.getPrefix) //Needs to be configurable for the helps
super.registerCommand(command) //Needs to be configurable for the helps
val greetCmdRequest = ApplicationCommandRequest.builder()
.name(command.getCommandPath) //TODO: Main path
.description("Connect your Minecraft account.") //TODO: Description

View file

@ -7,10 +7,10 @@ import discord4j.core.`object`.entity.{Member, Message, User}
import discord4j.core.event.domain.interaction.ChatInputInteractionEvent
import java.util.Optional
import reactor.core.scala.publisher.javaOptional2ScalaOption;
import scala.jdk.OptionConverters._
class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sender {
val authorAsMember: Option[Member] = event.getInteraction.getMember
val authorAsMember: Option[Member] = event.getInteraction.getMember.toScala
val author: User = event.getInteraction.getUser
override def sendMessage(message: String): Unit = {
if (message.isEmpty) return ()
@ -20,5 +20,5 @@ class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sen
override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message: _*))
override def getName: String = authorAsMember.flatMap(_.getNickname).getOrElse(author.getUsername)
override def getName: String = authorAsMember.flatMap(_.getNickname.toScala).getOrElse(author.getUsername)
}

View file

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

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ import discord4j.core.`object`.entity.{Guild, Role}
import org.apache.commons.lang.exception.ExceptionUtils
import org.bukkit.Bukkit
import org.bukkit.event.{EventHandler, Listener}
import reactor.core.scala.publisher.SMono
import reactor.core.publisher.Mono
import java.util
import java.util.stream.Collectors
@ -22,8 +22,8 @@ object ExceptionListenerModule {
if (instance == null) return ()
try getChannel.flatMap(channel => {
val coderRole = channel match {
case ch: GuildChannel => instance.pingRole(SMono(ch.getGuild)).get
case _ => SMono.empty
case ch: GuildChannel => instance.pingRole(ch.getGuild).get
case _ => Mono.empty
}
coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder
else new StringBuilder(role.getMention).append("\n"))
@ -36,7 +36,7 @@ object ExceptionListenerModule {
if (sb.length + stackTrace.length >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length)
sb.append(stackTrace).append("\n")
sb.append("```")
SMono(channel.createMessage(sb.toString))
channel.createMessage(sb.toString)
})
}).subscribe()
catch {
@ -47,9 +47,9 @@ object ExceptionListenerModule {
private var instance: ExceptionListenerModule = null
def getChannel: SMono[MessageChannel] = {
def getChannel: Mono[MessageChannel] = {
if (instance != null) return instance.channel.get
SMono.empty
Mono.empty
}
}
@ -69,7 +69,7 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener {
if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0)
lastthrown.add(e.getException)
lastsourcemsg.add(e.getSourceMessage)
e.setHandled()
e.setHandled(true)
}
/**
@ -80,7 +80,7 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener {
/**
* The role to ping if an error occurs. Set to empty ('') to disable.
*/
private def pingRole(guild: SMono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild)
private def pingRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild)
override protected def enable(): Unit = {
if (DPUtils.disableIfConfigError(this, channel)) return ()

View file

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

View file

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

View file

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

View file

@ -6,13 +6,14 @@ import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC}
import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayer}
import buttondevteam.lib.TBMCSystemChatEvent
import buttondevteam.lib.architecture.config.IConfigData
import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer}
import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel}
import discord4j.rest.util.{Permission, PermissionSet}
import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono
import reactor.core.publisher.Mono
import java.lang.reflect.Method
import java.util
@ -37,9 +38,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
val message: Message = null // TODO
if (checkPerms(message, null)) return true
else if (MCChatCustom.removeCustomChat(message.getChannelId))
DPUtils.reply(message, SMono.empty, "channel connection removed.").subscribe()
DPUtils.reply(message, Mono.empty, "channel connection removed.").subscribe()
else
DPUtils.reply(message, SMono.empty, "this channel isn't connected.").subscribe()
DPUtils.reply(message, Mono.empty, "this channel isn't connected.").subscribe()
true
}
@ -59,7 +60,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled"))
.collect(Collectors.joining("\n"))
if (toggle == null) {
DPUtils.reply(message, SMono.empty, "toggles:\n" + togglesString.get).subscribe()
DPUtils.reply(message, Mono.empty, "toggles:\n" + togglesString.get).subscribe()
return true
}
val arg: String = toggle.toUpperCase
@ -67,7 +68,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
if (b.isEmpty) {
val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg)
if (bt == null) {
DPUtils.reply(message, SMono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe()
DPUtils.reply(message, Mono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe()
return true
}
val add: Boolean = !(cc.brtoggles.contains(bt))
@ -87,7 +88,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
//1 1 | 0
// XOR
cc.toggles ^= (1 << b.get.id)
DPUtils.reply(message, SMono.empty, "'" + b.get.toString.toLowerCase + "' "
DPUtils.reply(message, Mono.empty, "'" + b.get.toString.toLowerCase + "' "
+ (if ((cc.toggles & (1 << b.get.id)) == 0) "disabled" else "enabled")).subscribe()
true
}
@ -105,8 +106,9 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
if (MCChatCustom.hasCustomChat(message.getChannelId)) {
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.")
}
val chan: Optional[Channel] = Channel.getChannels.filter((ch: Channel) => ch.ID.equalsIgnoreCase(channelID) || (util.Arrays.stream(ch.IDs.get).anyMatch((cid: String) => cid.equalsIgnoreCase(channelID)))).findAny
if (!(chan.isPresent)) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
val chan: Optional[Channel] = Channel.getChannels.filter(ch => ch.getIdentifier.equalsIgnoreCase(channelID)
|| util.Arrays.stream(ch.extraIdentifiers.get).anyMatch(cid => cid.equalsIgnoreCase(channelID))).findAny
if (!chan.isPresent) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
DPUtils.reply(message, channel, "MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.").subscribe()
return true
}
@ -120,7 +122,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
DPUtils.reply(message, channel, "you need to connect your Minecraft account. On the main server in " + DPUtils.botmention + " do " + DiscordPlugin.getPrefix + "connect <MCname>").subscribe()
return true
}
val dcp: DiscordConnectedPlayer = DiscordConnectedPlayer.create(message.getAuthor.get, channel, chp.getUUID, Bukkit.getOfflinePlayer(chp.getUUID).getName, module)
val dcp: DiscordConnectedPlayer = DiscordConnectedPlayer.create(message.getAuthor.get, channel, chp.getUniqueId, Bukkit.getOfflinePlayer(chp.getUniqueId).getName, module)
//Using a fake player with no login/logout, should be fine for this event
val groupid: String = chan.get.getGroupID(dcp)
if (groupid == null && !((chan.get.isInstanceOf[ChatRoom]))) { //ChatRooms don't allow it unless the user joins, which happens later
@ -175,6 +177,6 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
"To remove a connection use @ChromaBot channelcon remove in the channel.",
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix + " prefix only works in " + DPUtils.botmention + ".",
"Invite link: <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>")
}

View file

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

View file

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

View file

@ -20,8 +20,7 @@ import org.bukkit.entity.Player
import org.bukkit.event.Event
import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent}
import org.bukkit.plugin.AuthorNagException
import reactor.core.publisher.{Flux as JFlux, Mono as JMono}
import reactor.core.scala.publisher.SMono
import reactor.core.publisher.{Flux, Mono}
import java.net.InetAddress
import java.util
@ -36,6 +35,7 @@ import scala.collection.convert.ImplicitConversions.`map AsJavaMap`
import scala.collection.mutable.ListBuffer
import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava}
import scala.jdk.javaapi.CollectionConverters.asScala
import scala.jdk.javaapi.OptionConverters._
object MCChatUtils {
/**
@ -93,8 +93,7 @@ object MCChatUtils {
.filter(_ => C.incrementAndGet > 0) //Always true
.map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", "))
s(0) = s"$C player${if (C.get != 1) "s" else ""} online"
lmd.channel.asInstanceOf[TextChannel].edit((tce: LegacyTextChannelEditSpec) =>
tce.setTopic(String.join("\n----\n", s: _*)).setReason("Player list update").^^()).subscribe //Don't wait
lmd.channel.asInstanceOf[TextChannel].edit().withTopic(String.join("\n----\n", s: _*)).withReason("Player list update").subscribe() //Don't wait
}
private[mcchat] def checkEssentials(p: Player): Boolean = {
@ -126,11 +125,11 @@ object MCChatUtils {
else null.asInstanceOf
}
def forPublicPrivateChat(action: SMono[MessageChannel] => SMono[_]): SMono[_] = {
if (notEnabled) return SMono.empty
val list = MCChatPrivate.lastmsgPerUser.map(data => action(SMono.just(data.channel)))
def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = {
if (notEnabled) return Mono.empty
val list = MCChatPrivate.lastmsgPerUser.map(data => action(Mono.just(data.channel)))
.prepend(action(module.chatChannelMono))
SMono(JMono.whenDelayError(list.asJava))
Mono.whenDelayError(list.asJava)
}
/**
@ -140,14 +139,14 @@ object MCChatUtils {
* @param toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook
*/
def forCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = {
if (notEnabled) return SMono.empty
def forCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = {
if (notEnabled) return Mono.empty
val list = List(if (!GeneralEventBroadcasterModule.isHooked || !hookmsg)
forPublicPrivateChat(action) else SMono.empty) ++
forPublicPrivateChat(action) else Mono.empty) ++
(if (toggle == null) MCChatCustom.lastmsgCustom
else MCChatCustom.lastmsgCustom.filter(cc => (cc.toggles & (1 << toggle.id)) != 0))
.map(_.channel).map(SMono.just).map(action)
SMono(JMono.whenDelayError(list.asJava))
.map(_.channel).map(Mono.just).map(action)
Mono.whenDelayError(list.asJava)
}
/**
@ -157,15 +156,15 @@ object MCChatUtils {
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
*/
def forAllowedCustomMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): SMono[_] = {
if (notEnabled) return SMono.empty
def forAllowedCustomMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): Mono[_] = {
if (notEnabled) return Mono.empty
val st = MCChatCustom.lastmsgCustom.filter(clmd => { //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
if (toggle != null && ((clmd.toggles & (1 << toggle.id)) == 0)) false //If null then allow
else if (sender == null) true
else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender))
}).map(cc => action.apply(SMono.just(cc.channel))) //TODO: Send error messages on channel connect
}).map(cc => action.apply(Mono.just(cc.channel))) //TODO: Send error messages on channel connect
//Mono.whenDelayError((() => st.iterator).asInstanceOf[java.lang.Iterable[Mono[_]]]) //Can't convert as an iterator or inside the stream, but I can convert it as a stream
SMono.whenDelayError(st)
Mono.whenDelayError(st.asJava)
}
/**
@ -176,32 +175,32 @@ object MCChatUtils {
* @param toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook
*/
def forAllowedCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = {
if (notEnabled) return SMono.empty
def forAllowedCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = {
if (notEnabled) return Mono.empty
val cc = forAllowedCustomMCChat(action, sender, toggle)
if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return SMono.whenDelayError(Array(forPublicPrivateChat(action), cc))
SMono.whenDelayError(Array(cc))
if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc)
Mono.whenDelayError(cc)
}
def send(message: String): SMono[MessageChannel] => SMono[_] = _.flatMap((mc: MessageChannel) => {
def send(message: String): Mono[MessageChannel] => Mono[_] = _.flatMap((mc: MessageChannel) => {
resetLastMessage(mc)
SMono(mc.createMessage(DPUtils.sanitizeString(message)))
mc.createMessage(DPUtils.sanitizeString(message))
})
def forAllowedMCChat(action: SMono[MessageChannel] => SMono[_], event: TBMCSystemChatEvent): SMono[_] = {
if (notEnabled) return SMono.empty
val list = new ListBuffer[SMono[_]]
def forAllowedMCChat(action: Mono[MessageChannel] => Mono[_], event: TBMCSystemChatEvent): Mono[_] = {
if (notEnabled) return Mono.empty
val list = new ListBuffer[Mono[_]]
if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono))
for (data <- MCChatPrivate.lastmsgPerUser)
if (event.shouldSendTo(getSender(data.channel.getId, data.user)))
list.append(action(SMono.just(data.channel))) //TODO: Only store ID?
list.append(action(Mono.just(data.channel))) //TODO: Only store ID?
MCChatCustom.lastmsgCustom.filter(clmd =>
clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp))
.map(clmd => action(SMono.just(clmd.channel))).foreach(elem => {
.map(clmd => action(Mono.just(clmd.channel))).foreach(elem => {
list.append(elem)
()
})
SMono.whenDelayError(list)
Mono.whenDelayError(list.asJava)
}
/**
@ -212,7 +211,7 @@ object MCChatUtils {
.orElse(Option(getSender(ConnectedSenders, channel, author))) // This doesn't support the public chat, but it'll always return null for it
.orElse(Option(getSender(UnconnectedSenders, channel, author))) //
.orElse(Option(addSender(UnconnectedSenders, author,
new DiscordSender(author, SMono(DiscordPlugin.dc.getChannelById(channel)).block().asInstanceOf[MessageChannel]))))
new DiscordSender(author, DiscordPlugin.dc.getChannelById(channel).block().asInstanceOf[MessageChannel]))))
.get
}
@ -322,7 +321,7 @@ object MCChatUtils {
callEventExcludingSome(new PlayerJoinEvent(dcp, ""))
dcp.setLoggedIn(true)
if (module != null) {
if (module.serverWatcher != null) module.serverWatcher.fakePlayers.add(dcp)
// TODO: ServerWatcher
module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged in from Discord")
}
}
@ -345,7 +344,7 @@ object MCChatUtils {
dcp.setLoggedIn(false)
if (module != null) {
module.log(dcp.getName + " (" + dcp.getUniqueId + ") logged out from Discord")
if (module.serverWatcher != null) module.serverWatcher.fakePlayers.remove(dcp)
// TODO: ServerWatcher
}
}

View file

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

View file

@ -2,8 +2,6 @@ package buttondevteam.discordplugin.mcchat
import buttondevteam.core.component.channel.Channel
import buttondevteam.discordplugin.DPUtils.{MonoExtensions, SpecExtensions}
import buttondevteam.discordplugin.mcchat.playerfaker.ServerWatcher
import buttondevteam.discordplugin.mcchat.playerfaker.perm.LPInjector
import buttondevteam.discordplugin.mcchat.sender.DiscordConnectedPlayer
import buttondevteam.discordplugin.util.DPState
import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordPlugin}
@ -14,7 +12,7 @@ import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.rest.util.Color
import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono
import reactor.core.publisher.Mono
import java.util
import java.util.stream.Collectors
@ -33,27 +31,27 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
def getListener: MCChatListener = this.listener
private var listener: MCChatListener = null
private[mcchat] var serverWatcher: ServerWatcher = null
private var lpInjector: LPInjector = null
private[mcchat] var serverWatcher = null
private var lpInjector = null
private[mcchat] var disabling = false
/**
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
*/
val whitelistedCommands: ConfigData[util.ArrayList[String]] = getConfig.getData("whitelistedCommands",
() => Lists.newArrayList("list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins"))
Lists.newArrayList("list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins"))
/**
* The channel to use as the public Minecraft chat - everything public gets broadcasted here
*/
val chatChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L)
val chatChannel: ConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L)
def chatChannelMono: SMono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get)
def chatChannelMono: Mono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get)
/**
* The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute
*/
val modlogChannel: ReadOnlyConfigData[SMono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel")
val modlogChannel: ConfigData[Mono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel")
/**
* The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here
*/
@ -117,7 +115,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
val chconkeys = chcons.getKeys(false)
for (chconkey <- chconkeys.asScala) {
val chcon = chcons.getConfigurationSection(chconkey)
val mcch = Channel.getChannels.filter((ch: Channel) => ch.ID == chcon.getString("mcchid")).findAny
val mcch = Channel.getChannels.filter((ch: Channel) => ch.getIdentifier == chcon.getString("mcchid")).findAny
val ch = DiscordPlugin.dc.getChannelById(Snowflake.of(chcon.getLong("chid"))).block
val did = chcon.getLong("did")
val user = DiscordPlugin.dc.getUserById(Snowflake.of(did)).block
@ -135,18 +133,9 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
}
}
}
try if (lpInjector == null) lpInjector = new LPInjector //new LPInjector(DiscordPlugin.plugin)
catch {
case e: Exception =>
TBMCCoreAPI.SendException("Failed to init LuckPerms injector", e, this)
case e: NoClassDefFoundError =>
log("No LuckPerms, not injecting")
//e.printStackTrace();
}
// TODO: LPInjector
if (addFakePlayersToBukkit.get) try {
serverWatcher = new ServerWatcher
serverWatcher.enableDisable(true)
log("Finished hooking into the server")
// TODO: Fake players
} catch {
case e: Exception =>
TBMCCoreAPI.SendException("Failed to hack the server (object)! Disable addFakePlayersToBukkit in the config.", e, this)
@ -189,8 +178,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
serverUp.set(false) //Disable even if just the component is disabled because that way it won't falsely report crashes
try //If it's not enabled it won't do anything
if (serverWatcher != null) {
serverWatcher.enableDisable(false)
log("Finished unhooking the server")
// TODO: ServerWatcher
}
catch {
case e: Exception =>
@ -200,7 +188,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
val chconsc = getConfig.getConfig.createSection("chcons")
for (chcon <- chcons) {
val chconc = chconsc.createSection(chcon.channel.getId.asString)
chconc.set("mcchid", chcon.mcchannel.ID)
chconc.set("mcchid", chcon.mcchannel.getIdentifier)
chconc.set("chid", chcon.channel.getId.asLong)
chconc.set("did", chcon.user.getId.asLong)
chconc.set("mcuid", chcon.dcp.getUniqueId.toString)
@ -222,13 +210,13 @@ class MinecraftChatModule extends Component[DiscordPlugin] {
*/
private def sendStateMessage(color: Color, message: String) =
MCChatUtils.forCustomAndAllMCChat(_.flatMap(
_.createEmbed(_.setColor(color).setTitle(message).^^()).^^()
.onErrorResume(_ => SMono.empty)
_.createEmbed(_.setColor(color).setTitle(message))
.onErrorResume(_ => Mono.empty)
), ChannelconBroadcast.RESTART, hookmsg = false).block()
private def sendStateMessage(color: Color, message: String, extra: String) =
MCChatUtils.forCustomAndAllMCChat(_.flatMap(
_.createEmbed(_.setColor(color).setTitle(message).setDescription(extra).^^()).^^()
.onErrorResume(_ => SMono.empty)
_.createEmbed(_.setColor(color).setTitle(message).setDescription(extra))
.onErrorResume(_ => Mono.empty)
), ChannelconBroadcast.RESTART, hookmsg = false).block()
}

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
import buttondevteam.discordplugin.mcchat.MinecraftChatModule
import buttondevteam.discordplugin.mcchat.playerfaker.{DiscordInventory, VCMDWrapper}
import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.*
import org.bukkit.attribute.{Attribute, AttributeInstance, AttributeModifier}
import org.bukkit.entity.{Entity, Player}
import org.bukkit.event.inventory.InventoryType
import org.bukkit.event.player.{AsyncPlayerChatEvent, PlayerTeleportEvent}
import org.bukkit.inventory.{Inventory, PlayerInventory}
import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
@ -31,10 +31,6 @@ object DiscordConnectedPlayer {
try {
if (!Modifier.isAbstract(invocation.getMethod.getModifiers))
invocation.callRealMethod
else if (classOf[PlayerInventory].isAssignableFrom(invocation.getMethod.getReturnType))
Mockito.mock(classOf[DiscordInventory], Mockito.withSettings.extraInterfaces(classOf[PlayerInventory]))
else if (classOf[Inventory].isAssignableFrom(invocation.getMethod.getReturnType))
new DiscordInventory
else
RETURNS_DEFAULTS.answer(invocation)
} catch {
@ -64,7 +60,8 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u
private val basePlayer: OfflinePlayer = if (module == null) null else Bukkit.getOfflinePlayer(uniqueId)
private var perm: PermissibleBase = if (module == null) null else new PermissibleBase(basePlayer)
private val origPerm: PermissibleBase = perm
private val vanillaCmdListener: VCMDWrapper = if (module == null) null else new VCMDWrapper(VCMDWrapper.createListener(this, module))
private val vanillaCmdListener = null // TODO
private val inventory: PlayerInventory = if (module == null) null else Bukkit.createInventory(this, InventoryType.PLAYER).asInstanceOf
override def isPermissionSet(name: String): Boolean = this.origPerm.isPermissionSet(name)
@ -97,7 +94,7 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u
override def setDisplayName(displayName: String): Unit = this.displayName = displayName
override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener
override def getVanillaCmdListener = this.vanillaCmdListener
def isLoggedIn: Boolean = this.loggedIn
@ -197,6 +194,8 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val u
override def getGameMode = GameMode.SPECTATOR
override def getInventory: PlayerInventory = inventory
//noinspection ScalaDeprecation
/*@SuppressWarnings(Array("deprecation")) override def spigot: super.Spigot = new super.Spigot() {
override def getRawAddress: InetSocketAddress = null

View file

@ -1,7 +1,6 @@
package buttondevteam.discordplugin.mcchat.sender
import buttondevteam.discordplugin.mcchat.MinecraftChatModule
import buttondevteam.discordplugin.mcchat.playerfaker.VCMDWrapper
import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.entity.Player
@ -25,9 +24,8 @@ object DiscordPlayerSender {
}
abstract class DiscordPlayerSender(user: User, channel: MessageChannel, var player: Player, val module: Nothing) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordPlayerSender] {
val vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, player, module))
override def getVanillaCmdListener: VCMDWrapper = this.vanillaCmdListener
override def getVanillaCmdListener = null
override def sendMessage(message: String): Unit = {
player.sendMessage(message)
@ -35,7 +33,7 @@ abstract class DiscordPlayerSender(user: User, channel: MessageChannel, var play
}
override def sendMessage(messages: Array[String]): Unit = {
player.sendMessage(messages)
player.sendMessage(messages*)
super.sendMessage(messages)
}
}

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
package buttondevteam.discordplugin.role
import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.DPUtils.{FluxExtensions, MonoExtensions}
import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.architecture.{Component, ComponentMetadata}
import discord4j.core.`object`.entity.Role
@ -9,7 +8,7 @@ import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleEvent, RoleUpdateEvent}
import discord4j.rest.util.Color
import org.bukkit.Bukkit
import reactor.core.scala.publisher.SMono
import reactor.core.publisher.Mono
import java.util.Collections
import scala.jdk.CollectionConverters.SeqHasAsJava
@ -30,14 +29,14 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
val role = roleCreateEvent.getRole
if (!notMainServer(role)) {
grm.isGameRole(role).flatMap(b => {
if (!b) SMono.empty //Deleted or not a game role
if (!b) Mono.empty //Deleted or not a game role
else {
GameRoles.add(role.getName)
if (logChannel != null)
logChannel.flatMap(_.createMessage("Added " + role.getName + " as game role." +
" If you don't want this, change the role's color from the game role color.").^^())
" If you don't want this, change the role's color from the game role color."))
else
SMono.empty
Mono.empty
}
}).subscribe()
()
@ -48,7 +47,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
if (role == null) return ()
if (notMainServer(role)) return ()
if (GameRoles.remove(role.getName) && logChannel != null)
logChannel.flatMap(_.createMessage("Removed " + role.getName + " as a game role.").^^()).subscribe()
logChannel.flatMap(_.createMessage("Removed " + role.getName + " as a game role.")).subscribe()
case roleUpdateEvent: RoleUpdateEvent =>
if (!roleUpdateEvent.getOld.isPresent) {
grm.logWarn("Old role not stored, cannot update game role!")
@ -60,21 +59,21 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
grm.isGameRole(cr).flatMap(isGameRole => {
if (!isGameRole)
if (GameRoles.remove(or.getName) && logChannel != null)
logChannel.flatMap(_.createMessage("Removed " + or.getName + " as a game role because its color changed.").^^())
logChannel.flatMap(_.createMessage("Removed " + or.getName + " as a game role because its color changed."))
else
SMono.empty
Mono.empty
else if (GameRoles.contains(or.getName) && or.getName == cr.getName)
SMono.empty
Mono.empty
else {
val removed = GameRoles.remove(or.getName) //Regardless of whether it was a game role
GameRoles.add(cr.getName) //Add it because it has no color
if (logChannel != null)
if (removed)
logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Changed game role from " + or.getName + " to " + cr.getName + ".").^^())
logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Changed game role from " + or.getName + " to " + cr.getName + "."))
else
logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + cr.getName + " as game role because it has the color of one.").^^())
logChannel.flatMap((ch: MessageChannel) => ch.createMessage("Added " + cr.getName + " as game role because it has the color of one."))
else
SMono.empty
Mono.empty
}
}).subscribe()
case _ =>
@ -88,7 +87,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
override protected def enable(): Unit = {
getPlugin.manager.registerCommand(command)
GameRoles = DiscordPlugin.mainServer.getRoles.^^().filterWhen(this.isGameRole).map(_.getName).collectSeq().block().asJava
GameRoles = DiscordPlugin.mainServer.getRoles.filterWhen(this.isGameRole).map(_.getName).collectList().block()
}
override protected def disable(): Unit = getPlugin.manager.unregisterCommand(command)
@ -101,14 +100,16 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
* The role color that is used by game roles.
* Defaults to the second to last in the upper row - #95a5a6.
*/
final private val roleColor = getConfig.getConfig[Color]("roleColor").`def`(Color.of(149, 165, 166)).getter((rgb: Any) => Color.of(Integer.parseInt(rgb.asInstanceOf[String].substring(1), 16))).setter((color: Color) => String.format("#%08x", color.getRGB)).buildReadOnly
final private val roleColor = getConfig.getData("roleColor", Color.of(149, 165, 166),
rgb => Color.of(Integer.parseInt(rgb.asInstanceOf[String].substring(1), 16)),
color => String.format("#%08x", color.getRGB), true)
private def isGameRole(r: Role): SMono[Boolean] = {
if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return SMono.just(false) //Only allow on the main server
private def isGameRole(r: Role): Mono[java.lang.Boolean] = {
if (r.getGuildId.asLong != DiscordPlugin.mainServer.getId.asLong) return Mono.just(false) //Only allow on the main server
val rc = roleColor.get
if (r.getColor equals rc)
DiscordPlugin.dc.getSelf.flatMap(u => u.asMember(DiscordPlugin.mainServer.getId)).^^()
.flatMap(_.hasHigherRoles(Collections.singleton(r.getId)).^^().asInstanceOf).defaultIfEmpty(false) //Below one of our roles
else SMono.just(false)
DiscordPlugin.dc.getSelf.flatMap(u => u.asMember(DiscordPlugin.mainServer.getId))
.flatMap(_.hasHigherRoles(Collections.singleton(r.getId))).defaultIfEmpty(false) //Below one of our roles
else Mono.just(false)
}
}