diff --git a/build.gradle.kts b/build.gradle.kts index feadab68..cdc86f6d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ plugins { allprojects { group = "org.sayandev" - version = "1.10.5.20" + version = "1.10.5.20-pubsub2" description = "A modular Kotlin framework for Minecraft: JE" plugins.apply("maven-publish") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ffaffc3..b73ed88d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,12 +11,12 @@ cloud = "2.0.0" cloud-platform = "2.0.0-beta.10" #cloud-minecraft = "2.0.2-SNAPSHOT" adventure = "4.23.0" -sayanventure = "1.0.2" mysql-connector = "8.4.0" sqlite-jdbc = "3.46.0.0" h2 = "2.2.224" mariadb = "3.3.3" jedis = "5.0.0" +java-websocket = "1.6.0" reflections = "0.10.2" hikari = "5.1.0" guava = "31.1-jre" @@ -28,9 +28,10 @@ libby = "2.0.0-SNAPSHOT" mccoroutines = "2.20.0" kotlinx-coroutines = "1.10.1" caffeine = "3.2.0" +kaml = "0.85.0" # minecraft -adventure-platform = "4.3.4" +adventure-platform = "4.4.1" # bukkit paper = "1.21-R0.1-SNAPSHOT" @@ -65,12 +66,12 @@ cloud-kotlin-extensions = { group = "org.incendo", name = "cloud-kotlin-extensio adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" } adventure-text-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" } adventure-text-serializer-gson = { group = "net.kyori", name = "adventure-text-serializer-gson", version.ref = "adventure" } -sayanventure-api = { group = "org.sayandev.sayanventure", name = "sayanventure", version.ref = "sayanventure" } mysql-connector = { group = "com.mysql", name = "mysql-connector-j", version.ref = "mysql-connector" } sqlite-jdbc = { group = "org.xerial", name = "sqlite-jdbc", version.ref = "sqlite-jdbc" } h2 = { group = "com.h2database", name = "h2", version.ref = "h2" } mariadb = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version.ref = "mariadb" } jedis = { group = "redis.clients", name = "jedis", version.ref = "jedis" } +java-websocket = { group = "org.java-websocket", name = "Java-WebSocket", version.ref = "java-websocket" } reflections = { group = "org.reflections", name = "reflections", version.ref = "reflections" } hikari = { group = "com.zaxxer", name = "HikariCP", version.ref = "hikari" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } @@ -83,6 +84,7 @@ exposed-kotlin-datetime = { group = "org.jetbrains.exposed", name = "exposed-kot libby = { group = "com.alessiodp.libby", name = "libby-core", version.ref = "libby" } kotlinx-coroutines = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core-jvm", version.ref = "kotlinx-coroutines" } caffeine = { group = "com.github.ben-manes.caffeine", name = "caffeine", version.ref = "caffeine" } +kaml = { group = "com.charleskorn.kaml", name = "kaml", version.ref = "kaml" } # bukkit paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" } @@ -93,7 +95,6 @@ cloud-minecraft-extras = { group = "org.incendo", name = "cloud-minecraft-extras inventoryframework = { group = "com.github.stefvanschie.inventoryframework", name = "IF", version.ref = "inventoryframework" } xseries = { group = "com.github.cryptomorin", name = "XSeries", version.ref = "xseries" } adventure-platform-bukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventure-platform" } -sayanventure-platform-bukkit = { group = "org.sayandev.sayanventure", name = "sayanventure-bukkit", version.ref = "sayanventure" } libby-bukkit = { group = "com.alessiodp.libby", name = "libby-bukkit", version.ref = "libby" } libby-paper = { group = "com.alessiodp.libby", name = "libby-paper", version.ref = "libby" } pathetic-pathfinder-bukkit = { group = "com.github.patheloper.pathetic", name = "pathetic-mapping", version.ref = "pathetic-pathfinder-bukkit" } @@ -117,7 +118,7 @@ cloud-velocity = { group = "org.incendo", name = "cloud-velocity", version.ref = # proxy - bungeecord bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" } -sayanventure-platform-bungeecord = { group = "org.sayandev.sayanventure", name = "sayanventure-bungeecord", version.ref = "sayanventure" } +adventure-platform-bungeecord = { group = "net.kyori", name = "adventure-platform-bungeecord", version.ref = "adventure-platform" } libby-bungee = { group = "com.alessiodp.libby", name = "libby-bungee", version.ref = "libby" } mccoroutines-bungeecord-api = { group = "com.github.shynixn.mccoroutine", name = "mccoroutine-bungeecord-api", version.ref = "mccoroutines" } mccoroutines-bungeecord-core = { group = "com.github.shynixn.mccoroutine", name = "mccoroutine-bungeecord-core", version.ref = "mccoroutines" } @@ -149,18 +150,21 @@ implementation-core = [ "checker-qual", "javassist", "annotations", + "adventure-api", + "adventure-text-minimessage", + "adventure-text-serializer-gson", # "slf4j-api", "error_prone_annotations", "geantyref", # transitives - end - "kotlin", +# "kotlin", + "kaml", "snakeyaml", "configurate-yaml", "configurate-extra-kotlin", "cloud-core", "cloud-kotlin-extensions", # "cloud-kotlin-coroutines", - "sayanventure-api", "reflections", "kotlin-reflect", "hikari", @@ -175,6 +179,7 @@ implementation-core = [ # "h2", "mariadb", "jedis", + "java-websocket", # "caffeine" ] implementation-bukkit = [ @@ -182,7 +187,7 @@ implementation-bukkit = [ "cloud-minecraft-extras", "inventoryframework", "xseries", - "sayanventure-platform-bukkit", + "adventure-platform-bukkit", "pathetic-pathfinder-bukkit", "mccoroutines-bukkit-api", "mccoroutines-bukkit-core", @@ -201,7 +206,7 @@ implementation-proxy-velocity = [ "cloud-velocity" ] implementation-proxy-bungeecord = [ - "sayanventure-platform-bungeecord", + "adventure-platform-bungeecord", "mccoroutines-bungeecord-api", "mccoroutines-bungeecord-core", ] \ No newline at end of file diff --git a/stickynote-bukkit/build.gradle.kts b/stickynote-bukkit/build.gradle.kts index 9af03dc8..31f5ef6a 100644 --- a/stickynote-bukkit/build.gradle.kts +++ b/stickynote-bukkit/build.gradle.kts @@ -1,7 +1,6 @@ dependencies { api(libs.libby.bukkit) api(libs.libby.paper) - api(libs.sayanventure.platform.bukkit) api(libs.cloud.paper) api(libs.cloud.minecraft.extras) api(libs.inventoryframework) @@ -11,6 +10,7 @@ dependencies { api(libs.mccoroutines.bukkit.core) api(libs.mccoroutines.folia.api) api(libs.mccoroutines.folia.core) + api(libs.adventure.platform.bukkit) compileOnlyApi(libs.authlib) compileOnlyApi(libs.placeholderapi) diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Component_.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Component_.kt index a65b8933..42beb982 100644 --- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Component_.kt +++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Component_.kt @@ -1,7 +1,7 @@ package org.sayandev.stickynote.bukkit.extension -import org.sayandev.sayanventure.adventure.platform.bukkit.MinecraftComponentSerializer -import org.sayandev.sayanventure.adventure.text.Component +import net.kyori.adventure.platform.bukkit.MinecraftComponentSerializer +import net.kyori.adventure.text.Component fun Component.toNmsComponent(): Any { return MinecraftComponentSerializer.get().serialize(this) diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Player_.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Player_.kt index 7e6b5471..74b0d870 100644 --- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Player_.kt +++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/extension/Player_.kt @@ -2,18 +2,26 @@ package org.sayandev.stickynote.bukkit.extension import org.bukkit.command.CommandSender import org.bukkit.entity.Player -import org.sayandev.sayanventure.adventure.text.Component -import org.sayandev.sayanventure.adventure.text.minimessage.tag.resolver.TagResolver +import net.kyori.adventure.text.Component +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver import org.sayandev.stickynote.bukkit.utils.AdventureUtils fun CommandSender.sendComponent(message: String, vararg placeholder: TagResolver) { AdventureUtils.sendComponent(this, message, *placeholder) } +fun CommandSender.sendComponent(message: Component, vararg placeholder: TagResolver) { + AdventureUtils.sendComponent(this, message, *placeholder) +} + fun Player.sendComponentActionbar(content: String, vararg placeholder: TagResolver) { AdventureUtils.sendComponentActionbar(this, content, *placeholder) } +fun Player.sendComponentActionbar(content: Component, vararg placeholder: TagResolver) { + AdventureUtils.sendComponentActionbar(this, content, *placeholder) +} + fun CommandSender.openBook(title: Component, author: Component, vararg pages: Component) { AdventureUtils.openBook(this, title, author, *pages) } \ No newline at end of file diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/publisher/PluginMessagePublisher.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/publisher/PluginMessagePublisher.kt index bc66c5af..c6d2310e 100644 --- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/publisher/PluginMessagePublisher.kt +++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/publisher/PluginMessagePublisher.kt @@ -2,51 +2,57 @@ package org.sayandev.stickynote.bukkit.messaging.publisher import kotlinx.coroutines.CompletableDeferred import org.bukkit.entity.Player -import org.sayandev.stickynote.bukkit.StickyNote import org.sayandev.stickynote.bukkit.messaging.subscriber.PluginMessageSubscribeListener +import org.sayandev.stickynote.bukkit.onlinePlayers import org.sayandev.stickynote.bukkit.plugin -import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper -import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asJson -import org.sayandev.stickynote.core.messaging.publisher.Publisher - -abstract class PluginMessagePublisher
( - namespace: String, - name: String, - val payloadClass: Class
,
- val resultClass: Class,
+import org.sayandev.stickynote.bukkit.warn
+import org.sayandev.stickynote.core.messaging.MessageMeta
+import org.sayandev.stickynote.core.messaging.PayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asJson
+import org.sayandev.stickynote.core.messaging.Publisher
+import org.sayandev.stickynote.core.messaging.SimpleConnectionMeta
+
+abstract class PluginMessagePublisher
( + messageMeta: MessageMeta
, + connectionMeta: SimpleConnectionMeta, val withSubscriber: Boolean -): Publisher
(
- StickyNote.logger,
- namespace,
- name
+): Publisher ? = null
+ var subscriberListener: PluginMessageSubscribeListener ? = null
- init {
- register(this)
+ override fun register() {
+ super.register()
registerChannel()
}
+ override fun unregister() {
+ super.unregister()
+ unregisterChannel()
+ }
+
private fun registerChannel() {
- plugin.server.messenger.registerOutgoingPluginChannel(plugin, this.id())
+ plugin.server.messenger.registerOutgoingPluginChannel(plugin, messageMeta.id())
if (withSubscriber) {
- subscriberListener = PluginMessageSubscribeListener(namespace, name, payloadClass, resultClass, this)
+ subscriberListener = PluginMessageSubscribeListener(messageMeta, this)
}
}
private fun unregisterChannel() {
- plugin.server.messenger.unregisterOutgoingPluginChannel(plugin, this.id())
+ plugin.server.messenger.unregisterOutgoingPluginChannel(plugin, messageMeta.id())
if (withSubscriber) {
- plugin.server.messenger.unregisterIncomingPluginChannel(plugin, this.id())
+ plugin.server.messenger.unregisterIncomingPluginChannel(plugin, messageMeta.id())
}
}
- suspend fun publish(player: Player, payloadWrapper: PayloadWrapper ): CompletableDeferred ): CompletableDeferred (
- namespace: String,
- name: String,
- payloadClass: Class ,
- resultClass: Class (
- namespace,
- name,
- payloadClass,
- resultClass,
- true
+import org.sayandev.stickynote.core.messaging.MessageMeta
+import org.sayandev.stickynote.core.messaging.SimpleConnectionMeta
+
+abstract class ProxyPluginMessagePublisher (
+ messageMeta: MessageMeta ,
+ connectionMeta: SimpleConnectionMeta,
+): PluginMessagePublisher (
+ messageMeta,
+ connectionMeta,
+ false
) {
- override fun handle(payload: P): S? {
+ override fun handle(payload: P): R? {
return null
}
}
\ No newline at end of file
diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/subscriber/PluginMessageSubscribeListener.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/subscriber/PluginMessageSubscribeListener.kt
index 7fb99022..53cb83e2 100644
--- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/subscriber/PluginMessageSubscribeListener.kt
+++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/messaging/subscriber/PluginMessageSubscribeListener.kt
@@ -4,55 +4,49 @@ import org.bukkit.entity.Player
import org.bukkit.plugin.messaging.PluginMessageListener
import org.sayandev.stickynote.bukkit.messaging.publisher.PluginMessagePublisher
import org.sayandev.stickynote.bukkit.plugin
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asJson
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asPayloadWrapper
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.typedPayload
-import org.sayandev.stickynote.core.messaging.publisher.Publisher.Companion.HANDLER_LIST
+import org.sayandev.stickynote.bukkit.warn
+import org.sayandev.stickynote.core.messaging.MessageMeta
+import org.sayandev.stickynote.core.messaging.PayloadBehaviour
+import org.sayandev.stickynote.core.messaging.PayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asJson
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asPayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.typedPayload
+import org.sayandev.stickynote.core.messaging.Publisher.Companion.HANDLER_LIST
-class PluginMessageSubscribeListener (
- val namespace: String,
- val name: String,
- val payloadClass: Class ,
- val resultClass: Class ?
+class PluginMessageSubscribeListener (
+ val messageMeta: MessageMeta ,
+ val publisher: PluginMessagePublisher ?
): PluginMessageListener {
-
- fun id(): String {
- return "$namespace:$name"
- }
-
init {
- plugin.server.messenger.registerIncomingPluginChannel(plugin, this.id(), this)
+ plugin.server.messenger.registerIncomingPluginChannel(plugin, messageMeta.id(), this)
}
override fun onPluginMessageReceived(channel: String, player: Player, data: ByteArray) {
- val result = String(data).asPayloadWrapper ()
- val payloadResult = publisher.handle(wrappedPayload.typedPayload(payloadClass)) ?: return
- player.sendPluginMessage(plugin, channel, PayloadWrapper(wrappedPayload.uniqueId, payloadResult, PayloadWrapper.State.RESPOND, wrappedPayload.source).asJson().toByteArray())
+ val payloadResult = publisher.handle(wrappedPayload.typedPayload(messageMeta.payloadType)) ?: return
+ player.sendPluginMessage(plugin, channel, PayloadWrapper(wrappedPayload.uniqueId, payloadResult, PayloadBehaviour.RESPONSE, wrappedPayload.source).asJson().toByteArray())
} else {
- throw IllegalStateException("tried to handle a payload with state ${result.state}, but it doesn't have a publisher")
+ throw IllegalStateException("tried to handle a payload with state ${result.behaviour}, but it doesn't have a publisher")
}
}
- PayloadWrapper.State.RESPOND -> {
- for (publisher in HANDLER_LIST.filterIsInstance ()?.typedPayload(payloadClass)?.let { publisher.handle(it) }
- this.complete(result.typedPayload(resultClass))
+ this.complete(result.typedPayload(messageMeta.resultType))
publisher.payloads.remove(result.uniqueId)
- } /*?: throw IllegalStateException("No payload found for uniqueId ${result.uniqueId}")*/
+ } ?: throw IllegalStateException("No payload found for uniqueId ${result.uniqueId}")
}
}
}
else -> {
- throw IllegalStateException("a result payload has been received with ${result.state} state, but it doesn't belong here. (payload: ${result})")
+ throw IllegalStateException("a result payload has been received with ${result.behaviour} state, but it doesn't belong here. (payload: ${result})")
}
}
}
-}
\ No newline at end of file
+}
diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/AdventureUtils.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/AdventureUtils.kt
index 171e4cca..e2ba6bc0 100644
--- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/AdventureUtils.kt
+++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/AdventureUtils.kt
@@ -4,17 +4,16 @@ import net.md_5.bungee.api.chat.BaseComponent
import org.bukkit.ChatColor
import org.bukkit.command.CommandSender
import org.bukkit.entity.Player
-import org.sayandev.sayanventure.adventure.audience.Audience
-import org.sayandev.sayanventure.adventure.inventory.Book
-import org.sayandev.sayanventure.adventure.platform.bukkit.BukkitAudiences
-import org.sayandev.sayanventure.adventure.text.Component
-import org.sayandev.sayanventure.adventure.text.format.TextDecoration
-import org.sayandev.sayanventure.adventure.text.minimessage.MiniMessage
-import org.sayandev.sayanventure.adventure.text.minimessage.tag.resolver.TagResolver
-import org.sayandev.sayanventure.adventure.text.serializer.bungeecord.BungeeComponentSerializer
-import org.sayandev.sayanventure.adventure.text.serializer.gson.GsonComponentSerializer
-import org.sayandev.sayanventure.adventure.text.serializer.legacy.LegacyComponentSerializer
-import org.sayandev.stickynote.bukkit.StickyNote
+import net.kyori.adventure.audience.Audience
+import net.kyori.adventure.inventory.Book
+import net.kyori.adventure.platform.bukkit.BukkitAudiences
+import net.kyori.adventure.text.Component
+import net.kyori.adventure.text.format.TextDecoration
+import net.kyori.adventure.text.minimessage.MiniMessage
+import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer
import org.sayandev.stickynote.bukkit.hook.PlaceholderAPIHook
import org.sayandev.stickynote.bukkit.plugin
@@ -49,7 +48,16 @@ object AdventureUtils {
if (!ServerVersion.isAtLeast(21, 6)) {
senderAudience(sender).sendMessage(PlaceholderAPIHook.injectPlaceholders(sender as? Player, message).component(*placeholder))
} else {
- sender.sendMessage(PlaceholderAPIHook.injectPlaceholders(sender as? Player, message).component(*placeholder).adventureComponent())
+ sender.sendMessage(PlaceholderAPIHook.injectPlaceholders(sender as? Player, message).component(*placeholder))
+ }
+ }
+
+ @JvmStatic
+ fun sendComponent(sender: CommandSender, message: Component, vararg placeholder: TagResolver) {
+ if (!ServerVersion.isAtLeast(21, 6)) {
+ senderAudience(sender).sendMessage(message)
+ } else {
+ sender.sendMessage(message)
}
}
@@ -58,7 +66,7 @@ object AdventureUtils {
if (!ServerVersion.isAtLeast(21, 6)) {
senderAudience(sender).sendMessage(message)
} else {
- sender.sendMessage(message.adventureComponent())
+ sender.sendMessage(message)
}
}
@@ -67,7 +75,16 @@ object AdventureUtils {
if (!ServerVersion.isAtLeast(21, 6)) {
senderAudience(player).sendActionBar(PlaceholderAPIHook.injectPlaceholders(player, content).component(*placeholder))
} else {
- player.sendActionBar(PlaceholderAPIHook.injectPlaceholders(player, content).component(*placeholder).adventureComponent())
+ player.sendActionBar(PlaceholderAPIHook.injectPlaceholders(player, content).component(*placeholder))
+ }
+ }
+
+ @JvmStatic
+ fun sendComponentActionbar(player: Player, content: Component, vararg placeholder: TagResolver) {
+ if (!ServerVersion.isAtLeast(21, 6)) {
+ senderAudience(player).sendActionBar(content)
+ } else {
+ player.sendActionBar(content)
}
}
@@ -76,7 +93,7 @@ object AdventureUtils {
if (!ServerVersion.isAtLeast(21, 6)) {
senderAudience(sender).sendActionBar(content)
} else {
- sender.sendActionBar(content.adventureComponent())
+ sender.sendActionBar(content)
}
}
@@ -95,10 +112,10 @@ object AdventureUtils {
))
} else {
sender.openBook(
- net.kyori.adventure.inventory.Book.book(
- title.adventureComponent(),
- author.adventureComponent(),
- *pages.map { it.adventureComponent() }.toTypedArray()
+ Book.book(
+ title,
+ author,
+ *pages
)
)
}
@@ -110,42 +127,19 @@ object AdventureUtils {
return if (options.removeStartingItalic) Component.empty().decoration(TextDecoration.ITALIC, false).append(component) else component
}
- @JvmStatic
- fun toAdventureComponent(content: String, vararg placeholder: net.kyori.adventure.text.minimessage.tag.resolver.TagResolver): net.kyori.adventure.text.Component {
- val component = net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(content, *placeholder)
- return if (options.removeStartingItalic) net.kyori.adventure.text.Component.empty().decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false).append(component) else component
- }
-
@JvmStatic
fun toComponent(player: Player?, content: String, vararg placeholder: TagResolver): Component {
return miniMessage.deserialize(PlaceholderAPIHook.injectPlaceholders(player, content), *placeholder)
}
- @JvmStatic
- fun toAdventureComponent(player: Player?, content: String, vararg placeholder: net.kyori.adventure.text.minimessage.tag.resolver.TagResolver): net.kyori.adventure.text.Component {
- return net.kyori.adventure.text.minimessage.MiniMessage.miniMessage().deserialize(PlaceholderAPIHook.injectPlaceholders(player, content), *placeholder)
- }
-
fun String.component(vararg placeholder: TagResolver): Component {
return toComponent(this, *placeholder)
}
- fun Component.adventureComponent(): net.kyori.adventure.text.Component {
- return net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson().deserialize(GsonComponentSerializer.gson().serialize(this))
- }
-
- fun String.adventureComponent(vararg placeholder: net.kyori.adventure.text.minimessage.tag.resolver.TagResolver): net.kyori.adventure.text.Component {
- return toAdventureComponent(this, *placeholder)
- }
-
fun String.component(player: Player?, vararg placeholder: TagResolver): Component {
return toComponent(player, this, *placeholder)
}
- fun String.adventureComponent(player: Player?, vararg placeholder: net.kyori.adventure.text.minimessage.tag.resolver.TagResolver): net.kyori.adventure.text.Component {
- return toAdventureComponent(player, this, *placeholder)
- }
-
fun Component.legacyString(): String {
return legacyAmpersandSerializer.serialize(this)
}
diff --git a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/ItemUtils.kt b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/ItemUtils.kt
index 71ec5126..f4aed6a1 100644
--- a/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/ItemUtils.kt
+++ b/stickynote-bukkit/src/main/kotlin/org/sayandev/stickynote/bukkit/utils/ItemUtils.kt
@@ -4,7 +4,6 @@ import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import org.bukkit.inventory.Inventory
import org.bukkit.inventory.ItemStack
import org.sayandev.stickynote.bukkit.StickyNote
-import org.sayandev.stickynote.bukkit.utils.AdventureUtils.adventureComponent
import org.sayandev.stickynote.bukkit.utils.AdventureUtils.bungeeComponent
import org.sayandev.stickynote.bukkit.utils.AdventureUtils.component
import org.sayandev.stickynote.bukkit.utils.AdventureUtils.legacyColored
@@ -25,12 +24,12 @@ object ItemUtils {
fun ItemStack.withDisplayName(displayName: String, placeholders: Map (
+ val namespace: String,
+ val name: String,
+ val payloadType: KClass ,
+ val resultType: KClass {
+ return MessageMeta(namespace, name, P::class, S::class)
+ }
+ }
+}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadBehaviour.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadBehaviour.kt
new file mode 100644
index 00000000..4045cd02
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadBehaviour.kt
@@ -0,0 +1,18 @@
+package org.sayandev.stickynote.core.messaging
+
+enum class PayloadBehaviour {
+ /**
+ * Forwards to all available subscribers.
+ * Subscribers will handle the message and return a response if provided.
+ * The result will only be available if one of the subscribers returns a response.
+ * If none of the subscribers return a response, the payload will be ignored.
+ */
+ FORWARD,
+
+ FORWARD_PROXY,
+
+ /**
+ * The payload will be sent to the source of the message.
+ */
+ RESPONSE
+}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/PayloadWrapper.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadWrapper.kt
similarity index 80%
rename from stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/PayloadWrapper.kt
rename to stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadWrapper.kt
index a69ea10a..a3486b95 100644
--- a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/PayloadWrapper.kt
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/PayloadWrapper.kt
@@ -1,26 +1,21 @@
-package org.sayandev.stickynote.core.messaging.publisher
+package org.sayandev.stickynote.core.messaging
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonIOException
import com.google.gson.TypeAdapter
import java.util.*
+import kotlin.reflect.KClass
data class PayloadWrapper (
val uniqueId: UUID = UUID.randomUUID(),
val payload: P,
- val state: State = State.FORWARD,
+ val behaviour: PayloadBehaviour = PayloadBehaviour.FORWARD,
var source: String? = null,
val target: String? = null,
val excludeSource: Boolean = false,
) {
- constructor(payload: P, state: State = State.FORWARD): this(UUID.randomUUID(), payload, state)
-
- enum class State {
- PROXY,
- FORWARD,
- RESPOND,
- }
+ constructor(payload: P, behaviour: PayloadBehaviour = PayloadBehaviour.FORWARD): this(UUID.randomUUID(), payload, behaviour)
companion object {
private var gson = Gson()
@@ -82,12 +77,19 @@ data class PayloadWrapper (
} catch (_: Exception) { null }
}
- fun PayloadWrapper<*>.typedPayload(payloadClass: Class ): P {
+ fun PayloadWrapper<*>.typedPayload(payloadType: KClass ): P {
return try {
- gson.fromJson(gson.toJson(this.payload), payloadClass)
+ gson.fromJson(gson.toJson(this.payload), payloadType.java)
} catch (e: JsonIOException) {
throw IllegalStateException("Could not convert payload to $this", e)
}
}
+
+ fun P.toPayloadWrapper(behaviour: PayloadBehaviour = PayloadBehaviour.FORWARD): PayloadWrapper {
+ return PayloadWrapper(
+ payload = this,
+ behaviour = behaviour
+ )
+ }
}
}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/Publisher.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/Publisher.kt
new file mode 100644
index 00000000..85d5a379
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/Publisher.kt
@@ -0,0 +1,61 @@
+package org.sayandev.stickynote.core.messaging
+
+import kotlinx.coroutines.CompletableDeferred
+import java.util.UUID
+import java.util.logging.Logger
+
+abstract class Publisher ,
+ val connectionMeta: M,
+ val logger: Logger,
+) {
+
+ val payloads: MutableMap ): CompletableDeferred (
+ val messageMeta: MessageMeta ,
+) {
+
+ abstract suspend fun onSubscribe(payload: P): Deferred register(subscriber: Subscriber ) {
+ HANDLER_LIST.add(subscriber)
+ }
+
+ fun unregister(subscriber: Subscriber ) {
+ HANDLER_LIST.remove(subscriber)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/Publisher.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/Publisher.kt
deleted file mode 100644
index 3444fcd3..00000000
--- a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/Publisher.kt
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.sayandev.stickynote.core.messaging.publisher
-
-import kotlinx.coroutines.CompletableDeferred
-import java.util.*
-import java.util.logging.Logger
-
-abstract class Publisher (
- val logger: Logger,
- val namespace: String,
- val name: String,
-) {
-
- val payloads: MutableMap ): CompletableDeferred register(publisher: Publisher ) {
- require(!HANDLER_LIST.contains(publisher)) { "Publisher with id ${publisher.id()} is already registered" }
- HANDLER_LIST.add(publisher)
- }
-
- fun unregister(publisher: Publisher ) {
- HANDLER_LIST.remove(publisher)
- }
- }
-
-}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/RedisPublisher.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/RedisPublisher.kt
deleted file mode 100644
index aec47a08..00000000
--- a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/publisher/RedisPublisher.kt
+++ /dev/null
@@ -1,112 +0,0 @@
-package org.sayandev.stickynote.core.messaging.publisher
-
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.delay
-import org.sayandev.stickynote.core.coroutine.dispatcher.AsyncDispatcher
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asJson
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asPayloadWrapper
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.typedPayload
-import org.sayandev.stickynote.core.messaging.redis.RedisConnectionManager
-import org.sayandev.stickynote.core.utils.CoroutineUtils.launch
-import redis.clients.jedis.JedisPool
-import java.util.*
-import java.util.logging.Level
-import java.util.logging.Logger
-
-abstract class RedisPublisher (
- val dispatcher: CoroutineDispatcher,
- val redis: JedisPool,
- namespace: String,
- name: String,
- val payloadClass: Class ,
- val resultClass: Class (
- logger,
- namespace,
- name
-) {
- val channel = "$namespace:$name"
-
- init {
- RedisConnectionManager.registerNamespace(namespace, redis, dispatcher)
- }
-
- fun handleForwardMessage(payloadWrapper: PayloadWrapper ): CompletableDeferred (
+ messageMeta: MessageMeta ,
+ connectionMeta: RedisConnectionMeta,
+ logger: Logger
+) : Publisher ()
+ if (wrappedPayload.excludeSource && isSource(wrappedPayload.uniqueId)) return
+ val payloadResult = handle(wrappedPayload.typedPayload(messageMeta.payloadType)) ?: return
+ connectionMeta.pool.resource.publish(
+ channel.toByteArray(),
+ PayloadWrapper(
+ wrappedPayload.uniqueId,
+ payloadResult,
+ PayloadBehaviour.RESPONSE,
+ wrappedPayload.source,
+ wrappedPayload.target,
+ wrappedPayload.excludeSource
+ ).asJson().toByteArray()
+ )
+ }
+ PayloadBehaviour.RESPONSE -> {
+ for (publisher in HANDLER_LIST.filterIsInstance ): CompletableDeferred (
+ messageMeta: MessageMeta ,
+ val connectionMeta: RedisConnectionMeta,
+ val logger: Logger
+) : Subscriber (messageMeta) {
+
+ init {
+ val pubSub = object : JedisPubSub() {
+ override fun onMessage(channel: String, message: String) {
+ if (channel != messageMeta.id()) return
+ val payloadWrapper = message.asPayloadWrapper ()
+
+ when (payloadWrapper.behaviour) {
+ PayloadBehaviour.FORWARD_PROXY -> {
+ if (!isVelocity) return
+
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ val result =
+ (HANDLER_LIST.find { it.messageMeta.id() == messageMeta.id() } as Subscriber )
+ .onSubscribe(payloadWrapper.typedPayload(messageMeta.payloadType))
+ result.await()
+ publishWithTimeout(
+ PayloadWrapper(
+ payloadWrapper.uniqueId,
+ result.getCompleted(),
+ PayloadBehaviour.RESPONSE,
+ payloadWrapper.source
+ )
+ )
+ }
+ }
+ PayloadBehaviour.FORWARD -> {
+ if (payloadWrapper.excludeSource && isSource(payloadWrapper.uniqueId)) return
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ val result =
+ (HANDLER_LIST.find { it.messageMeta.id() == messageMeta.id() } as? Subscriber )?.onSubscribe(
+ payloadWrapper.typedPayload(messageMeta.payloadType)
+ )
+ if (payloadWrapper.target == "PROCESSED") return@launch;
+ publishWithTimeout(
+ PayloadWrapper(
+ payloadWrapper.uniqueId,
+ result?.getCompleted() ?: payloadWrapper.payload,
+ if (result != null) PayloadBehaviour.RESPONSE else payloadWrapper.behaviour,
+ payloadWrapper.source,
+ "PROCESSED"
+ )
+ )
+ }
+ }
+ else -> { }
+ }
+ }
+ }
+ Thread({
+ while (true) {
+ try {
+ connectionMeta.pool.resource.use { jedis ->
+ jedis.subscribe(pubSub, messageMeta.id())
+ }
+ } catch (e: JedisConnectionException) {
+ logger.severe("Redis connection lost for channel ${messageMeta.id()}: ${e.message}. Retrying in ${connectionMeta.timeoutMillis} milliseconds...")
+ Thread.sleep(connectionMeta.timeoutMillis)
+ } catch (e: Exception) {
+ logger.severe("Unexpected error in RedisSubscriber: ${e.message}")
+ Thread.sleep(connectionMeta.timeoutMillis)
+ }
+ }
+ }, "redis-sub-sub-thread-${messageMeta.id()}-${UUID.randomUUID().toString().split("-").first()}").start()
+ }
+
+ private suspend fun publishWithTimeout(payload: PayloadWrapper<*>) {
+ val deferred = CompletableDeferred (
- val dispatcher: CoroutineDispatcher,
- val redis: JedisPool,
- namespace: String,
- name: String,
- val payloadClass: Class ,
- val logger: Logger
-) : Subscriber (namespace, name) {
-
- val channel = "$namespace:$name"
-
- init {
- RedisConnectionManager.registerNamespace(namespace, redis, dispatcher)
- }
-
- fun handleForwardMessage(payloadWrapper: PayloadWrapper (
- val namespace: String,
- val name: String,
-) {
-
- abstract suspend fun onSubscribe(payload: P): CompletableDeferred register(subscriber: Subscriber ) {
- HANDLER_LIST.add(subscriber)
- }
-
- fun unregister(subscriber: Subscriber ) {
- HANDLER_LIST.remove(subscriber)
- }
- }
-
-}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/MessageWebSocketServer.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/MessageWebSocketServer.kt
new file mode 100644
index 00000000..3baf3649
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/MessageWebSocketServer.kt
@@ -0,0 +1,59 @@
+package org.sayandev.stickynote.core.messaging.websocket
+
+import org.java_websocket.WebSocket
+import org.java_websocket.client.WebSocketClient
+import org.java_websocket.handshake.ClientHandshake
+import org.java_websocket.handshake.ServerHandshake
+import org.java_websocket.server.WebSocketServer
+import java.net.InetSocketAddress
+import java.net.URI
+
+class MessageWebSocketServer(
+ uri: URI
+) : WebSocketServer(InetSocketAddress(uri.host, uri.port)) {
+ override fun onOpen(connection: WebSocket, handshake: ClientHandshake) {
+ }
+
+ override fun onClose(connection: WebSocket, code: Int, reason: String, remote: Boolean) {
+ // TODO: throw exception or something?
+ }
+
+ override fun onMessage(connection: WebSocket, message: String) {
+ broadcast(message)
+ }
+
+ override fun onError(connection: WebSocket, e: Exception) {
+ }
+
+ override fun onStart() {
+ }
+
+ companion object {
+ fun isWebSocketAvailable(uri: URI): Boolean {
+ var available = false
+ val client = object : WebSocketClient(uri) {
+ override fun onOpen(handshakedata: ServerHandshake?) {
+ available = true
+ close()
+ }
+ override fun onMessage(message: String?) {}
+ override fun onClose(code: Int, reason: String?, remote: Boolean) {}
+ override fun onError(ex: Exception?) {}
+ }
+ try {
+ client.connectBlocking()
+ } catch (e: Exception) {
+ return false
+ }
+ return available
+ }
+
+ fun getWebSocketServer(uri: URI): MessageWebSocketServer? {
+ return if (isWebSocketAvailable(uri)) {
+ MessageWebSocketServer(uri)
+ } else {
+ null
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketConnectionMeta.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketConnectionMeta.kt
new file mode 100644
index 00000000..5b88a78c
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketConnectionMeta.kt
@@ -0,0 +1,11 @@
+package org.sayandev.stickynote.core.messaging.websocket
+
+import kotlinx.coroutines.CoroutineDispatcher
+import org.sayandev.stickynote.core.messaging.ConnectionMeta
+import java.net.URI
+
+data class WebSocketConnectionMeta(
+ val uri: URI,
+ val dispatcher: CoroutineDispatcher,
+ override val timeoutMillis: Long = 5000L
+): ConnectionMeta
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketPublisher.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketPublisher.kt
new file mode 100644
index 00000000..4392df21
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/websocket/WebSocketPublisher.kt
@@ -0,0 +1,295 @@
+package org.sayandev.stickynote.core.messaging.websocket
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeoutOrNull
+import org.java_websocket.WebSocket
+import org.java_websocket.client.WebSocketClient
+import org.java_websocket.handshake.ClientHandshake
+import org.java_websocket.handshake.ServerHandshake
+import org.java_websocket.server.WebSocketServer
+import org.sayandev.stickynote.core.messaging.MessageMeta
+import org.sayandev.stickynote.core.messaging.PayloadBehaviour
+import org.sayandev.stickynote.core.messaging.PayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asJson
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asPayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.typedPayload
+import org.sayandev.stickynote.core.messaging.Publisher
+import org.sayandev.stickynote.core.utils.CoroutineUtils.launch
+import java.net.InetSocketAddress
+import java.net.ServerSocket
+import java.net.Socket
+import java.net.URI
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.TimeUnit
+import java.util.logging.Logger
+import kotlin.math.min
+
+open class WebSocketPublisher (
+ messageMeta: MessageMeta ,
+ connectionMeta: WebSocketConnectionMeta,
+ logger: Logger,
+) : Publisher ()
+ if (wrappedPayload.excludeSource && isSource(wrappedPayload.uniqueId)) return
+ val payloadResult = handle(wrappedPayload.typedPayload(messageMeta.payloadType)) ?: return
+ client?.takeIf { it.isOpen }?.send(
+ PayloadWrapper(
+ wrappedPayload.uniqueId,
+ payloadResult,
+ PayloadBehaviour.RESPONSE,
+ wrappedPayload.source,
+ wrappedPayload.target,
+ wrappedPayload.excludeSource
+ ).asJson()
+ )
+ }
+ PayloadBehaviour.RESPONSE -> {
+ for (publisher in HANDLER_LIST.filterIsInstance ): CompletableDeferred (
+ messageMeta: MessageMeta ,
+ val connectionMeta: WebSocketConnectionMeta,
+ val logger: Logger
+) : Subscriber (messageMeta) {
+
+ private val client: WebSocketClient
+
+ init {
+ client = object : WebSocketClient(connectionMeta.uri) {
+ override fun onOpen(handshakedata: ServerHandshake?) {
+ }
+
+ override fun onMessage(message: String) {
+ handleMessage(message)
+ }
+
+ override fun onClose(code: Int, reason: String?, remote: Boolean) {
+ logger.info("WebSocket connection closed: $reason")
+ }
+
+ override fun onError(ex: Exception?) {
+ logger.severe("WebSocket error: ${ex?.message}")
+ }
+ }
+ client.connect()
+ }
+
+ private fun handleMessage(message: String) {
+ val payloadWrapper = message.asPayloadWrapper ()
+
+ when (payloadWrapper.behaviour) {
+ PayloadBehaviour.FORWARD_PROXY -> {
+ if (!isVelocity) return
+
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ val result = (HANDLER_LIST.find { it.messageMeta.id() == messageMeta.id() } as Subscriber )
+ .onSubscribe(payloadWrapper.typedPayload(messageMeta.payloadType))
+ result.await()
+ publishWithTimeout(
+ PayloadWrapper(
+ payloadWrapper.uniqueId,
+ result.getCompleted(),
+ PayloadBehaviour.RESPONSE,
+ payloadWrapper.source
+ )
+ )
+ }
+ }
+ PayloadBehaviour.FORWARD -> {
+ if (payloadWrapper.excludeSource && isSource(payloadWrapper.uniqueId)) return
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ val result =
+ (HANDLER_LIST.find { it.messageMeta.id() == messageMeta.id() } as? Subscriber )?.onSubscribe(
+ payloadWrapper.typedPayload(messageMeta.payloadType)
+ )
+ if (payloadWrapper.target == "PROCESSED") return@launch
+ publishWithTimeout(
+ PayloadWrapper(
+ payloadWrapper.uniqueId,
+ result?.getCompleted() ?: payloadWrapper.payload,
+ if (result != null) PayloadBehaviour.RESPONSE else payloadWrapper.behaviour,
+ payloadWrapper.source,
+ "PROCESSED"
+ )
+ )
+ }
+ }
+ else -> { }
+ }
+ }
+
+ private suspend fun publishWithTimeout(payload: PayloadWrapper<*>) {
+ val deferred = CompletableDeferred (
- namespace: String,
- name: String,
- val resultClass: Class (
- StickyNote.javaLogger,
- namespace,
- name
+abstract class PluginMessagePublisher (
+ messageMeta: MessageMeta ,
+ connectionMeta: SimpleConnectionMeta,
+ logger: Logger
+): Publisher (
registerListener(this)
}
- suspend fun publish(server: RegisteredServer, payloadWrapper: PayloadWrapper ): CompletableDeferred ): CompletableDeferred ): CompletableDeferred ): CompletableDeferred (
- namespace: String,
- name: String,
- val payloadClass: Class
+class PluginMessageSubscribeListener (
+ val messageMeta: MessageMeta
) {
- val channelIdentifier = MinecraftChannelIdentifier.create(namespace, name)
+ val channelIdentifier = MinecraftChannelIdentifier.create(messageMeta.namespace, messageMeta.name)
init {
server.channelRegistrar.register(channelIdentifier)
@@ -36,47 +36,47 @@ class PluginMessageSubscribeListener (
val rawMessage = String(event.data, StandardCharsets.UTF_8)
val payloadWrapper = rawMessage.asPayloadWrapper ()
- when (payloadWrapper.state) {
- PayloadWrapper.State.PROXY -> {
+ when (payloadWrapper.behaviour) {
+ PayloadBehaviour.FORWARD_PROXY -> {
launch {
- val result = (Subscriber.HANDLER_LIST.find { it.namespace == channelIdentifier.namespace && it.name == channelIdentifier.name } as Subscriber ).onSubscribe(payloadWrapper.typedPayload(payloadClass))
- result?.invokeOnCompletion {
- (event.source as ServerConnection).sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), PayloadWrapper.State.RESPOND, payloadWrapper.source).asJson().toByteArray())
+ val result = (Subscriber.HANDLER_LIST.find { it.messageMeta.namespace == channelIdentifier.namespace && it.messageMeta.name == channelIdentifier.name } as Subscriber ).onSubscribe(payloadWrapper.typedPayload(messageMeta.payloadType))
+ result.invokeOnCompletion {
+ (event.source as ServerConnection).sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), PayloadBehaviour.RESPONSE, payloadWrapper.source).asJson().toByteArray())
}
}
}
- PayloadWrapper.State.FORWARD -> {
+ PayloadBehaviour.FORWARD -> {
launch {
val targetServerName = payloadWrapper.target
val targetServer = StickyNote.server.allServers.find { it.serverInfo.name == targetServerName }
- val result = (Subscriber.HANDLER_LIST.find { it.namespace == channelIdentifier.namespace && it.name == channelIdentifier.name } as? Subscriber )?.onSubscribe(payloadWrapper.typedPayload(payloadClass))
+ val result = (Subscriber.HANDLER_LIST.find { it.messageMeta.namespace == channelIdentifier.namespace && it.messageMeta.name == channelIdentifier.name } as? Subscriber )?.onSubscribe(payloadWrapper.typedPayload(messageMeta.payloadType))
result?.invokeOnCompletion {
if (targetServerName != null) {
- targetServer?.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), payloadWrapper.state, source.serverInfo.name, targetServerName).asJson().toByteArray()) ?: warn("target server name was specified as ${targetServerName} but there's not server with this id. will ignore pluginmessage request")
+ targetServer?.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), payloadWrapper.behaviour, source.serverInfo.name, targetServerName).asJson().toByteArray()) ?: warn("target server name was specified as ${targetServerName} but there's not server with this id. will ignore pluginmessage request")
} else {
for (server in StickyNote.server.allServers) {
if (payloadWrapper.excludeSource && source.serverInfo.name == server.serverInfo.name) continue
- server.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), payloadWrapper.state, source.serverInfo.name, server.serverInfo.name).asJson().toByteArray())
+ server.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, result.getCompleted(), payloadWrapper.behaviour, source.serverInfo.name, server.serverInfo.name).asJson().toByteArray())
}
}
} ?: let {
if (targetServerName != null) {
- targetServer?.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.state, source.serverInfo.name, targetServerName).asJson().toByteArray()) ?: warn("target server name was specified as ${targetServerName} but there's not server with this id. will ignore pluginmessage request")
+ targetServer?.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.behaviour, source.serverInfo.name, targetServerName).asJson().toByteArray()) ?: warn("target server name was specified as ${targetServerName} but there's not server with this id. will ignore pluginmessage request")
} else {
for (server in StickyNote.server.allServers) {
if (payloadWrapper.excludeSource && source.serverInfo.name == server.serverInfo.name) continue
- server.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.state, source.serverInfo.name, server.serverInfo.name).asJson().toByteArray())
+ server.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.behaviour, source.serverInfo.name, server.serverInfo.name).asJson().toByteArray())
}
}
}
}
}
- PayloadWrapper.State.RESPOND -> {
+ PayloadBehaviour.RESPONSE -> {
val payloadSource = payloadWrapper.source ?: throw IllegalArgumentException("Can't respond a message if the source is null (payload: ${payloadWrapper})")
val sourceServer = StickyNote.server.allServers.find { it.serverInfo.name.lowercase() == payloadSource.lowercase() } ?: throw IllegalArgumentException("Can't find the source server on proxy (payload: ${payloadWrapper})")
- sourceServer.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.state, payloadSource, payloadWrapper.target).asJson().toByteArray())
+ sourceServer.sendPluginMessage(channelIdentifier, PayloadWrapper(payloadWrapper.uniqueId, payloadWrapper.payload, payloadWrapper.behaviour, payloadSource, payloadWrapper.target).asJson().toByteArray())
}
}
}
-}
\ No newline at end of file
+}
diff --git a/stickynote-proxy/stickynote-proxy-velocity/src/main/kotlin/org/sayandev/stickynote/velocity/messaging/ProxySubscriber.kt b/stickynote-proxy/stickynote-proxy-velocity/src/main/kotlin/org/sayandev/stickynote/velocity/messaging/ProxySubscriber.kt
deleted file mode 100644
index ab554790..00000000
--- a/stickynote-proxy/stickynote-proxy-velocity/src/main/kotlin/org/sayandev/stickynote/velocity/messaging/ProxySubscriber.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.sayandev.stickynote.velocity.messaging
-
-import org.sayandev.stickynote.core.messaging.subscriber.Subscriber
-
-abstract class ProxySubscriber (namespace: String, name: String, payloadClass: Class ) : Subscriber (namespace, name) {
- init {
- PluginMessageSubscribeListener (namespace, name, payloadClass)
- }
-}
\ No newline at end of file
{
- player.sendPluginMessage(plugin, this.id(), payloadWrapper.asJson().toByteArray())
+ suspend fun publish(player: Player, payloadWrapper: PayloadWrapper,
-): PluginMessagePublisher,
- val publisher: PluginMessagePublisher()
- when (result.state) {
- PayloadWrapper.State.FORWARD -> {
+ val result = String(data).asPayloadWrapper,
+) {
+ fun id(): String {
+ return "$namespace:$name"
+ }
+
+ companion object {
+ inline fun {
- val deferred = CompletableDeferred()
- payloads[payloadWrapper.uniqueId] = deferred
-
- return deferred
- }
-
- fun register() {
- register(this)
- }
-
- fun unregister() {
- unregister(this)
- }
-
- companion object {
- val HANDLER_LIST = mutableListOf,
- logger: Logger
-) : Publisher {
- val result = super.publish(payloadWrapper)
-
- launch(dispatcher) {
- val localJedis = redis.resource
- try {
- val published = localJedis.publish(channel.toByteArray(), payloadWrapper.asJson().toByteArray())
- if (published <= 0) {
- payloads.remove(payloadWrapper.uniqueId)
- return@launch
- }
- } finally {
- localJedis.close()
- }
-
- delay(TIMEOUT_SECONDS * 1000L)
- if (result.isActive) {
- result.completeExceptionally(IllegalStateException("No response received in $TIMEOUT_SECONDS seconds"))
- payloads.remove(payloadWrapper.uniqueId)
- }
- }
-
- return result
- }
-
- abstract fun handle(payload: P): S?
-
- fun isSource(uniqueId: UUID): Boolean {
- return HANDLER_LIST.asSequence()
- .flatMap { publisher -> publisher.payloads.keys.asSequence() }.contains(uniqueId)
- }
-
- companion object {
- const val TIMEOUT_SECONDS = 5L
- }
-}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisConnectionManager.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisConnectionManager.kt
deleted file mode 100644
index b7947380..00000000
--- a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisConnectionManager.kt
+++ /dev/null
@@ -1,165 +0,0 @@
-package org.sayandev.stickynote.core.messaging.redis
-
-import kotlinx.coroutines.CoroutineDispatcher
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper
-import org.sayandev.stickynote.core.messaging.publisher.PayloadWrapper.Companion.asPayloadWrapper
-import org.sayandev.stickynote.core.messaging.publisher.Publisher
-import org.sayandev.stickynote.core.messaging.publisher.RedisPublisher
-import org.sayandev.stickynote.core.messaging.subscriber.RedisSubscriber
-import org.sayandev.stickynote.core.messaging.subscriber.Subscriber
-import redis.clients.jedis.JedisPool
-import redis.clients.jedis.JedisPubSub
-import redis.clients.jedis.exceptions.JedisException
-import java.util.*
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.atomic.AtomicBoolean
-import java.util.logging.Level
-import java.util.logging.Logger
-
-object RedisConnectionManager {
- private val namespaceSubscriptions = ConcurrentHashMap()
+ when (result.behaviour) {
+ PayloadBehaviour.FORWARD -> {
+ val wrappedPayload = message.asPayloadWrapper {
+ if (!HANDLER_LIST.contains(this)) {
+ throw IllegalStateException("Publisher with id ${messageMeta.id()} is not registered")
+ }
+
+ val result = super.publish(payload)
+
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ delay(connectionMeta.timeoutMillis)
+ if (result.isActive) {
+ result.completeExceptionally(IllegalStateException("Sent payload has not been responded in ${connectionMeta.timeoutMillis}ms. Payload: $payload (id: ${messageMeta.id()})"))
+ }
+ payloads.remove(payload.uniqueId)
+ }
+
+ CoroutineUtils.launch(connectionMeta.dispatcher) {
+ connectionMeta.pool.resource.publish(messageMeta.id().toByteArray(), payload.asJson().toByteArray())
+ }
+
+ return result
+ }
+}
\ No newline at end of file
diff --git a/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisSubscriber.kt b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisSubscriber.kt
new file mode 100644
index 00000000..fc31e017
--- /dev/null
+++ b/stickynote-core/src/main/kotlin/org/sayandev/stickynote/core/messaging/redis/RedisSubscriber.kt
@@ -0,0 +1,106 @@
+package org.sayandev.stickynote.core.messaging.redis
+
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import org.sayandev.stickynote.core.messaging.MessageMeta
+import org.sayandev.stickynote.core.messaging.PayloadBehaviour
+import org.sayandev.stickynote.core.messaging.PayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asJson
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.asPayloadWrapper
+import org.sayandev.stickynote.core.messaging.PayloadWrapper.Companion.typedPayload
+import org.sayandev.stickynote.core.messaging.Publisher
+import org.sayandev.stickynote.core.messaging.Subscriber
+import org.sayandev.stickynote.core.utils.CoroutineUtils
+import org.sayandev.stickynote.core.utils.CoroutineUtils.awaitWithTimeout
+import redis.clients.jedis.JedisPubSub
+import redis.clients.jedis.exceptions.JedisConnectionException
+import java.util.*
+import java.util.logging.Logger
+
+@OptIn(ExperimentalCoroutinesApi::class)
+abstract class RedisSubscriber?
-
- fun id(): String {
- return "$namespace:$name"
- }
-
- fun register() {
- register(this)
- }
-
- fun unregister() {
- unregister(this)
- }
-
- companion object {
- val HANDLER_LIST = mutableListOf
-): Publisher {
+ suspend fun publish(server: RegisteredServer, payloadWrapper: PayloadWrapper {
+ suspend fun publish(player: Player, payloadWrapper: PayloadWrapper()
- when (result.state) {
- PayloadWrapper.State.RESPOND -> {
- for (publisher in HANDLER_LIST.filterIsInstance