Skip to content

Commit

Permalink
Merge pull request #32 from IntellectualSites/bugfix/20_fix_automatic…
Browse files Browse the repository at this point in the history
…_poll

Fix automatic poll of clipboards
  • Loading branch information
TheMeinerLP authored Jan 30, 2024
2 parents 060ab22 + bbda18a commit a0d51f3
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 79 deletions.
8 changes: 8 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import de.chojo.Repo
import io.papermc.hangarpublishplugin.model.Platforms
import net.minecrell.pluginyml.bukkit.BukkitPluginDescription
import net.minecrell.pluginyml.paper.PaperPluginDescription
import xyz.jpenilla.runpaper.task.RunServer

plugins {
kotlin("jvm") version "1.9.22"
Expand Down Expand Up @@ -47,6 +48,13 @@ tasks {
runServer {
minecraftVersion("1.20.4")
}
register<RunServer>("runServer2") {
group = "run paper"
minecraftVersion("1.20.4")
runDirectory.set(File("run-2"))
pluginJars(*rootProject.getTasksByName("shadowJar", false).map { (it as Jar).archiveFile }
.toTypedArray())
}
shadowJar {
relocate("org.bstats", "net.onelitefeather.clipboardconnect.org.bstats")
}
Expand Down
8 changes: 8 additions & 0 deletions docker/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: '3'
services:
keydb:
image: eqalpha/keydb
container_name: clipboard-connect-keydb
restart: unless-stopped
ports:
- "127.0.0.1:6379:6379"
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import net.onelitefeather.clipboardconnect.commands.LoadCommand
import net.onelitefeather.clipboardconnect.commands.SaveCommand
import net.onelitefeather.clipboardconnect.commands.SetupCommand
import net.onelitefeather.clipboardconnect.conversation.ConversationContext
import net.onelitefeather.clipboardconnect.listener.PlayerJoinListener
import net.onelitefeather.clipboardconnect.listener.PlayerQuitListener
import net.onelitefeather.clipboardconnect.listener.SetupListener
import net.onelitefeather.clipboardconnect.services.SetupService
Expand Down Expand Up @@ -138,6 +139,7 @@ class ClipboardConnect : JavaPlugin() {
val redisFile = Path(dataFolder.toString(), "redis.yml")
if (Files.exists(redisFile)) {
server.pluginManager.registerEvents(injector.instance(PlayerQuitListener::class.java), this)
server.pluginManager.registerEvents(injector.instance(PlayerJoinListener::class.java), this)
injector.instance(AnnotationParser::class.java).parse(injector.instance(SaveCommand::class.java))
injector.instance(AnnotationParser::class.java).parse(injector.instance(LoadCommand::class.java))
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package net.onelitefeather.clipboardconnect.listener

import com.sk89q.worldedit.bukkit.BukkitAdapter
import jakarta.inject.Inject
import jakarta.inject.Named
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.minimessage.MiniMessage
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder
import net.onelitefeather.clipboardconnect.ClipboardConnect
import net.onelitefeather.clipboardconnect.services.SyncService
import org.bukkit.event.EventHandler
import org.bukkit.event.EventPriority
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent

/**
* Listener class that handles the 'PlayerJoinEvent' when a player joins the server.
*
* @property syncService The SyncService instance used for syncing player data.
* @property plugin The ClipboardConnect plugin instance.
*/
class PlayerJoinListener
@Inject constructor(
private val syncService: SyncService,
private val plugin: ClipboardConnect,
@Named("prefix") private val prefix: Component,
) : Listener {

/**
* Handles the 'PlayerQuitEvent' when a player quits the server.
*
* @param event The PlayerQuitEvent object.
*/
@EventHandler(priority = EventPriority.LOWEST)
fun playerJoin(event: PlayerJoinEvent) {
val player = event.player
plugin.componentLogger.debug(
MiniMessage.miniMessage()
.deserialize("<player> is logging in", Placeholder.component("player", player.name()))
)
if (!player.hasPermission("clipboardconnect.service.load")) return
plugin.componentLogger.debug(
MiniMessage.miniMessage()
.deserialize("<player> permission check was successful", Placeholder.component("player", player.name()))
)
val worldEditPlayer = BukkitAdapter.adapt(player)
plugin.componentLogger.debug(
MiniMessage.miniMessage()
.deserialize("Try to pull clipboard for <player>", Placeholder.component("player", player.name()))
)
if (syncService.syncPull(worldEditPlayer)) {
plugin.componentLogger.debug(
MiniMessage.miniMessage().deserialize(
"<green>Clipboard from <actor> was successful written into actor",
Placeholder.unparsed("actor", worldEditPlayer.name)
)
)
player.sendMessage(
MiniMessage.miniMessage().deserialize(
"<prefix><green>Clipboard <green>was successfully transfered to this server",
Placeholder.component("prefix", prefix)
)
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.github.luben.zstd.ZstdOutputStream
import com.sk89q.worldedit.EmptyClipboardException
import com.sk89q.worldedit.WorldEdit
import com.sk89q.worldedit.bukkit.BukkitAdapter
import com.sk89q.worldedit.bukkit.BukkitPlayer
import com.sk89q.worldedit.extension.platform.Actor
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat
import com.sk89q.worldedit.session.ClipboardHolder
Expand Down Expand Up @@ -46,52 +47,45 @@ import kotlin.time.toJavaDuration
* @property duration The duration for which clipboards are stored in Redis.
*/
@Singleton
class SyncService @Inject constructor(private val config: FileConfiguration, private val plugin: ClipboardConnect, @Named("prefix") private val prefix: Component, @Named("fawe") private val faweSupport: Boolean) {
class SyncService @Inject constructor(
private val config: FileConfiguration,
private val plugin: ClipboardConnect,
@Named("prefix") private val prefix: Component,
@Named("fawe") private val faweSupport: Boolean
) {

private val logger = ComponentLogger.logger(javaClass)
private val redisson: RedissonClient = buildRedis()
private val topicName = "ClipboardConnect"
private val topicNameUUID = "ClipboardConnect-UUID"
private val serverName = config.getString("servername") ?: "Unknown"
private val codec = TypedJsonJacksonCodec(ClipboardMessage::class.java)
private val messageRQueue = redisson.getQueue<ClipboardMessage>(topicName,codec)
private val messageUUIDRQueue = redisson.getQueue<String>(topicNameUUID)
private val waitForUpload = redisson.getList<String>(topicName)
private val pubSub = redisson.getTopic(topicName, codec)
private val duration: Duration = loadDuration()
private val pushMarker = MarkerFactory.getMarker("Sync Push")
private val pullMarker = MarkerFactory.getMarker("Sync Pull")


init {
plugin.server.scheduler.runTaskTimerAsynchronously(plugin, this::pollUpdates, 0, 20)
messageRQueue.expire(Duration.parse("5m").toJavaDuration())
messageUUIDRQueue.expire(Duration.parse("5m").toJavaDuration())
pubSub.addListener(ClipboardMessage::class.java, this::onPollMessage)
}

private fun pollUpdates() {
try {
val message = messageRQueue.peek()
logger.debug(MiniMessage.miniMessage().deserialize("Pull message queue"))
if (message != null) {
logger.debug(MiniMessage.miniMessage().deserialize("Found message"))
val player = Bukkit.getPlayer(message.userId()) ?: return
if (messageUUIDRQueue.contains(player.uniqueId.toString())) {
return
}
logger.debug(MiniMessage.miniMessage().deserialize("Found player"))
if (!player.hasPermission("clipboardconnect.service.load")) return
logger.debug(MiniMessage.miniMessage().deserialize("Player permission check ok"))
messageUUIDRQueue.add(player.uniqueId.toString())
if (syncPull(BukkitAdapter.adapt(player))) {
logger.debug(MiniMessage.miniMessage().deserialize("Pull was successful"))
player.sendMessage(MiniMessage.miniMessage().deserialize("<prefix><green>Clipboard from <gold><server> <green>was successfully transfered to this server", Placeholder.unparsed("server", message.fromServer()), Placeholder.component("prefix",prefix)))
logger.debug(MiniMessage.miniMessage().deserialize("Remove message from queue"))
messageRQueue.remove(message)
messageUUIDRQueue.remove(player.uniqueId.toString())
}
private fun onPollMessage(channel: CharSequence, clipboardMessage: ClipboardMessage) {
if (channel == topicName) {
val message = clipboardMessage;
val player = Bukkit.getPlayer(message.userId()) ?: return
logger.debug(MiniMessage.miniMessage().deserialize("Found player"))
if (!player.hasPermission("clipboardconnect.service.load")) return
if (syncPull(BukkitAdapter.adapt(player))) {
logger.debug(MiniMessage.miniMessage().deserialize("Pull was successful"))
player.sendMessage(
MiniMessage.miniMessage().deserialize(
"<prefix><green>Clipboard from <gold><server> <green>was successfully transfered to this server",
Placeholder.unparsed("server", message.fromServer()),
Placeholder.component("prefix", prefix)
)
)
}
} catch (e: Exception) {
e.printStackTrace()
Bukkit.getPluginManager().disablePlugin(plugin)
}
}

Expand All @@ -110,7 +104,10 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
logger.error(MiniMessage.miniMessage().deserialize("<red>Failed to initialize a redis connection"), e)
plugin.server.pluginManager.disablePlugin(plugin)
} catch (e: Exception) {
logger.error(MiniMessage.miniMessage().deserialize("<red>Something went wrong while trying to initialize a redis connection"), e)
logger.error(
MiniMessage.miniMessage()
.deserialize("<red>Something went wrong while trying to initialize a redis connection"), e
)
plugin.server.pluginManager.disablePlugin(plugin)
}
}
Expand All @@ -125,11 +122,21 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
* @return True if the synchronization and save were successful, false otherwise.
*/
@Suppress("DEPRECATION")
fun syncPush(actor: Actor, automatic: Boolean = true): Boolean {
logger.debug(pushMarker, MiniMessage.miniMessage().deserialize("Open actor<player> stream from redis", Placeholder.unparsed("player", actor.name)))
fun syncPush(actor: Actor, automatic: Boolean = true): Boolean {
logger.debug(
pushMarker,
MiniMessage.miniMessage()
.deserialize("Open actor<player> stream from redis", Placeholder.unparsed("player", actor.name))
)
val stream = redisson.getBinaryStream(actor.uniqueId.toString())
if (stream.isExists) {
plugin.componentLogger.debug(pushMarker, MiniMessage.miniMessage().deserialize("Delete old actor<player> stream from redis", Placeholder.unparsed("player", actor.name)))
plugin.componentLogger.debug(
pushMarker,
MiniMessage.miniMessage().deserialize(
"Delete old actor<player> stream from redis",
Placeholder.unparsed("player", actor.name)
)
)
stream.delete()
}
try {
Expand All @@ -139,7 +146,7 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
MiniMessage.miniMessage()
.deserialize("Find actor<player> session", Placeholder.unparsed("player", actor.name))
)

waitForUpload.add(actor.uniqueId.toString())
val pushAsync = Runnable {
val clipboardHolder = session.clipboard ?: return@Runnable
logger.debug(
Expand All @@ -159,7 +166,7 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
return@Runnable
}

val format = if(faweSupport) {
val format = if (faweSupport) {
BuiltInClipboardFormat.FAST
} else {
BuiltInClipboardFormat.SPONGE_SCHEMATIC
Expand All @@ -184,10 +191,12 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
}

} catch (e: Exception) {
logger.error(MiniMessage.miniMessage().deserialize(
"<green>Something went wrong to write clipboard",
Placeholder.unparsed("actor", actor.name)
), e)
logger.error(
MiniMessage.miniMessage().deserialize(
"<green>Something went wrong to write clipboard",
Placeholder.unparsed("actor", actor.name)
), e
)
}
logger.debug(
pushMarker,
Expand All @@ -206,7 +215,8 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
Placeholder.unparsed("player", actor.name)
)
)
messageRQueue.add(ClipboardMessage(actor.uniqueId, serverName))
pubSub.publish(ClipboardMessage(actor.uniqueId, serverName))
waitForUpload.remove(actor.uniqueId.toString())
}
}
if (faweSupport) {
Expand All @@ -229,51 +239,77 @@ class SyncService @Inject constructor(private val config: FileConfiguration, pri
*/
@Suppress("Deprecation")
fun syncPull(actor: Actor): Boolean {
logger.debug(pullMarker, MiniMessage.miniMessage().deserialize("Open actor<player> stream from redis to pull", Placeholder.unparsed("player", actor.name)))
val stream = redisson.getBinaryStream(actor.uniqueId.toString())
if (stream.isExists) {
logger.debug(pullMarker,
if (waitForUpload.contains(actor.uniqueId.toString())) {
if (actor is BukkitPlayer) {
actor.player.sendMessage(
MiniMessage.miniMessage().deserialize(
"<green>Clipboard from <actor> was successful downloaded",
Placeholder.unparsed("actor", actor.name)
"<prefix><gold>The clipboard is still being transferred. Please wait until the clipboard is released.",
Placeholder.component("prefix", prefix)
)
)
logger.debug(pullMarker, MiniMessage.miniMessage().deserialize("Find actor<player> session", Placeholder.unparsed("player", actor.name)))
val session = WorldEdit.getInstance().sessionManager.get(actor)
logger.debug(pullMarker, MiniMessage.miniMessage().deserialize("Open actor<player> reader", Placeholder.unparsed("player", actor.name)))
val format = if(faweSupport) {
BuiltInClipboardFormat.FAST
} else {
BuiltInClipboardFormat.SPONGE_SCHEMATIC
}
try {
format.getReader(ZstdInputStream(stream.inputStream)).use {
logger.debug(
pullMarker,
MiniMessage.miniMessage().deserialize(
"<green>Clipboard from <actor> was successful written into a clipboard holder",
Placeholder.unparsed("actor", actor.name)
)
}
return false;
}
logger.debug(
pullMarker,
MiniMessage.miniMessage()
.deserialize("Open actor<player> stream from redis to pull", Placeholder.unparsed("player", actor.name))
)
val stream = redisson.getBinaryStream(actor.uniqueId.toString())
if (stream.isExists) {
logger.debug(
pullMarker,
MiniMessage.miniMessage().deserialize(
"<green>Clipboard from <actor> was successful downloaded",
Placeholder.unparsed("actor", actor.name)
)
)
logger.debug(
pullMarker,
MiniMessage.miniMessage()
.deserialize("Find actor<player> session", Placeholder.unparsed("player", actor.name))
)
val session = WorldEdit.getInstance().sessionManager.get(actor)
logger.debug(
pullMarker,
MiniMessage.miniMessage()
.deserialize("Open actor<player> reader", Placeholder.unparsed("player", actor.name))
)
val format = if (faweSupport) {
BuiltInClipboardFormat.FAST
} else {
BuiltInClipboardFormat.SPONGE_SCHEMATIC
}
try {
format.getReader(ZstdInputStream(stream.inputStream)).use {
logger.debug(
pullMarker,
MiniMessage.miniMessage().deserialize(
"<green>Clipboard from <actor> was successful written into a clipboard holder",
Placeholder.unparsed("actor", actor.name)
)
logger.debug(
pullMarker,
MiniMessage.miniMessage().deserialize(
"Create clipboard holder for actor<player>",
Placeholder.unparsed("player", actor.name)
)
)
logger.debug(
pullMarker,
MiniMessage.miniMessage().deserialize(
"Create clipboard holder for actor<player>",
Placeholder.unparsed("player", actor.name)
)
session.clipboard = ClipboardHolder(it.read())
}
return true
} catch (e: Exception) {
logger.error(MiniMessage.miniMessage().deserialize(
)
session.clipboard = ClipboardHolder(it.read())
}
return true
} catch (e: Exception) {
logger.error(
MiniMessage.miniMessage().deserialize(
"<green>Something went wrong to load clipboard",
Placeholder.unparsed("actor", actor.name)
), e)
}

), e
)
}
return false

}
return false
}

private fun loadDuration(): Duration {
Expand Down

0 comments on commit a0d51f3

Please sign in to comment.