Use Scala version of Reactor & data types

This commit is contained in:
Norbi Peti 2021-03-02 01:18:20 +01:00
parent c57ac26b2d
commit fce6b91b97
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
18 changed files with 282 additions and 314 deletions

15
pom.xml
View file

@ -78,21 +78,6 @@
</useSystemClassLoader> <!-- https://stackoverflow.com/a/53012553/2703239 -->
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>pre-scala</id>
<configuration>
<compilerArgs>-proc:only</compilerArgs>
</configuration>
<phase>generate-sources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>

View file

@ -6,10 +6,10 @@ import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.core.`object`.entity.{Guild, Message, Role}
import discord4j.core.spec.EmbedCreateSpec
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.SMono
import java.util
import java.util.{Comparator, Optional}
import java.util.Comparator
import java.util.logging.Logger
import java.util.regex.Pattern
import javax.annotation.Nullable
@ -62,8 +62,8 @@ object DPUtils {
return matcher.replaceAll(aFunctionalInterface); //Find nearest URL match and if it's not reaching to the char then escape*/ val sb = new StringBuffer
while ( {
matcher.find
}) matcher.appendReplacement(sb, if (Optional.ofNullable(ts.floor(Array[Int](matcher.start, 0))).map( //Find a URL start <= our start
(a: Array[Int]) => a(1)).orElse(-1) < matcher.start //Check if URL end < our start
}) matcher.appendReplacement(sb, if (Option(ts.floor(Array[Int](matcher.start, 0))).map( //Find a URL start <= our start
(a: Array[Int]) => a(1)).getOrElse(-1) < matcher.start //Check if URL end < our start
) "\\\\" + matcher.group else matcher.group)
matcher.appendTail(sb)
sb.toString
@ -74,22 +74,22 @@ object DPUtils {
DiscordPlugin.plugin.getLogger
}
def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[Mono[MessageChannel]] =
def channelData(config: IHaveConfig, key: String): ReadOnlyConfigData[SMono[MessageChannel]] =
config.getReadOnlyDataPrimDef(key, 0L, (id: Any) =>
getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), (_: Mono[MessageChannel]) => 0L) //We can afford to search for the channel in the cache once (instead of using mainServer)
def roleData(config: IHaveConfig, key: String, defName: String): ReadOnlyConfigData[Mono[Role]] =
roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer))
getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), (_: SMono[MessageChannel]) => 0L) //We can afford to search for the channel in the cache once (instead of using mainServer)
def roleData(config: IHaveConfig, key: String, defName: String): ReadOnlyConfigData[SMono[Role]] =
roleData(config, key, defName, SMono.just(DiscordPlugin.mainServer))
/**
* Needs to be a {@link ConfigData} for checking if it's set
*/
def roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): ReadOnlyConfigData[Mono[Role]] = config.getReadOnlyDataPrimDef(key, defName, (name: Any) => {
def foo(name: Any): Mono[Role] = {
if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) return Mono.empty[Role]
def roleData(config: IHaveConfig, key: String, defName: String, guild: SMono[Guild]): ReadOnlyConfigData[SMono[Role]] = config.getReadOnlyDataPrimDef(key, defName, (name: Any) => {
def foo(name: Any): SMono[Role] = {
if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) return SMono.empty[Role]
guild.flatMapMany(_.getRoles).filter((r: Role) => r.getName == name).onErrorResume((e: Throwable) => {
def foo(e: Throwable): Mono[Role] = {
def foo(e: Throwable): SMono[Role] = {
getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage)
Mono.empty[Role]
SMono.empty[Role]
}
foo(e)
@ -97,7 +97,7 @@ object DPUtils {
}
foo(name)
}, (_: Mono[Role]) => defName)
}, (_: SMono[Role]) => defName)
def snowflakeData(config: IHaveConfig, key: String, defID: Long): ReadOnlyConfigData[Snowflake] =
config.getReadOnlyDataPrimDef(key, defID, (id: Any) => Snowflake.of(id.asInstanceOf[Long]), _.asLong)
@ -137,7 +137,7 @@ object DPUtils {
*/
def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = {
//noinspection ConstantConditions
if (result == null || (result.isInstanceOf[Mono[_]] && !result.asInstanceOf[Mono[_]].hasElement.block)) {
if (result == null || (result.isInstanceOf[SMono[_]] && !result.asInstanceOf[SMono[_]].hasElement.block())) {
var path: String = null
try {
if (component != null) Component.setComponentEnabled(component, false)
@ -157,26 +157,26 @@ object DPUtils {
}
/**
* Send a response in the form of "@User, message". Use Mono.empty() if you don't have a channel object.
* Send a response in the form of "@User, message". Use SMono.empty() if you don't have a channel object.
*
* @param original The original message to reply to
* @param channel The channel to send the message in, defaults to the original
* @param message The message to send
* @return A mono to send the message
*/
def reply(original: Message, @Nullable channel: MessageChannel, message: String): Mono[Message] = {
val ch = if (channel == null) original.getChannel
else Mono.just(channel)
def reply(original: Message, @Nullable channel: MessageChannel, message: String): SMono[Message] = {
val ch = if (channel == null) SMono(original.getChannel)
else SMono.just(channel)
reply(original, ch, message)
}
/**
* @see #reply(Message, MessageChannel, String)
*/
def reply(original: Message, ch: Mono[MessageChannel], message: String): Mono[Message] =
ch.flatMap(_.createMessage((if (original.getAuthor.isPresent)
def reply(original: Message, ch: SMono[MessageChannel], message: String): SMono[Message] =
ch.flatMap(channel => SMono(channel.createMessage((if (original.getAuthor.isPresent)
original.getAuthor.get.getMention + ", "
else "") + message))
else "") + message)))
def nickMention(userId: Snowflake): String = "<@!" + userId.asString + ">"
@ -189,21 +189,21 @@ object DPUtils {
* @param id The channel ID
* @return A message channel
*/
def getMessageChannel(key: String, id: Snowflake): Mono[MessageChannel] = {
if (id.asLong == 0L) return Mono.empty[MessageChannel]
def getMessageChannel(key: String, id: Snowflake): SMono[MessageChannel] = {
if (id.asLong == 0L) return SMono.empty[MessageChannel]
DiscordPlugin.dc.getChannelById(id).onErrorResume(e => {
SMono(DiscordPlugin.dc.getChannelById(id)).onErrorResume(e => {
def foo(e: Throwable) = {
getLogger.warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage)
Mono.empty
SMono.empty
}
foo(e)
}).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel])
}).filter(ch => ch.isInstanceOf[MessageChannel]).cast[MessageChannel]
}
def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] =
def getMessageChannel(config: ConfigData[Snowflake]): SMono[MessageChannel] =
getMessageChannel(config.getPath, config.get)
def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.empty)
def ignoreError[T](mono: SMono[T]): SMono[T] = mono.onErrorResume((_: Throwable) => SMono.empty)
}

View file

@ -15,8 +15,8 @@ import org.bukkit.inventory.{Inventory, PlayerInventory}
import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
import org.bukkit.plugin.Plugin
import org.mockito.Answers.RETURNS_DEFAULTS
import org.mockito.{MockSettings, Mockito}
import org.mockito.invocation.InvocationOnMock
import org.mockito.{MockSettings, Mockito}
import java.lang.reflect.Modifier
import java.net.InetSocketAddress
@ -52,7 +52,24 @@ object DiscordConnectedPlayer {
}).stubOnly
}
abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordConnectedPlayer] {
/**
* @constructor The parameters must match with {@link #create ( User, MessageChannel, UUID, String, MinecraftChatModule)}
* @param user May be null.
* @param channel May not be null.
* @param uniqueId The UUID of the player.
* @param name The Minecraft name of the player.
* @param module The MinecraftChatModule or null if testing.
*/
abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel, val uniqueId: UUID, val name: String, val module: MinecraftChatModule) extends DiscordSenderBase(user, channel) with IMCPlayer[DiscordConnectedPlayer] {
private var loggedIn = false
private var displayName: String = name
private var location: Location = if (module == null) null else Bukkit.getWorlds.get(0).getSpawnLocation
private val basePlayer: OfflinePlayer = if (module == null) null else Bukkit.getOfflinePlayer(uniqueId)
private var perm: PermissibleBase = if (module == null) null else new PermissibleBase(basePlayer)
private val origPerm: PermissibleBase = perm
private val vanillaCmdListener: VCMDWrapper = if (module == null) null else new VCMDWrapper(VCMDWrapper.createListener(this, module))
override def isPermissionSet(name: String): Boolean = this.origPerm.isPermissionSet(name)
override def isPermissionSet(perm: Permission): Boolean = this.origPerm.isPermissionSet(perm)
@ -98,40 +115,11 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten
override def getDisplayName: String = this.displayName
private var vanillaCmdListener: VCMDWrapper = null
private var loggedIn = false
private var origPerm: PermissibleBase = null
private var name: String = null
private var basePlayer: OfflinePlayer = null
private var perm: PermissibleBase = null
private var location: Location = null
final private var module: MinecraftChatModule = null
final private var uniqueId: UUID = null
final private var displayName: String = null
/**
* The parameters must match with {@link #create ( User, MessageChannel, UUID, String, MinecraftChatModule)}
*/
def this(user: User, channel: MessageChannel, uuid: UUID, mcname: String, module: MinecraftChatModule) {
this(user, channel)
location = Bukkit.getWorlds.get(0).getSpawnLocation
perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid))
origPerm = perm
name = mcname
this.module = module
uniqueId = uuid
displayName = mcname
vanillaCmdListener = new VCMDWrapper(VCMDWrapper.createListener(this, module))
}
/**
* For testing
*/
def this(user: User, channel: MessageChannel) {
this(user, channel)
module = null
uniqueId = UUID.randomUUID
}
def this(user: User, channel: MessageChannel) =
this(user, channel, UUID.randomUUID(), "Test", null)
override def setOp(value: Boolean): Unit = { //CraftPlayer-compatible implementation
this.origPerm.setOp(value)
@ -214,7 +202,7 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten
override def getGameMode = GameMode.SPECTATOR
//noinspection ScalaDeprecation
@SuppressWarnings(Array("deprecation")) final private val spigot: Spigot = new Spigot() {
@SuppressWarnings(Array("deprecation")) override def spigot: Spigot = new Spigot() {
override def getRawAddress: InetSocketAddress = null
override def playEffect(location: Location, effect: Effect, id: Int, data: Int, offsetX: Float, offsetY: Float, offsetZ: Float, speed: Float, particleCount: Int, radius: Int): Unit = {
@ -241,11 +229,9 @@ abstract class DiscordConnectedPlayer(user: User, channel: MessageChannel) exten
override def sendMessage(position: ChatMessageType, component: BaseComponent): Unit =
sendMessage(component) //Ignore position
override def sendMessage(position: ChatMessageType, components: BaseComponent*) =
sendMessage(components)
override def sendMessage(position: ChatMessageType, components: BaseComponent*): Unit =
sendMessage(components: _*)
override def isInvulnerable = true
}
override def spigot: Spigot = spigot
}

View file

@ -15,12 +15,12 @@ import buttondevteam.lib.architecture._
import buttondevteam.lib.player.ChromaGamerBase
import com.google.common.io.Files
import discord4j.common.util.Snowflake
import discord4j.core.{DiscordClientBuilder, GatewayDiscordClient}
import discord4j.core.`object`.entity.{ApplicationInfo, Guild, Role}
import discord4j.core.`object`.presence.{Activity, Presence}
import discord4j.core.`object`.reaction.ReactionEmoji
import discord4j.core.event.domain.guild.GuildCreateEvent
import discord4j.core.event.domain.lifecycle.ReadyEvent
import discord4j.core.{DiscordClientBuilder, GatewayDiscordClient}
import discord4j.gateway.ShardInfo
import discord4j.store.jdk.JdkStoreService
import org.apache.logging.log4j.LogManager
@ -29,7 +29,7 @@ import org.bukkit.command.CommandSender
import org.bukkit.configuration.file.YamlConfiguration
import org.mockito.internal.util.MockUtil
import reactor.core.Disposable
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.SMono
import java.io.File
import java.nio.charset.StandardCharsets
@ -65,13 +65,16 @@ import java.util.Optional
* The main server where the roles and other information is pulled from. It's automatically set to the first server the bot's invited to.
*/
private def mainServer = getIConfig.getDataPrimDef("mainServer", 0L, (id: Any) => {
def foo(id: Any) = { //It attempts to get the default as well
if (id.asInstanceOf[Long] == 0L) Optional.empty //Hack?
else DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long])).onErrorResume((t: Throwable) => Mono.fromRunnable(() => getLogger.warning("Failed to get guild: " + t.getMessage))).blockOptional
def foo(id: Any): Option[Guild] = { //It attempts to get the default as well
if (id.asInstanceOf[Long] == 0L) Option.empty
else SMono.fromPublisher(DiscordPlugin.dc.getGuildById(Snowflake.of(id.asInstanceOf[Long])))
.onErrorResume((t: Throwable) => {
getLogger.warning("Failed to get guild: " + t.getMessage); SMono.empty
}).blockOption()
}
foo(id)
}, (g: Optional[Guild]) => (g.map((gg: Guild) => gg.getId.asLong): Optional[Long]).orElse(0L))
}, (g: Option[Guild]) => (g.map(_.getId.asLong): Option[Long]).getOrElse(0L))
/**
* The (bot) channel to use for Discord commands like /role.
@ -81,7 +84,7 @@ import java.util.Optional
* The role that allows using mod-only Discord commands.
* If empty (''), then it will only allow for the owner.
*/
var modRole: ReadOnlyConfigData[Mono[Role]] = null
var modRole: ReadOnlyConfigData[SMono[Role]] = null
/**
* The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access.
*/
@ -153,7 +156,9 @@ import java.util.Optional
}
private def stopStarting(): Unit = {
this synchronized (starting = false)
this synchronized {
starting = false
}
notifyAll()
}
@ -164,7 +169,7 @@ import java.util.Optional
DiscordPlugin.dc.updatePresence(Presence.online(Activity.playing("Minecraft"))).subscribe //Update from the initial presence
return
}
DiscordPlugin.mainServer = mainServer.get.orElse(null) //Shouldn't change afterwards
DiscordPlugin.mainServer = mainServer.get.orNull //Shouldn't change afterwards
if (DiscordPlugin.mainServer == null) {
if (event.size == 0) {
getLogger.severe("Main server not found! Invite the bot and do /discord restart")
@ -174,7 +179,7 @@ import java.util.Optional
}
DiscordPlugin.mainServer = event.get(0).getGuild
getLogger.warning("Main server set to first one: " + DiscordPlugin.mainServer.getName)
mainServer.set(Optional.of(DiscordPlugin.mainServer)) //Save in config
mainServer.set(Option(DiscordPlugin.mainServer)) //Save in config
}
DiscordPlugin.SafeMode = false
setupConfig()

View file

@ -2,28 +2,24 @@ package buttondevteam.discordplugin
import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel
import org.bukkit.{Bukkit, Server}
import org.bukkit.command.CommandSender
import org.bukkit.permissions.{PermissibleBase, Permission, PermissionAttachment, PermissionAttachmentInfo}
import org.bukkit.plugin.Plugin
import reactor.core.publisher.Mono
import org.bukkit.{Bukkit, Server}
import reactor.core.scala.publisher.SMono
import java.util
class DiscordSender(user: User, channel: MessageChannel) extends DiscordSenderBase(user, channel) with CommandSender {
class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender {
private val perm = new PermissibleBase(this)
private var name: String = null
private val name: String = Option(pname)
.orElse(Option(user).map(u => SMono(u.asMember(DiscordPlugin.mainServer.getId))
.onErrorResume(_ => SMono.empty).blockOption()
.map(u => u.getDisplayName)))
.getOrElse("Discord user")
def this(user: User, channel: MessageChannel) {
this(user, channel)
val `def` = "Discord user"
name = if (user == null) `def`
else user.asMember(DiscordPlugin.mainServer.getId).onErrorResume((_: Throwable) => Mono.empty).blockOptional.map(_.getDisplayName).orElse(`def`)
}
def this(user: User, channel: MessageChannel, name: String) {
this(user, channel)
this.name = name
this(user, channel, null)
}
override def isPermissionSet(name: String): Boolean = perm.isPermissionSet(name)

View file

@ -90,7 +90,8 @@ import reactor.core.publisher.Flux
}
if (msgsb.length > 0) channel.get.flatMap((ch: MessageChannel) => ch.createMessage(msgsb.toString)).flatMap(Message.pin).subscribe
if (modmsgsb.length > 0) modChannel.get.flatMap((ch: MessageChannel) => ch.createMessage(modmsgsb.toString)).flatMap(Message.pin).subscribe
if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded} catch {
if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded
} catch {
case e: Exception =>
e.printStackTrace()
}

View file

@ -7,8 +7,7 @@ import buttondevteam.lib.chat.{Command2, CommandClass}
object VersionCommand {
def getVersion: Array[String] = {
val desc = DiscordPlugin.plugin.getDescription
Array[String]( //
desc.getFullName, desc.getWebsite //)
Array[String](desc.getFullName, desc.getWebsite)
}
}

View file

@ -1,7 +1,6 @@
package buttondevteam.discordplugin.exceptions
import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.exceptions.ExceptionListenerModule.SendException
import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.architecture.Component
import buttondevteam.lib.{TBMCCoreAPI, TBMCExceptionEvent}
@ -12,6 +11,7 @@ import org.bukkit.Bukkit
import org.bukkit.event.{EventHandler, Listener}
import reactor.core.publisher.Mono
import java.util
import java.util.stream.Collectors
/**
@ -65,7 +65,10 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener {
@EventHandler def onException(e: TBMCExceptionEvent): Unit = {
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass)) return
if (lastthrown.stream.anyMatch((ex: Throwable) => util.Arrays.equals(e.getException.getStackTrace, ex.getStackTrace) && (if (e.getException.getMessage == null) ex.getMessage == null
else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message && lastsourcemsg.contains(e.getSourceMessage)) { return }
else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message
&& lastsourcemsg.contains(e.getSourceMessage)) {
return
}
ExceptionListenerModule
.SendException(e.getException, e.getSourceMessage)
if (lastthrown.size >= 10) lastthrown.remove(0)

View file

@ -59,7 +59,7 @@ object FunModule {
return true //Handled
}
lastlistp = Bukkit.getOnlinePlayers.size.toShort //Didn't handle
if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady.get.anyMatch(msglowercased.contains)) {
if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady).get.anyMatch(msglowercased.contains _)) {
var next = 0
if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings()
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size))

View file

@ -6,7 +6,7 @@ import buttondevteam.lib.TBMCCoreAPI
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel}
import discord4j.core.`object`.entity.{Member, Message, Role, User}
import reactor.core.publisher.{Flux, Mono}
import reactor.core.scala.publisher.{SFlux, SMono}
import java.util.concurrent.atomic.AtomicBoolean
@ -18,14 +18,14 @@ object CommandListener {
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
* @return Whether it <b>did not run</b> the command
*/
def runCommand(message: Message, commandChannelID: Snowflake, mentionedonly: Boolean): Mono[Boolean] = {
def runCommand(message: Message, commandChannelID: Snowflake, mentionedonly: Boolean): SMono[Boolean] = {
val timings = CommonListeners.timings
val ret = Mono.just(true)
val ret = SMono.just(true)
if (message.getContent.isEmpty) return ret //Pin messages and such, let the mcchat listener deal with it
val content = message.getContent
timings.printElapsed("A")
message.getChannel.flatMap((channel: MessageChannel) => {
def foo(channel: MessageChannel): Mono[Boolean] = {
SMono(message.getChannel).flatMap((channel: MessageChannel) => {
def foo(channel: MessageChannel): SMono[Boolean] = {
var tmp = ret
if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
timings.printElapsed("B")
@ -33,22 +33,24 @@ object CommandListener {
return ret
}
timings.printElapsed("C")
tmp = ret.`then`(channel.`type`).thenReturn(true) // Fun (this true is ignored - x)
tmp = ret.`then`(SMono(channel.`type`)).`then`(ret) // Fun (this true is ignored - x)
}
val cmdwithargs = new StringBuilder(content)
val gotmention = new AtomicBoolean
timings.printElapsed("Before self")
tmp.flatMapMany((x: Boolean) => DiscordPlugin.dc.getSelf.flatMap((self: User) => self.asMember(DiscordPlugin.mainServer.getId)).flatMapMany((self: Member) => {
def foo(self: Member): Flux[String] = {
timings.printElapsed("D")
gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message))
gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get)
val mentions = message.getRoleMentions
self.getRoles.filterWhen((r: Role) => mentions.any((rr: Role) => rr.getName == r.getName)).map(_.getMention)
}
tmp.flatMapMany((_: Boolean) => SMono(DiscordPlugin.dc.getSelf)
.flatMap((self: User) => SMono(self.asMember(DiscordPlugin.mainServer.getId)))
.flatMapMany((self: Member) => {
def foo(self: Member) = {
timings.printElapsed("D")
gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message))
gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get)
val mentions = SFlux(message.getRoleMentions)
SFlux(self.getRoles).filterWhen((r: Role) => mentions.any((rr: Role) => rr.getName == r.getName)).map(_.getMention)
}
foo(self)
}).map((mentionRole: String) => {
foo(self)
}).map((mentionRole: String) => {
def foo(mentionRole: String): Boolean = {
timings.printElapsed("E")
gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get) // Delete all mentions
@ -56,17 +58,20 @@ object CommandListener {
}
foo(mentionRole)
}: Boolean)[Mono[Boolean]].switchIfEmpty(Mono.fromSupplier[Boolean](() => !mentionedonly || gotmention.get)))[Mono[Boolean]].filter((b: Boolean) => b).last(false).filter((b: Boolean) => b).doOnNext((b: Boolean) => channel.`type`.subscribe).flatMap((b: Boolean) => {
def foo(): Mono[Boolean] = {
}).switchIfEmpty(SMono.fromCallable(() => !mentionedonly || gotmention.get))).filter(b => b)
.last(Option(false)).filter(b => b)
.doOnNext(_ => channel.`type`.subscribe).flatMap(_ => {
def foo(): SMono[Boolean] = {
val cmdwithargsString = cmdwithargs.toString
try {
timings.printElapsed("F")
if (!DiscordPlugin.plugin.manager.handleCommand(new Command2DCSender(message), cmdwithargsString)) return DPUtils.reply(message, channel, "unknown command. Do " + DiscordPlugin.getPrefix + "help for help.").map((_: Message) => false)
if (!DiscordPlugin.plugin.manager.handleCommand(new Command2DCSender(message), cmdwithargsString))
return DPUtils.reply(message, channel, "unknown command. Do " + DiscordPlugin.getPrefix + "help for help.").map(_ => false)
} catch {
case e: Exception =>
TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e, DiscordPlugin.plugin)
}
Mono.just(false) //If the command succeeded or there was an error, return false
SMono.just(false) //If the command succeeded or there was an error, return false
}
foo()

View file

@ -13,6 +13,7 @@ import discord4j.core.event.domain.PresenceUpdateEvent
import discord4j.core.event.domain.message.MessageCreateEvent
import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent}
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.SMono
object CommonListeners {
val timings = new Timings
@ -36,24 +37,24 @@ object CommonListeners {
if (!author.isPresent || author.get.isBot) return `def`
if (FunModule.executeMemes(event.getMessage)) return `def`
val commandChannel = DiscordPlugin.plugin.commandChannel.get
event.getMessage.getChannel.map((mch: MessageChannel) => (commandChannel != null && mch.getId.asLong == commandChannel.asLong) //If mentioned, that's higher than chat
SMono(event.getMessage.getChannel).map((mch: MessageChannel) => (commandChannel != null && mch.getId.asLong == commandChannel.asLong) //If mentioned, that's higher than chat
|| mch.isInstanceOf[PrivateChannel] || event.getMessage.getContent.contains("channelcon")).flatMap(
(shouldRun: Boolean) => { //Only 'channelcon' is allowed in other channels
def foo(shouldRun: Boolean): Mono[Boolean] = { //Only continue if this doesn't handle the event
if (!shouldRun) return Mono.just(true) //The condition is only for the first command execution, not mcchat
def foo(shouldRun: Boolean): SMono[Boolean] = { //Only continue if this doesn't handle the event
if (!shouldRun) return SMono.just(true) //The condition is only for the first command execution, not mcchat
timings.printElapsed("Run command 1")
CommandListener.runCommand(event.getMessage, commandChannel, mentionedonly = true) //#bot is handled here
}
foo(shouldRun)
}).filterWhen((ch: Any) => {
def foo(): Mono[Boolean] = {
def foo() = {
timings.printElapsed("mcchat")
val mcchat = Component.getComponents.get(classOf[MinecraftChatModule])
if (mcchat != null && mcchat.isEnabled) { //ComponentManager.isEnabled() searches the component again
return mcchat.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event) //Also runs Discord commands in chat channels
}
Mono.just(true) //Wasn't handled, continue
SMono.just(true) //Wasn't handled, continue
}
foo()

View file

@ -1,27 +1,24 @@
package buttondevteam.discordplugin.mcchat
import buttondevteam.core.component.channel.Channel
import buttondevteam.core.component.channel.ChatRoom
import buttondevteam.core.component.channel.{Channel, ChatRoom}
import buttondevteam.discordplugin.ChannelconBroadcast.ChannelconBroadcast
import buttondevteam.discordplugin._
import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC}
import buttondevteam.lib.TBMCSystemChatEvent
import buttondevteam.lib.chat.Command2
import buttondevteam.lib.chat.CommandClass
import buttondevteam.lib.player.TBMCPlayer
import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer}
import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel}
import discord4j.rest.util.{Permission, PermissionSet}
import lombok.RequiredArgsConstructor
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.SMono
import javax.annotation.Nullable
import java.lang.reflect.Method
import java.util
import java.util.{Objects, Optional}
import java.util.function.Supplier
import java.util.stream.Collectors
import java.util.{Objects, Optional}
import javax.annotation.Nullable
@SuppressWarnings(Array("SimplifyOptionalCallChains")) //Java 11
@CommandClass(helpText = Array(Array("Channel connect", //
@ -37,11 +34,11 @@ import java.util.stream.Collectors
class ChannelconCommand(private val module: MinecraftChatModule) extends ICommand2DC {
@Command2.Subcommand def remove(sender: Command2DCSender): Boolean = {
val message = sender.getMessage
if (checkPerms(message, null)) true
if (checkPerms(message, null)) return true
else if (MCChatCustom.removeCustomChat(message.getChannelId))
DPUtils.reply(message, Mono.empty, "channel connection removed.").subscribe
DPUtils.reply(message, SMono.empty, "channel connection removed.").subscribe
else
DPUtils.reply(message, Mono.empty, "this channel isn't connected.").subscribe
DPUtils.reply(message, SMono.empty, "this channel isn't connected.").subscribe
true
}
@ -54,31 +51,30 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
if (cc == null) {
return respond(sender, "this channel isn't connected.")
}
val togglesString: Supplier[String] = () => util.Arrays.stream(ChannelconBroadcast.values)
.map((t: ChannelconBroadcast) =>
t.toString.toLowerCase + ": " + (if ((cc.toggles & t.flag) == 0) "disabled" else "enabled"))
.collect(Collectors.joining("\n")) + "\n\n" +
val togglesString: Supplier[String] = () => ChannelconBroadcast.values
.map(t => t.toString.toLowerCase + ": " + (if ((cc.toggles & (1 << t.id)) == 0) "disabled" else "enabled"))
.mkString("\n") + "\n\n" +
TBMCSystemChatEvent.BroadcastTarget.stream.map((target: TBMCSystemChatEvent.BroadcastTarget) =>
target.getName + ": " + (if (cc.brtoggles.contains(target)) "enabled" else "disabled"))
.collect(Collectors.joining("\n"))
if (toggle == null) {
DPUtils.reply(message, Mono.empty, "toggles:\n" + togglesString.get).subscribe
DPUtils.reply(message, SMono.empty, "toggles:\n" + togglesString.get).subscribe
return true
}
val arg: String = toggle.toUpperCase
val b: Optional[ChannelconBroadcast] = util.Arrays.stream(ChannelconBroadcast.values).filter((t: ChannelconBroadcast) => t.toString == arg).findAny
if (!b.isPresent) {
val b = ChannelconBroadcast.values.find((t: ChannelconBroadcast) => t.toString == arg)
if (b.isEmpty) {
val bt: TBMCSystemChatEvent.BroadcastTarget = TBMCSystemChatEvent.BroadcastTarget.get(arg)
if (bt == null) {
DPUtils.reply(message, Mono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe
DPUtils.reply(message, SMono.empty, "cannot find toggle. Toggles:\n" + togglesString.get).subscribe
return true
}
val add: Boolean = !(cc.brtoggles.contains(bt))
if (add) {
cc.brtoggles.add(bt)
cc.brtoggles += bt
}
else {
cc.brtoggles.remove(bt)
cc.brtoggles -= bt
}
return respond(sender, "'" + bt.getName + "' " + (if (add) "en" else "dis") + "abled")
}
@ -89,10 +85,10 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
//1 0 | 1
//1 1 | 0
// XOR
cc.toggles ^= b.get.flag
DPUtils.reply(message, Mono.empty, "'" + b.get.toString.toLowerCase + "' "
+ (if ((cc.toggles & b.get.flag) == 0) "disabled" else "enabled")).subscribe
return true
cc.toggles ^= (1 << b.get.id)
DPUtils.reply(message, SMono.empty, "'" + b.get.toString.toLowerCase + "' "
+ (if ((cc.toggles & (1 << b.get.id)) == 0) "disabled" else "enabled")).subscribe
true
}
@Command2.Subcommand def `def`(sender: Command2DCSender, channelID: String): Boolean = {
@ -139,14 +135,14 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
return true;
}*/
//TODO: "Channel admins" that can connect channels?
MCChatCustom.addCustomChat(channel, groupid, chan.get, author, dcp, 0, new util.HashSet[TBMCSystemChatEvent.BroadcastTarget])
MCChatCustom.addCustomChat(channel, groupid, chan.get, author, dcp, 0, Set())
if (chan.get.isInstanceOf[ChatRoom]) {
DPUtils.reply(message, channel, "alright, connection made to the room!").subscribe
}
else {
DPUtils.reply(message, channel, "alright, connection made to group `" + groupid + "`!").subscribe
}
return true
true
}
@SuppressWarnings(Array("ConstantConditions"))
@ -167,7 +163,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
false
}
def getHelpText(method: Method, ann: Command2.Subcommand): Array[String] =
override def getHelpText(method: Method, ann: Command2.Subcommand): Array[String] =
Array[String](
"Channel connect",
"This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).",
@ -178,6 +174,6 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman
"To remove a connection use @ChromaBot channelcon remove in the channel.",
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix + " prefix only works in " + DPUtils.botmention + ".",
"Invite link: <https://discordapp.com/oauth2/authorize?client_id="
+ DiscordPlugin.dc.getApplicationInfo.map((info) => info.getId.asString).blockOptional.orElse("Unknown")
+ SMono(DiscordPlugin.dc.getApplicationInfo).map(info => info.getId.asString).blockOption().getOrElse("Unknown")
+ "&scope=bot&permissions=268509264>")
}

View file

@ -18,7 +18,7 @@ object MCChatCustom {
*/
private[mcchat] val lastmsgCustom = new util.ArrayList[MCChatCustom.CustomLMD]
def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: util.Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = {
def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = {
lastmsgCustom synchronized {
var gid: String = null
mcchannel match {
@ -58,9 +58,9 @@ object MCChatCustom {
def getCustomChats: util.List[CustomLMD] = Collections.unmodifiableList(lastmsgCustom)
class CustomLMD private(@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String,
@NonNull val mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int,
var brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]) extends MCChatUtils.LastMsgData(channel, user) {
class CustomLMD private[mcchat](@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String,
@NonNull mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int,
var brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]) extends MCChatUtils.LastMsgData(channel, user, mcchannel) {
}
}

View file

@ -21,6 +21,7 @@ import org.bukkit.entity.Player
import org.bukkit.event.{EventHandler, Listener}
import org.bukkit.scheduler.BukkitTask
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.{SFlux, SMono}
import java.time.Instant
import java.util
@ -244,30 +245,30 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
private var recthread: Thread = null
// Discord
def handleDiscord(ev: MessageCreateEvent): Mono[Boolean] = {
def handleDiscord(ev: MessageCreateEvent): SMono[Boolean] = {
val timings: Timings = CommonListeners.timings
timings.printElapsed("Chat event")
val author: Optional[User] = ev.getMessage.getAuthor
val hasCustomChat: Boolean = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId)
val prefix: Char = DiscordPlugin.getPrefix
return ev.getMessage.getChannel.filter((channel: MessageChannel) => {
def foo(channel: MessageChannel) = {
timings.printElapsed("Filter 1")
return !((ev.getMessage.getChannelId.asLong != module.chatChannel.get.asLong && !((channel.isInstanceOf[PrivateChannel] && author.map((u: User) => MCChatPrivate.isMinecraftChatEnabled(u.getId.asString)).orElse(false))) && !(hasCustomChat))) //Chat isn't enabled on this channel
}
val author = Option(ev.getMessage.getAuthor.orElse(null))
val hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage.getChannelId)
val prefix = DiscordPlugin.getPrefix
SMono(ev.getMessage.getChannel).filter(channel => {
def hasPrivateChat = channel.isInstanceOf[PrivateChannel] &&
author.exists((u: User) => MCChatPrivate.isMinecraftChatEnabled(u.getId.asString))
foo(channel)
}).filter((channel: MessageChannel) => {
def foo(channel: MessageChannel) = {
timings.printElapsed("Filter 2")
return !((channel.isInstanceOf[PrivateChannel] //Only in private chat && ev.getMessage.getContent.length < "/mcchat<>".length && ev.getMessage.getContent.replace(prefix + "", "").equalsIgnoreCase("mcchat")))//Either mcchat or /mcchat
//Allow disabling the chat if needed
}
def hasPublicChat = ev.getMessage.getChannelId.asLong == module.chatChannel.get.asLong
foo(channel)
}).filterWhen((channel: MessageChannel) => CommandListener.runCommand(ev.getMessage, DiscordPlugin.plugin.commandChannel.get, true)).filter //Allow running commands in chat channels
((channel: MessageChannel) => {
def foo(channel: MessageChannel) = {
timings.printElapsed("Filter 1")
val chatEnabled = hasPublicChat || hasPrivateChat || hasCustomChat
chatEnabled
}).filter(channel => {
timings.printElapsed("Filter 2")
!(channel.isInstanceOf[PrivateChannel] //Only in private chat
&& ev.getMessage.getContent.length < "/mcchat<>".length
&& ev.getMessage.getContent.replace(prefix + "", "").equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat
//Allow disabling the chat if needed
}).filterWhen(_ =>
CommandListener.runCommand(ev.getMessage, DiscordPlugin.plugin.commandChannel.get, mentionedonly = true)) //Allow running commands in chat channels
.filter(channel => {
MCChatUtils.resetLastMessage(channel)
recevents.add(ev)
timings.printElapsed("Message event added")
@ -275,22 +276,15 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
return true
}
recrun = () => {
def foo() = { //Don't return in a while loop next time
recthread = Thread.currentThread
processDiscordToMC()
if (DiscordPlugin.plugin.isEnabled && !(stop)) {
rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Continue message processing
}
recthread = Thread.currentThread
processDiscordToMC()
if (DiscordPlugin.plugin.isEnabled && !(stop)) {
rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Continue message processing
}
foo()
}
rectask = Bukkit.getScheduler.runTaskAsynchronously(DiscordPlugin.plugin, recrun) //Start message processing
return true
}
foo(channel)
}).map((b: MessageChannel) => false).defaultIfEmpty(true)
}).map(_ => false).defaultIfEmpty(true)
}
private def processDiscordToMC(): Unit = {
@ -307,7 +301,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
val dsender: DiscordSenderBase = MCChatUtils.getSender(event.getMessage.getChannelId, sender)
val user: DiscordPlayer = dsender.getChromaUser
for (u <- event.getMessage.getUserMentions.toIterable) { //TODO: Role mentions
for (u <- SFlux(event.getMessage.getUserMentions).toIterable()) { //TODO: Role mentions
dmessage = dmessage.replace(u.getMention, "@" + u.getUsername) // TODO: IG Formatting
val m: Optional[Member] = u.asMember(DiscordPlugin.mainServer.getId).onErrorResume((t: Throwable) => Mono.empty).blockOptional
if (m.isPresent) {
@ -317,7 +311,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
}
}
for (ch <- event.getGuild.flux.flatMap(_.getChannels).toIterable) {
for (ch <- SFlux(event.getGuild.flux).flatMap(_.getChannels).toIterable()) {
dmessage = dmessage.replace(ch.getMention, "#" + ch.getName)
}
dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE) //Converts emoji to text- TODO: Add option to disable (resource pack?)
@ -388,7 +382,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
}
react = true
}
return react
react
}
private def handleIngameCommand(event: MessageCreateEvent, dmessage: String, dsender: DiscordSenderBase, user: DiscordPlayer, clmd: MCChatCustom.CustomLMD, isPrivate: Boolean): Boolean = {

View file

@ -1,6 +1,7 @@
package buttondevteam.discordplugin.mcchat
import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.mcchat.MCChatUtils.LastMsgData
import buttondevteam.discordplugin.{DiscordConnectedPlayer, DiscordPlayer, DiscordPlugin, DiscordSenderBase}
import buttondevteam.lib.player.TBMCPlayer
import discord4j.common.util.Snowflake
@ -8,52 +9,53 @@ import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel}
import org.bukkit.Bukkit
import java.util
import scala.collection.mutable.ListBuffer
object MCChatPrivate {
/**
* Used for messages in PMs (mcchat).
*/
private[mcchat] val lastmsgPerUser = new util.ArrayList[MCChatUtils.LastMsgData]
private[mcchat] var lastmsgPerUser: ListBuffer[LastMsgData] = ListBuffer()
def privateMCChat(channel: MessageChannel, start: Boolean, user: User, dp: DiscordPlayer): Unit = {
MCChatUtils.ConnectedSenders synchronized
val mcp = dp.getAs(classOf[TBMCPlayer])
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID)
val op = Bukkit.getOfflinePlayer(mcp.getUUID)
val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule])
if (start) {
val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID, op.getName, mcm)
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender)
MCChatUtils.LoggedInPlayers.put(mcp.getUUID, sender)
if (p == null) { // Player is offline - If the player is online, that takes precedence
MCChatUtils.callLoginEvents(sender)
MCChatUtils.ConnectedSenders synchronized {
val mcp = dp.getAs(classOf[TBMCPlayer])
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID)
val op = Bukkit.getOfflinePlayer(mcp.getUUID)
val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule])
if (start) {
val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUUID, op.getName, mcm)
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender)
MCChatUtils.LoggedInPlayers.put(mcp.getUUID, sender)
if (p == null) { // Player is offline - If the player is online, that takes precedence
MCChatUtils.callLoginEvents(sender)
}
}
}
else {
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId, user)
assert(sender != null)
Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => {
def foo(): Unit = {
if ((p == null || p.isInstanceOf[DiscordSenderBase]) // Player is offline - If the player is online, that takes precedence
&& sender.isLoggedIn) { //Don't call the quit event if login failed
MCChatUtils.callLogoutEvent(sender, false) //The next line has to run *after* this one, so can't use the needsSync parameter
else {
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId, user)
assert(sender != null)
Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => {
def foo(): Unit = {
if ((p == null || p.isInstanceOf[DiscordSenderBase]) // Player is offline - If the player is online, that takes precedence
&& sender.isLoggedIn) { //Don't call the quit event if login failed
MCChatUtils.callLogoutEvent(sender, false) //The next line has to run *after* this one, so can't use the needsSync parameter
}
MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId)
sender.setLoggedIn(false)
}
MCChatUtils.LoggedInPlayers.remove(sender.getUniqueId)
sender.setLoggedIn(false)
foo()
}
foo()
)
}
)
// ---- PermissionsEx warning is normal on logout ----
}
// ---- PermissionsEx warning is normal on logout ----
if (!start) MCChatUtils.lastmsgfromd.remove(channel.getId.asLong)
if (start) lastmsgPerUser += new MCChatUtils.LastMsgData(channel, user) // Doesn't support group DMs
else lastmsgPerUser.filterInPlace(_.channel.getId.asLong != channel.getId.asLong) //Remove
}
if (!start) MCChatUtils.lastmsgfromd.remove(channel.getId.asLong)
if (start) lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
else lastmsgPerUser.removeIf((lmd: MCChatUtils.LastMsgData) => lmd.channel.getId.asLong == channel.getId.asLong)
}
def isMinecraftChatEnabled(dp: DiscordPlayer): Boolean = isMinecraftChatEnabled(dp.getDiscordID)
@ -64,11 +66,12 @@ object MCChatPrivate {
}
def logoutAll(): Unit = {
MCChatUtils.ConnectedSenders synchronized
for (entry <- MCChatUtils.ConnectedSenders.entrySet) {
for (valueEntry <- entry.getValue.entrySet) {
if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out
MCChatUtils.callLogoutEvent(valueEntry.getValue, !Bukkit.isPrimaryThread)
MCChatUtils.ConnectedSenders synchronized {
for (entry <- asScala(MCChatUtils.ConnectedSenders.entrySet)) {
for (valueEntry <- entry.getValue.entrySet) {
if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out
MCChatUtils.callLogoutEvent(valueEntry.getValue, !Bukkit.isPrimaryThread)
}
}
}
}

View file

@ -30,15 +30,16 @@ import java.util.function.Supplier
import java.util.logging.Level
import java.util.stream.{Collectors, Stream}
import javax.annotation.Nullable
import scala.jdk.javaapi.CollectionConverters.asScala
object MCChatUtils {
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
val UnconnectedSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordSender]]
val ConnectedSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordConnectedPlayer]]
val OnlineSenders = new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordPlayerSender]]
val LoggedInPlayers = new ConcurrentHashMap[UUID, DiscordConnectedPlayer]
val UnconnectedSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordSender]])
val ConnectedSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordConnectedPlayer]])
val OnlineSenders = asScala(new ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, DiscordPlayerSender]])
val LoggedInPlayers = asScala(new ConcurrentHashMap[UUID, DiscordConnectedPlayer])
@Nullable private[mcchat] var lastmsgdata: MCChatUtils.LastMsgData = null
private[mcchat] val lastmsgfromd = new LongObjectHashMap[Message] // Last message sent by a Discord user, used for clearing checkmarks
private var module: MinecraftChatModule = null
@ -361,10 +362,15 @@ object MCChatUtils {
private[mcchat] def callEventSync(event: Event) = Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => callEventExcludingSome(event))
class LastMsgData(val channel: MessageChannel, val user: User) {
var message: String = null
var message: Message = null
var time = 0L
var content: String = null
var mcchannel: component.channel.Channel = null
protected def this(channel: MessageChannel, user: User, mcchannel: component.channel.Channel) = {
this(channel, user)
this.mcchannel = mcchannel
}
}
}

View file

@ -1,66 +0,0 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.lib.TBMCCoreAPI;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import javax.annotation.Nullable;
@RequiredArgsConstructor
public class VCMDWrapper {
@Getter //Needed to mock the player
@Nullable
private final Object listener;
/**
* This constructor will only send raw vanilla messages to the sender in plain text.
*
* @param player The Discord sender player (the wrapper)
*/
public static <T extends DiscordSenderBase & IMCPlayer<T>> Object createListener(T player, MinecraftChatModule module) {
return createListener(player, null, module);
}
/**
* This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player.
*
* @param player The Discord sender player (the wrapper)
* @param bukkitplayer The Bukkit player to send the raw message to
* @param module The Minecraft chat module
*/
public static <T extends DiscordSenderBase & IMCPlayer<T>> Object createListener(T player, Player bukkitplayer, MinecraftChatModule module) {
try {
Object ret;
String mcpackage = Bukkit.getServer().getClass().getPackage().getName();
if (mcpackage.contains("1_12"))
ret = new VanillaCommandListener<>(player, bukkitplayer);
else if (mcpackage.contains("1_14"))
ret = new VanillaCommandListener14<>(player, bukkitplayer);
else if (mcpackage.contains("1_15") || mcpackage.contains("1_16"))
ret = VanillaCommandListener15.create(player, bukkitplayer); //bukkitplayer may be null but that's fine
else
ret = null;
if (ret == null)
compatWarning(module);
return ret;
} catch (NoClassDefFoundError | Exception e) {
compatWarning(module);
TBMCCoreAPI.SendException("Failed to create vanilla command listener", e, module);
return null;
}
}
private static void compatWarning(MinecraftChatModule module) {
module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message.");
}
static boolean compatResponse(DiscordSenderBase dsender) {
dsender.sendMessage("Vanilla commands are not supported on this Minecraft version.");
return true;
}
}

View file

@ -0,0 +1,54 @@
package buttondevteam.discordplugin.playerfaker
import buttondevteam.discordplugin.{DiscordSenderBase, IMCPlayer}
import buttondevteam.discordplugin.mcchat.MinecraftChatModule
import buttondevteam.lib.TBMCCoreAPI
import org.bukkit.Bukkit
import org.bukkit.entity.Player
object VCMDWrapper {
/**
* This constructor will only send raw vanilla messages to the sender in plain text.
*
* @param player The Discord sender player (the wrapper)
*/
def createListener[T <: DiscordSenderBase with IMCPlayer[T]](player: T, module: MinecraftChatModule): AnyRef =
createListener(player, null, module)
/**
* This constructor will send both raw vanilla messages to the sender in plain text and forward the raw message to the provided player.
*
* @param player The Discord sender player (the wrapper)
* @param bukkitplayer The Bukkit player to send the raw message to
* @param module The Minecraft chat module
*/
def createListener[T <: DiscordSenderBase with IMCPlayer[T]](player: T, bukkitplayer: Player, module: MinecraftChatModule): AnyRef = try {
var ret: AnyRef = null
val mcpackage = Bukkit.getServer.getClass.getPackage.getName
if (mcpackage.contains("1_12")) ret = new VanillaCommandListener[T](player, bukkitplayer)
else if (mcpackage.contains("1_14")) ret = new VanillaCommandListener14[T](player, bukkitplayer)
else if (mcpackage.contains("1_15") || mcpackage.contains("1_16")) ret = VanillaCommandListener15.create(player, bukkitplayer) //bukkitplayer may be null but that's fine
else ret = null
if (ret == null) compatWarning(module)
ret
} catch {
case e@(_: NoClassDefFoundError | _: Exception) =>
compatWarning(module)
TBMCCoreAPI.SendException("Failed to create vanilla command listener", e, module)
null
}
private def compatWarning(module: MinecraftChatModule): Unit =
module.logWarn("Vanilla commands won't be available from Discord due to a compatibility error. Disable vanilla command support to remove this message.")
private[playerfaker] def compatResponse(dsender: DiscordSenderBase) = {
dsender.sendMessage("Vanilla commands are not supported on this Minecraft version.")
true
}
}
class VCMDWrapper(private val listener: AnyRef) {
@javax.annotation.Nullable def getListener: AnyRef = listener
//Needed to mock the player @Nullable
}