diff --git a/.github/workflows/release-dev.yml b/.github/workflows/release-dev.yml
new file mode 100644
index 0000000..e96e3b9
--- /dev/null
+++ b/.github/workflows/release-dev.yml
@@ -0,0 +1,81 @@
+name: Create Release with ShadowJars
+
+on:
+ workflow_dispatch:
+
+jobs:
+ build:
+ name: Build ShadowJars and Create Release
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ ref: develop # Ensure it works from the develop branch
+
+ - name: Set up JDK 21
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'adopt'
+ java-version: '21'
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ gradle-${{ runner.os }}
+
+ - name: Make gradlew executable
+ run: chmod +x ./gradlew
+
+ - name: Build ShadowJars
+ run: ./gradlew clean build shadowJar
+
+ - name: Get Gradle Version
+ id: gradle_version
+ run: echo "GRADLE_VERSION=$(./gradlew properties -q | grep "version:" | awk '{print $2}')" >> $GITHUB_ENV
+
+ - name: Get Commit Hash
+ id: commit_hash
+ run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
+
+# - name: Publish to SimpleCloud Repository
+# run: ./gradlew publishMavenJavaPublicationToSimplecloudRepository
+# env:
+# COMMIT_HASH: ${{ env.COMMIT_HASH }}
+# SIMPLECLOUD_USERNAME: ${{ secrets.SIMPLECLOUD_USERNAME }}
+# SIMPLECLOUD_PASSWORD: ${{ secrets.SIMPLECLOUD_PASSWORD }}
+
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ with:
+ tag_name: v${{ env.GRADLE_VERSION }}-dev.${{ env.COMMIT_HASH }}
+ release_name: v${{ env.GRADLE_VERSION }}-dev.${{ env.COMMIT_HASH }}
+ draft: false
+ prerelease: true
+ commitish: develop
+ body: |
+ This release contains dev builds for all Gradle modules.
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload ShadowJars to Release
+ run: |
+ # Find JAR files in any submodule's build/libs directory
+ for jar in $(find . -type f -name "*.jar" -path "*/build/libs/*.jar" -not -path "./build/libs/*"); do
+ # Check if the filename contains a version number (e.g., a dash followed by numbers)
+ if [[ $(basename "$jar") =~ -[0-9]+\.[0-9]+ ]]; then
+ echo "Skipping $jar due to version number"
+ else
+ echo "Uploading $jar"
+ gh release upload v${{ env.GRADLE_VERSION }}-dev.${{ env.COMMIT_HASH }} "$jar"
+ fi
+ done
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index 2b8a50f..d4b7acc 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index c28d5d0..69eb25a 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -7,7 +7,7 @@
-
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 7af9308..8b4b07f 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -6,10 +6,13 @@ plugins {
alias(libs.plugins.shadow)
}
-allprojects {
+val baseVersion = "0.0.1"
+val commitHash = System.getenv("COMMIT_HASH")
+val snapshotversion = "${baseVersion}-dev.$commitHash"
+allprojects {
group = "app.simplecloud.plugin.proxy"
- version = "1.0.0"
+ version = if (commitHash != null) snapshotversion else baseVersion
repositories {
mavenCentral()
@@ -18,10 +21,12 @@ allprojects {
subprojects {
apply(plugin = "org.jetbrains.kotlin.jvm")
- apply(plugin = "com.github.johnrengelman.shadow")
+ apply(plugin = "com.gradleup.shadow")
repositories {
mavenCentral()
+ maven("https://buf.build/gen/maven")
+
maven {
name = "papermc"
url = uri("https://repo.papermc.io/repository/maven-public/")
@@ -29,19 +34,27 @@ subprojects {
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
}
+ maven {
+ name = "simplecloudRepositorySnapshots"
+ url = uri("https://repo.simplecloud.app/snapshots")
+ }
}
dependencies {
- testImplementation(rootProject.libs.kotlinTest)
- implementation(rootProject.libs.kotlinJvm)
+ testImplementation(rootProject.libs.kotlin.test)
+ implementation(rootProject.libs.kotlin.jvm)
+ implementation(rootProject.libs.kotlin.coroutines)
}
- kotlin {
- jvmToolchain(17)
+ java {
+ toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}
- tasks.withType {
- kotlinOptions.jvmTarget = "17"
+ kotlin {
+ jvmToolchain(21)
+ compilerOptions {
+ apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0)
+ }
}
tasks.named("shadowJar", ShadowJar::class) {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e5d143d..be52b1f 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,26 +1,42 @@
[versions]
-kotlin = "1.8.0"
-shadow = "8.1.1"
-velocity = "3.1.1"
-bungeecord = "1.19-R0.1-SNAPSHOT"
+kotlin = "2.0.20"
+kotlin-coroutines = "1.9.0"
+shadow = "8.3.3"
+velocity = "3.4.0-SNAPSHOT"
+bungeecord = "1.20-R0.1-SNAPSHOT"
adventure = "4.16.0"
-adventurelatform = "4.3.2"
+adventure-platform = "4.3.2"
gson = "2.10.1"
-configurateYaml = "4.0.0"
-configurateKotlin = "4.1.2"
+configurate-yaml = "4.0.0"
+configurate-kotlin = "4.1.2"
+simplecloud-event-wrapper = "0.0.1-dev.950792a"
+simplecloud-controller = "0.0.30-dev.bf5da83"
+command-cloud-core = "2.0.0"
+command-cloud-velocity = "2.0.0-beta.10"
+command-cloud-bungeecord = "2.0.0-beta.10"
[libraries]
-kotlinJvm = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
-kotlinTest = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
-velocityApi = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
-bungeecordApi = { module = "net.md-5:bungeecord-api", version.ref = "bungeecord" }
-adventureLegacySerializer = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "adventure" }
-adventureMinimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
-adventureBungeecordPlatform = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventurelatform" }
+kotlin-jvm = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
+
+command-cloud-core = { module = "org.incendo:cloud-core", version.ref = "command-cloud-core" }
+command-cloud-velocity = { module = "org.incendo:cloud-velocity", version.ref = "command-cloud-velocity" }
+command-cloud-bungeecord = { module = "org.incendo:cloud-bungee", version.ref = "command-cloud-bungeecord" }
+
+velocity = { module = "com.velocitypowered:velocity-api", version.ref = "velocity" }
+bungeecord = { module = "net.md-5:bungeecord-api", version.ref = "bungeecord" }
+adventure-legacy-serializer = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "adventure" }
+adventure-minimessage = { module = "net.kyori:adventure-text-minimessage", version.ref = "adventure" }
+adventure-bungeecord-platform = { module = "net.kyori:adventure-platform-bungeecord", version.ref = "adventure-platform" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
-configurateYaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurateYaml" }
-configurateKotlin = { module = "org.spongepowered:configurate-extra-kotlin", version.ref = "configurateKotlin" }
+configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate-yaml" }
+configurate-kotlin = { module = "org.spongepowered:configurate-extra-kotlin", version.ref = "configurate-kotlin" }
+
+simplecloud-event-wrapper-velocity = { module = "app.simplecloud.event:event-wrapper-velocity", version.ref = "simplecloud-event-wrapper" }
+simplecloud-event-wrapper-bungeecord = { module = "app.simplecloud.event:event-wrapper-bungeecord", version.ref = "simplecloud-event-wrapper" }
+simplecloud-controller = { module = "app.simplecloud.controller:controller-api", version.ref = "simplecloud-controller" }
[plugins]
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
-shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
\ No newline at end of file
+shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 30609b7..34ace61 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Thu Mar 21 21:19:37 CET 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/proxy-bungeecord/build.gradle.kts b/proxy-bungeecord/build.gradle.kts
index be86456..0ba193c 100644
--- a/proxy-bungeecord/build.gradle.kts
+++ b/proxy-bungeecord/build.gradle.kts
@@ -1,9 +1,14 @@
dependencies {
api(project(":proxy-shared"))
- compileOnly(rootProject.libs.bungeecordApi)
+ compileOnly(rootProject.libs.bungeecord)
- implementation(rootProject.libs.adventureLegacySerializer)
- implementation(rootProject.libs.adventureMinimessage)
- implementation(rootProject.libs.adventureBungeecordPlatform)
+ implementation(rootProject.libs.adventure.legacy.serializer)
+ implementation(rootProject.libs.adventure.minimessage)
+ implementation(rootProject.libs.adventure.bungeecord.platform)
+
+ compileOnly(rootProject.libs.simplecloud.event.wrapper.bungeecord)
+
+ implementation(rootProject.libs.command.cloud.core)
+ implementation(rootProject.libs.command.cloud.bungeecord)
}
\ No newline at end of file
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/BungeeCordCommandSender.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/BungeeCordCommandSender.kt
new file mode 100644
index 0000000..114a258
--- /dev/null
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/BungeeCordCommandSender.kt
@@ -0,0 +1,22 @@
+package app.simplecloud.plugin.proxy.bungeecord
+
+import app.simplecloud.plugin.proxy.shared.handler.command.CommandSender
+import net.kyori.adventure.text.Component
+import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer
+import net.md_5.bungee.api.chat.BaseComponent
+
+class BungeeCordCommandSender(private val commandSender: net.md_5.bungee.api.CommandSender
+) : CommandSender {
+
+ fun getCommandSender(): net.md_5.bungee.api.CommandSender {
+ return commandSender
+ }
+
+ override fun sendMessage(message: String) {
+ commandSender.sendMessage(message)
+ }
+}
+
+fun Component.toBaseComponent(): BaseComponent {
+ return BungeeComponentSerializer.get().serialize(this)[0]
+}
\ No newline at end of file
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/ProxyBungeeCordPlugin.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/ProxyBungeeCordPlugin.kt
index 904ac30..29abcb1 100644
--- a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/ProxyBungeeCordPlugin.kt
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/ProxyBungeeCordPlugin.kt
@@ -2,59 +2,74 @@ package app.simplecloud.plugin.proxy.bungeecord
import app.simplecloud.plugin.proxy.bungeecord.event.ConfigureTagResolversEvent
import app.simplecloud.plugin.proxy.bungeecord.handler.TabListHandler
-import app.simplecloud.plugin.proxy.bungeecord.listener.ConfigureTagResolversListener
-import app.simplecloud.plugin.proxy.bungeecord.listener.ProxyPingListener
-import app.simplecloud.plugin.proxy.bungeecord.listener.TabListListener
-import app.simplecloud.plugin.proxy.shared.config.GeneralConfig
+import app.simplecloud.plugin.proxy.bungeecord.listener.*
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
import app.simplecloud.plugin.proxy.shared.config.YamlConfig
import app.simplecloud.plugin.proxy.shared.config.placeholder.PlaceHolderConfiguration
import app.simplecloud.plugin.proxy.shared.config.tablis.TabListConfiguration
import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+import app.simplecloud.plugin.proxy.shared.handler.command.CommandSender
+import app.simplecloud.plugin.proxy.shared.handler.command.ProxyCommandHandler
import net.kyori.adventure.platform.bungeecord.BungeeAudiences
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.minimessage.MiniMessage
import net.md_5.bungee.api.connection.ProxiedPlayer
import net.md_5.bungee.api.plugin.Plugin
+import org.incendo.cloud.SenderMapper
+import org.incendo.cloud.bungee.BungeeCommandManager
+import org.incendo.cloud.execution.ExecutionCoordinator
class ProxyBungeeCordPlugin: Plugin() {
- lateinit var generalConfiguration: GeneralConfig
- lateinit var tabListConfiguration: TabListConfiguration
- lateinit var placeHolderConfiguration: PlaceHolderConfiguration
+ val proxyPlugin = ProxyPlugin(this.dataFolder.path)
+
- val config = YamlConfig(this.dataFolder.path)
val tabListHandler = TabListHandler(this)
- val motdLayoutHandler = MotdLayoutHandler(config, generalConfiguration)
+
+ private lateinit var commandManager: BungeeCommandManager
private var adventure: BungeeAudiences? = null
private val miniMessage = MiniMessage.miniMessage()
override fun onEnable() {
- val config = YamlConfig(this.dataFolder.path)
-
- this.generalConfiguration = config.load("general")!!
- config.save("general", this.generalConfiguration)
-
- this.tabListConfiguration = config.load("tablist")!!
- config.save("tablist", this.tabListConfiguration)
+ this.proxyPlugin.config.save("tablist", this.proxyPlugin.tabListConfiguration)
+ this.proxyPlugin.config.save("placeholder", this.proxyPlugin.placeHolderConfiguration)
+ this.proxyPlugin.config.save("messages", this.proxyPlugin.messagesConfiguration)
- this.placeHolderConfiguration = config.load("placeholder")!!
- config.save("placeholder", this.placeHolderConfiguration)
-
- this.motdLayoutHandler.loadMotdLayouts()
+ this.proxyPlugin.motdLayoutHandler.loadMotdLayouts()
this.adventure = BungeeAudiences.create(this);
+ this.proxy.pluginManager.registerListener(this, ProxyPingListener(this))
+ this.proxy.pluginManager.registerListener(this, ConfigureTagResolversListener(this))
+ this.proxy.pluginManager.registerListener(this, CloudListener(this))
+ this.proxy.pluginManager.registerListener(this, ServerPreConnectListener(this))
- if (this.tabListConfiguration.tabListUpdateTime > 0)
+ if (this.proxyPlugin.tabListConfiguration.tabListUpdateTime > 0)
this.tabListHandler.startTabListTask()
else
this.logger.info("Tablist update time is set to 0, tablist will not be updated automatically")
- this.proxy.pluginManager.registerListener(this, TabListListener(this))
- this.proxy.pluginManager.registerListener(this, ProxyPingListener(this))
- this.proxy.pluginManager.registerListener(this, ConfigureTagResolversListener(this))
+ val executionCoordinator = ExecutionCoordinator.simpleCoordinator()
+
+ val senderMapper = SenderMapper.create(
+ { commandSender -> BungeeCordCommandSender(commandSender) },
+ { cloudSender -> (cloudSender as BungeeCordCommandSender).getCommandSender() }
+ )
+
+ commandManager = BungeeCommandManager(
+ this,
+ executionCoordinator,
+ senderMapper
+ )
+
+ val proxyCommandHandler = ProxyCommandHandler(commandManager, this.proxyPlugin)
+ proxyCommandHandler.loadCommands()
+
+ System.getenv("SIMPLECLOUD_MAINTENANCE")?.let {
+ this.proxyPlugin.maintenance = it == "true"
+ }
}
override fun onDisable() {
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/handler/TabListHandler.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/handler/TabListHandler.kt
index 9632995..750d0ea 100644
--- a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/handler/TabListHandler.kt
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/handler/TabListHandler.kt
@@ -7,7 +7,6 @@ import net.md_5.bungee.api.connection.ProxiedPlayer
import net.md_5.bungee.api.scheduler.ScheduledTask
import java.util.concurrent.TimeUnit
-
class TabListHandler(
private val plugin: ProxyBungeeCordPlugin
) {
@@ -24,7 +23,7 @@ class TabListHandler(
this.tabListIndex.forEach { (key, value) ->
this.tabListIndex[key] = value + 1
}
- }, 1, this.plugin.tabListConfiguration.tabListUpdateTime, TimeUnit.MILLISECONDS)
+ }, 1, this.plugin.proxyPlugin.tabListConfiguration.tabListUpdateTime, TimeUnit.MILLISECONDS)
}
fun stopTabListTask() {
@@ -37,7 +36,7 @@ class TabListHandler(
}
fun updateTabListForPlayer(player: ProxiedPlayer) {
- val configuration = plugin.tabListConfiguration
+ val configuration = plugin.proxyPlugin.tabListConfiguration
val serviceName = player.server.info.name
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/CloudListener.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/CloudListener.kt
new file mode 100644
index 0000000..40448e4
--- /dev/null
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/CloudListener.kt
@@ -0,0 +1,67 @@
+package app.simplecloud.plugin.proxy.bungeecord.listener
+
+import app.simplecloud.event.bungeecord.mapping.CloudServerUpdateEvent
+import app.simplecloud.plugin.proxy.bungeecord.ProxyBungeeCordPlugin
+import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+import net.md_5.bungee.api.plugin.Listener
+import net.md_5.bungee.event.EventHandler
+import java.util.logging.Logger
+
+class CloudListener(
+ private val plugin: ProxyBungeeCordPlugin
+) : Listener {
+
+ private val logger = Logger.getLogger(CloudListener::class.java.name)
+
+ @EventHandler
+ fun test(event: CloudServerUpdateEvent) {
+
+ if (event.getTo().uniqueId != System.getenv("SIMPLECLOUD_UNIQUE_ID")) return
+
+ checkMaintenanceChance(event)
+ checkLayoutMaintenanceChance(event)
+ checkLayoutChance(event)
+ }
+
+ private fun checkMaintenanceChance(event: CloudServerUpdateEvent) {
+ val isMaintenance = event.getTo().properties["maintenance"]
+
+ if (isMaintenance == event.getFrom().properties["maintenance"]) return
+
+ val newMaintenanceState = isMaintenance == "true"
+
+ if (this.plugin.proxyPlugin.maintenance == newMaintenanceState) return
+
+ this.plugin.proxyPlugin.maintenance = newMaintenanceState
+
+ this.logger.info("Maintenance mode has been toggled to $newMaintenanceState")
+ }
+
+ private fun checkLayoutMaintenanceChance(event: CloudServerUpdateEvent) {
+ val layout = event.getTo().properties[MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY]
+
+ if (layout == event.getFrom().properties[MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY]) return
+
+ val newLayout = layout ?: MotdLayoutHandler.DEFAULT_MAINTENANCE_LAYOUT_NAME
+
+ if (MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY == newLayout) return
+
+ MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY = newLayout
+
+ this.logger.info("Layout has been changed to $newLayout")
+ }
+
+ private fun checkLayoutChance(event: CloudServerUpdateEvent) {
+ val layout = event.getTo().properties[MotdLayoutHandler.CURRENT_LAYOUT_KEY]
+
+ if (layout == event.getFrom().properties[MotdLayoutHandler.CURRENT_LAYOUT_KEY]) return
+
+ val newLayout = layout ?: MotdLayoutHandler.DEFAULT_LAYOUT_NAME
+
+ if (MotdLayoutHandler.CURRENT_LAYOUT_KEY == newLayout) return
+
+ MotdLayoutHandler.CURRENT_LAYOUT_KEY = newLayout
+
+ this.logger.info("Layout has been changed to $newLayout")
+ }
+}
\ No newline at end of file
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ConfigureTagResolversListener.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ConfigureTagResolversListener.kt
index cabdb40..b134f1e 100644
--- a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ConfigureTagResolversListener.kt
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ConfigureTagResolversListener.kt
@@ -3,9 +3,11 @@ package app.simplecloud.plugin.proxy.bungeecord.listener
import app.simplecloud.plugin.proxy.bungeecord.ProxyBungeeCordPlugin
import app.simplecloud.plugin.proxy.bungeecord.event.ConfigureTagResolversEvent
import app.simplecloud.plugin.proxy.shared.resolver.TagResolverHelper
+import kotlinx.coroutines.runBlocking
import net.md_5.bungee.api.plugin.Listener
import net.md_5.bungee.event.EventHandler
import net.md_5.bungee.event.EventPriority
+import kotlin.jvm.optionals.getOrNull
class ConfigureTagResolversListener(
private val plugin: ProxyBungeeCordPlugin
@@ -13,25 +15,28 @@ class ConfigureTagResolversListener(
@EventHandler(priority = EventPriority.LOWEST)
fun onConfigureTagResolvers(event: ConfigureTagResolversEvent) {
- val player = event.player
- val serverName = player?.server?.info?.name ?: "unknown"
-
- val ping = player?.ping?.toLong() ?: -1
- val pingColors = plugin.placeHolderConfiguration.pingColors
-
- val onlinePlayers = this.plugin.proxy.players.size
- val realMaxPlayers = this.plugin.proxy.config.listeners.sumOf { it.maxPlayers }
-
- event.withTagResolvers(
- TagResolverHelper.getDefaultTagResolvers(
- serverName,
- ping,
- pingColors,
- onlinePlayers,
- realMaxPlayers,
- this.plugin.motdLayoutHandler.getCurrentMotdLayout()
+
+ runBlocking {
+ val player = event.player
+ val serverName = player?.server?.info?.name ?: "unknown"
+
+ val ping = player?.ping ?: -1
+ val pingColors = plugin.proxyPlugin.placeHolderConfiguration.pingColors
+
+ val onlinePlayers = plugin.proxy.players.size
+ val realMaxPlayers = plugin.proxy.config.playerLimit
+
+ event.withTagResolvers(
+ TagResolverHelper.getDefaultTagResolvers(
+ serverName,
+ ping.toLong(),
+ pingColors,
+ onlinePlayers,
+ realMaxPlayers,
+ plugin.proxyPlugin.motdLayoutHandler.getCurrentMotdLayout()
+ )
)
- )
+ }
}
}
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ProxyPingListener.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ProxyPingListener.kt
index 5756358..37463eb 100644
--- a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ProxyPingListener.kt
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ProxyPingListener.kt
@@ -2,6 +2,7 @@ package app.simplecloud.plugin.proxy.bungeecord.listener
import app.simplecloud.plugin.proxy.bungeecord.ProxyBungeeCordPlugin
import app.simplecloud.plugin.proxy.shared.config.motd.MaxPlayerDisplayType
+import kotlinx.coroutines.runBlocking
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer
import net.md_5.bungee.api.Favicon
@@ -11,10 +12,12 @@ import net.md_5.bungee.api.plugin.Listener
import net.md_5.bungee.event.EventHandler
import java.awt.image.BufferedImage
import java.io.File
+import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.util.*
import javax.imageio.ImageIO
+import kotlin.jvm.optionals.getOrNull
class ProxyPingListener(
private val plugin: ProxyBungeeCordPlugin
@@ -22,62 +25,64 @@ class ProxyPingListener(
@EventHandler
fun onPing(event: ProxyPingEvent) {
- val socketAddress = event.connection.socketAddress as? InetSocketAddress
- val hostStringFromConnection = socketAddress?.address?.hostName?: ""
- val hostStringFromServer = InetAddress.getLocalHost().hostAddress
-
- if (hostStringFromConnection == hostStringFromServer) {
- return
+ runBlocking {
+ val socketAddress = event.connection.socketAddress as? InetSocketAddress
+ val hostStringFromConnection = socketAddress?.address?.hostName ?: ""
+ val hostStringFromServer = InetAddress.getLocalHost().hostAddress
+
+ if (hostStringFromConnection == hostStringFromServer) {
+ return@runBlocking
+ }
+
+ val response = event.response
+
+ val motdConfiguration = plugin.proxyPlugin.motdLayoutHandler.getCurrentMotdLayout()
+
+ val firstLine = motdConfiguration.firstLines.random()
+ val secondLine = motdConfiguration.secondLines.random()
+
+ val messageOfTheDay = plugin.deserializeToComponent("$firstLine\n$secondLine")
+
+ response.descriptionComponent = BungeeComponentSerializer.get().serialize(messageOfTheDay)[0]
+
+ val playerList = motdConfiguration.playerInfo.map { PlayerInfo(it, UUID.randomUUID()) }
+ response.players = when (motdConfiguration.maxPlayerDisplayType) {
+ null -> Players(
+ response.players.max,
+ response.players.online,
+ playerList.toTypedArray().ifEmpty { response.players.sample }
+ )
+
+ MaxPlayerDisplayType.REAL -> Players(
+ response.players.max,
+ response.players.online,
+ playerList.toTypedArray().ifEmpty { response.players.sample }
+ )
+
+ else -> Players(
+ response.players.online + motdConfiguration.dynamicPlayerRange,
+ response.players.online,
+ playerList.toTypedArray().ifEmpty { response.players.sample }
+ )
+ }
+
+ response.version = when (motdConfiguration.versionName) {
+ "" -> response.version
+ else -> Protocol(
+ motdConfiguration.versionName,
+ -1
+ )
+ }
+
+ val favicon = if (motdConfiguration.serverIcon == "") {
+ response.faviconObject
+ } else {
+ val serverIcon: BufferedImage = ImageIO.read(File(motdConfiguration.serverIcon))
+ Favicon.create(serverIcon)
+ }
+
+ response.setFavicon(favicon)
}
-
- val response = event.response
-
- val motdConfiguration = this.plugin.motdLayoutHandler.getCurrentMotdLayout()
-
- val firstLine = motdConfiguration.firstLines.random()
- val secondLine = motdConfiguration.secondLines.random()
-
- val messageOfTheDay: Component = this.plugin.deserializeToComponent(firstLine + "\n" + secondLine)
-
- response.descriptionComponent = BungeeComponentSerializer.get().serialize(messageOfTheDay)[0]
-
- val playerList = motdConfiguration.playerInfo.map { PlayerInfo(it, UUID.randomUUID()) }
- response.players = when (motdConfiguration.maxPlayerDisplayType) {
- null -> Players(
- response.players.max,
- response.players.online,
- playerList.toTypedArray().ifEmpty { response.players.sample }
- )
-
- MaxPlayerDisplayType.REAL -> Players(
- response.players.max,
- response.players.online,
- playerList.toTypedArray().ifEmpty { response.players.sample }
- )
-
- else -> Players(
- response.players.online + motdConfiguration.dynamicPlayerRange,
- response.players.online,
- playerList.toTypedArray().ifEmpty { response.players.sample }
- )
- }
-
- response.version = when (motdConfiguration.versionName) {
- "" -> response.version
- else -> Protocol(
- motdConfiguration.versionName,
- -1
- )
- }
-
- val favicon = if (motdConfiguration.serverIcon == "") {
- response.faviconObject
- } else {
- val serverIcon: BufferedImage = ImageIO.read(File(motdConfiguration.serverIcon))
- Favicon.create(serverIcon)
- }
-
- response.setFavicon(favicon)
}
}
\ No newline at end of file
diff --git a/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ServerPreConnectListener.kt b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ServerPreConnectListener.kt
new file mode 100644
index 0000000..8458a33
--- /dev/null
+++ b/proxy-bungeecord/src/main/kotlin/app/simplecloud/plugin/proxy/bungeecord/listener/ServerPreConnectListener.kt
@@ -0,0 +1,55 @@
+package app.simplecloud.plugin.proxy.bungeecord.listener
+
+import app.simplecloud.plugin.proxy.bungeecord.ProxyBungeeCordPlugin
+import app.simplecloud.plugin.proxy.bungeecord.toBaseComponent
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
+import kotlinx.coroutines.runBlocking
+import net.md_5.bungee.api.connection.ProxiedPlayer
+import net.md_5.bungee.api.event.ServerConnectEvent
+import net.md_5.bungee.api.plugin.Listener
+import net.md_5.bungee.event.EventHandler
+import net.md_5.bungee.event.EventPriority
+import java.util.logging.Logger
+
+class ServerPreConnectListener(
+ private val proxyPlugin: ProxyBungeeCordPlugin,
+): Listener {
+ private val logger = Logger.getLogger(ServerPreConnectListener::class.java.name)
+
+ @EventHandler(priority = EventPriority.HIGH)
+ fun handle(event: ServerConnectEvent) {
+ val player = event.player
+
+ if (proxyPlugin.proxyPlugin.maintenance && !player.hasPermission(ProxyPlugin.JOIN_MAINTENANCE_PERMISSION)) {
+ denyAccess(
+ player,
+ this.proxyPlugin.proxyPlugin.messagesConfiguration.kickMessage.networkMaintenance,
+ event
+ )
+ return
+ }
+
+ runBlocking {
+ try {
+ if (!isServerFull(player)) {
+ return@runBlocking
+ }
+ denyAccess(player, proxyPlugin.proxyPlugin.messagesConfiguration.kickMessage.networkFull, event)
+ } catch (e: Exception) {
+ logger.severe("Error checking player limits: ${e.message}")
+ }
+ }
+ }
+
+ private suspend fun isServerFull(player: ProxiedPlayer): Boolean {
+ val maxPlayers = proxyPlugin.proxyPlugin.cloudControllerHandler.getMaxPlayersInGroup()
+ val onlinePlayers = proxyPlugin.proxyPlugin.cloudControllerHandler.getOnlinePlayersInGroup()
+
+ return onlinePlayers >= maxPlayers && !player.hasPermission(ProxyPlugin.JOIN_FULL_PERMISSION)
+ }
+
+ private fun denyAccess(player: ProxiedPlayer, message: String, event: ServerConnectEvent) {
+ player.disconnect(proxyPlugin.deserializeToComponent(message, player).toBaseComponent())
+ event.isCancelled = true
+ }
+}
diff --git a/proxy-shared/build.gradle.kts b/proxy-shared/build.gradle.kts
index 1ba8b0a..19c6aa3 100644
--- a/proxy-shared/build.gradle.kts
+++ b/proxy-shared/build.gradle.kts
@@ -1,8 +1,12 @@
dependencies {
compileOnly(rootProject.libs.gson)
- compileOnly(rootProject.libs.adventureMinimessage)
+ compileOnly(rootProject.libs.adventure.minimessage)
- implementation(rootProject.libs.configurateYaml)
- implementation(rootProject.libs.configurateKotlin)
+ implementation(rootProject.libs.configurate.yaml)
+ implementation(rootProject.libs.configurate.kotlin)
+
+ compileOnly(rootProject.libs.simplecloud.controller)
+
+ compileOnly(rootProject.libs.command.cloud.core)
}
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/ProxyPlugin.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/ProxyPlugin.kt
new file mode 100644
index 0000000..5d9137c
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/ProxyPlugin.kt
@@ -0,0 +1,27 @@
+package app.simplecloud.plugin.proxy.shared
+
+import app.simplecloud.plugin.proxy.shared.config.YamlConfig
+import app.simplecloud.plugin.proxy.shared.config.message.MessageConfig
+import app.simplecloud.plugin.proxy.shared.config.placeholder.PlaceHolderConfiguration
+import app.simplecloud.plugin.proxy.shared.config.tablis.TabListConfiguration
+import app.simplecloud.plugin.proxy.shared.handler.CloudControllerHandler
+import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+
+open class ProxyPlugin(
+ dirPath: String
+) {
+
+ val config = YamlConfig(dirPath)
+ val tabListConfiguration = config.load("tablist")!!
+ val placeHolderConfiguration = config.load("placeholder")!!
+ val messagesConfiguration = config.load("messages")!!
+ val cloudControllerHandler = CloudControllerHandler()
+ val motdLayoutHandler = MotdLayoutHandler(config, this)
+
+ var maintenance = true
+
+ companion object {
+ val JOIN_MAINTENANCE_PERMISSION = "simplecloud.proxy.join.maintenance"
+ val JOIN_FULL_PERMISSION = "simplecloud.proxy.join.full"
+ }
+}
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/GeneralConfig.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/GeneralConfig.kt
deleted file mode 100644
index 59f879e..0000000
--- a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/GeneralConfig.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package app.simplecloud.plugin.proxy.shared.config
-
-import org.spongepowered.configurate.objectmapping.ConfigSerializable
-
-@ConfigSerializable
-data class GeneralConfig(
- var currentLayout: String = "default-motd"
-)
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/CommandMessageConfig.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/CommandMessageConfig.kt
new file mode 100644
index 0000000..8d76efe
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/CommandMessageConfig.kt
@@ -0,0 +1,17 @@
+package app.simplecloud.plugin.proxy.shared.config.message
+
+import org.spongepowered.configurate.objectmapping.ConfigSerializable
+
+@ConfigSerializable
+data class CommandMessageConfig(
+ val toggleMaintenance: String = "You have toggled the maintenance mode.",
+ val activateMaintenance: String = "You have activated the maintenance mode.",
+ val maintenanceAlreadyActivated: String = "The maintenance mode is already activated.",
+ val deactivateMaintenance: String = "You have deactivated the maintenance mode.",
+ val maintenanceAlreadyDeactivated: String = "The maintenance mode is already deactivated.",
+ val layoutNotFound: String = "The layout could not be found.",
+ val layoutMaintenanceAlreadySet: String = "The layout is already set for maintenance.",
+ val layoutMaintenanceSet: String = "The layout has been set for maintenance.",
+ val layoutNonMaintenanceAlreadySet: String = "The layout is already set for non-maintenance.",
+ val layoutNonMaintenanceSet: String = "The layout has been set for non-maintenance."
+)
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/KickMessageConfig.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/KickMessageConfig.kt
new file mode 100644
index 0000000..4e57cfc
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/KickMessageConfig.kt
@@ -0,0 +1,9 @@
+package app.simplecloud.plugin.proxy.shared.config.message
+
+import org.spongepowered.configurate.objectmapping.ConfigSerializable
+
+@ConfigSerializable
+data class KickMessageConfig(
+ val networkMaintenance: String = "The network is currently in maintenance mode. Please try again later.",
+ val networkFull: String = "The network is currently full. Please try again later.",
+)
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/MessageConfig.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/MessageConfig.kt
new file mode 100644
index 0000000..ef46d5f
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/message/MessageConfig.kt
@@ -0,0 +1,9 @@
+package app.simplecloud.plugin.proxy.shared.config.message
+
+import org.spongepowered.configurate.objectmapping.ConfigSerializable
+
+@ConfigSerializable
+data class MessageConfig(
+ var kickMessage: KickMessageConfig = KickMessageConfig(),
+ var commandMessage: CommandMessageConfig = CommandMessageConfig()
+)
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/motd/MotdLayoutConfiguration.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/motd/MotdLayoutConfiguration.kt
index 22e7f45..5a02064 100644
--- a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/motd/MotdLayoutConfiguration.kt
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/motd/MotdLayoutConfiguration.kt
@@ -5,8 +5,8 @@ import java.awt.image.BufferedImage
@ConfigSerializable
data class MotdLayoutConfiguration(
- val firstLines: List = listOf("SimpleCloud » Simplify your network |<#4595ff> 1.12<#545454> - <#4595ff>1.20"),
- val secondLines: List = listOf("× <#178fff>Status: <#22cc22>Online - <#ffffff>%PROXY%"),
+ val firstLines: List = listOf("A simplecloud.app network"),
+ val secondLines: List = listOf("● ▍ ʀᴇᴀᴅ ᴅᴏᴄs.sɪᴍᴘʟᴇᴄʟᴏᴜᴅ.ᴀᴘᴘ ᴛᴏ ᴄᴏɴғɪɢᴜʀᴇ"),
val playerInfo: List = listOf(),
val versionName: String = "",
var serverIcon: String = "server-icon.png",
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/tablis/TabList.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/tablis/TabList.kt
index 87dd905..5a73412 100644
--- a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/tablis/TabList.kt
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/config/tablis/TabList.kt
@@ -5,15 +5,15 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable
@ConfigSerializable
data class TabList(
val header: List = listOf(
- " ",
- "SimpleCloud » Simplify your network",
- "Online » / ┃ Server » ",
- ""
+ "",
+ "SimpleCloud v3",
+ " Customize header and footer in the config! ",
+ ""
),
val footer: List = listOf(
- "",
- "Twitter » @theSimpleCloud",
- "Discord » discord.simplecloud.app",
- ""
+ "",
+ " players are playing on your network ",
+ " sɪᴍᴘʟᴇᴄʟᴏᴜᴅ.ᴀᴘᴘ",
+ ""
),
)
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/CloudControllerHandler.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/CloudControllerHandler.kt
new file mode 100644
index 0000000..1fd07c7
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/CloudControllerHandler.kt
@@ -0,0 +1,127 @@
+package app.simplecloud.plugin.proxy.shared.handler
+
+import app.simplecloud.controller.api.ControllerApi
+import kotlinx.coroutines.runBlocking
+import java.util.logging.Logger
+
+class CloudControllerHandler {
+
+ private val controllerApi = ControllerApi.createCoroutineApi()
+ private var groupName: String? = null
+ private val logger = Logger.getLogger(CloudControllerHandler::class.java.name)
+
+ init {
+ initializeGroupName()
+ }
+
+ private fun initializeGroupName() {
+ val uniqueId = System.getenv("SIMPLECLOUD_UNIQUE_ID")
+
+ if (uniqueId == null) {
+ logger.warning("Environment variable SIMPLECLOUD_UNIQUE_ID is not set.")
+ return
+ }
+
+ runBlocking {
+ try {
+ val service = controllerApi.getServers().getServerById(uniqueId)
+ groupName = service.group
+ logger.info("Group name initialized to: $groupName")
+ } catch (e: Exception) {
+ logger.severe("Error retrieving server by ID: ${e.message}")
+ }
+ }
+ }
+
+ suspend fun getServiceProperties(key: String): String {
+ return retrievePropertyOrEmpty {
+ val uniqueId = System.getenv("SIMPLECLOUD_UNIQUE_ID")
+ controllerApi.getServers().getServerById(uniqueId).properties[key]
+ }
+ }
+
+ suspend fun getGroupProperties(key: String): String {
+ return groupName?.let {
+ retrievePropertyOrEmpty {
+ controllerApi.getGroups().getGroupByName(it).properties[key]
+ }
+ } ?: run {
+ logger.warning("Group name is not initialized.")
+ ""
+ }
+ }
+
+ suspend fun setServiceProperties(key: String, value: String) {
+ val uniqueId = System.getenv("SIMPLECLOUD_UNIQUE_ID")
+
+ try {
+ controllerApi.getServers().updateServerProperty(uniqueId, key, value)
+ logger.info("Service property '$key' updated to '$value'")
+ } catch (e: Exception) {
+ logger.severe("Error updating service properties: ${e.message}")
+ }
+ }
+
+ suspend fun setServicePropertiesOnAllGroupServices(key: String, value: String) {
+ groupName?.let { name ->
+ try {
+ controllerApi.getServers().getServersByGroup(name).forEach { server ->
+ controllerApi.getServers().updateServerProperty(server.uniqueId, key, value)
+ }
+ logger.info("Service property '$key' updated to '$value' on all services in group '$name'")
+ } catch (e: Exception) {
+ logger.severe("Error updating service properties on all group services: ${e.message}")
+ }
+ } ?: logger.warning("Group name is not initialized.")
+ }
+
+ suspend fun setGroupProperties(key: String, value: String) {
+ groupName?.let { name ->
+ try {
+ val group = controllerApi.getGroups().getGroupByName(name)
+ val updatedGroup = group.copy(properties = group.properties + (key to value))
+ controllerApi.getGroups().updateGroup(updatedGroup)
+ logger.info("Group property '$key' updated to '$value'")
+ } catch (e: Exception) {
+ logger.severe("Error updating group properties: ${e.message}")
+ }
+ } ?: logger.warning("Group name is not initialized.")
+ }
+
+ suspend fun getOnlinePlayersInGroup(): Int {
+ return groupName?.let {
+ try {
+ controllerApi.getServers().getServersByGroup(it).sumBy { it.playerCount.toInt() }
+ } catch (e: Exception) {
+ logger.severe("Error retrieving online players in group: ${e.message}")
+ 0
+ }
+ } ?: run {
+ logger.warning("Group name is not initialized.")
+ 0
+ }
+ }
+
+ suspend fun getMaxPlayersInGroup(): Int {
+ return groupName?.let {
+ try {
+ controllerApi.getGroups().getGroupByName(it).maxPlayers.toInt()
+ } catch (e: Exception) {
+ logger.severe("Error retrieving max players in group: ${e.message}")
+ 0
+ }
+ } ?: run {
+ logger.warning("Group name is not initialized.")
+ 0
+ }
+ }
+
+ private suspend fun retrievePropertyOrEmpty(retrieve: suspend () -> String?): String {
+ return try {
+ retrieve() ?: ""
+ } catch (e: Exception) {
+ logger.severe("Error retrieving property: ${e.message}")
+ ""
+ }
+ }
+}
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/MotdLayoutHandler.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/MotdLayoutHandler.kt
index 0139b4a..ceee4a1 100644
--- a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/MotdLayoutHandler.kt
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/MotdLayoutHandler.kt
@@ -1,77 +1,123 @@
package app.simplecloud.plugin.proxy.shared.handler
-import app.simplecloud.plugin.proxy.shared.config.GeneralConfig
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
import app.simplecloud.plugin.proxy.shared.config.YamlConfig
import app.simplecloud.plugin.proxy.shared.config.motd.MotdLayoutConfiguration
-import java.awt.image.BufferedImage
-import java.io.ByteArrayOutputStream
+import kotlinx.coroutines.runBlocking
import java.io.File
-import java.util.*
-import javax.imageio.ImageIO
-
+import java.util.logging.Logger
class MotdLayoutHandler(
- val yamlConfig: YamlConfig,
- val generalConfig: GeneralConfig
+ private val yamlConfig: YamlConfig,
+ private val proxyPlugin: ProxyPlugin
) {
+ private val cloudControllerHandler = proxyPlugin.cloudControllerHandler
private val loadedMotdLayouts: MutableMap = mutableMapOf()
+ private val logger = Logger.getLogger(MotdLayoutHandler::class.java.name)
+
+ companion object {
+ private const val RANDOM_MOTD_KEY = "random-motd-layouts"
+ var CURRENT_LAYOUT_KEY = "current-motd-layout"
+ var CURRENT_MAINTENANCE_LAYOUT_KEY = "current-maintenance-layout"
+ const val DEFAULT_LAYOUT_NAME = "default-motd"
+ const val DEFAULT_MAINTENANCE_LAYOUT_NAME = "default-maintenance-motd"
+ }
fun loadMotdLayouts() {
loadedMotdLayouts.clear()
- val directory = File(yamlConfig.dirPath + "/layout")
+ initializeLayoutDirectory()
+ loadLayoutsFromDirectory()
+ createDefaultLayoutsIfEmpty()
+ initializeCloudProperties()
+ }
+ private fun initializeLayoutDirectory() {
+ val directory = File(yamlConfig.dirPath + "/layout")
if (!directory.exists()) {
directory.mkdirs()
}
+ }
- directory.listFiles()?.forEach {
- val name = it.nameWithoutExtension
- val motdLayout = yamlConfig.load("layout/$name")
- if (motdLayout != null) {
- //motdLayout.serverIcon = convirtImageFileToBase64Url(File(motdLayout.serverIcon))
- loadedMotdLayouts[name] = motdLayout
- println("Loaded motd layout: $name")
+ private fun loadLayoutsFromDirectory() {
+ File(yamlConfig.dirPath + "/layout").listFiles()?.forEach { file ->
+ yamlConfig.load("layout/${file.nameWithoutExtension}")?.let { layout ->
+ loadedMotdLayouts[file.nameWithoutExtension] = layout
+ logger.info("Loaded MOTD layout: ${file.nameWithoutExtension}")
}
}
+ }
- if (loadedMotdLayouts.isEmpty()) {
- println("No motd layouts found, creating default motd layout")
- val defaultMotdLayout = MotdLayoutConfiguration()
- yamlConfig.save("layout/default-motd", defaultMotdLayout)
- loadedMotdLayouts["default-motd"] = defaultMotdLayout
+ private fun createDefaultLayoutsIfEmpty() {
+ if (getAllNoneMaintenanceLayouts().isEmpty()) {
+ createAndSaveDefaultLayout(DEFAULT_LAYOUT_NAME)
+ }
+ if (getAllMaintenanceLayouts().isEmpty()) {
+ createAndSaveDefaultLayout(DEFAULT_MAINTENANCE_LAYOUT_NAME)
}
}
- fun getMotdLayout(name: String): MotdLayoutConfiguration {
- return loadedMotdLayouts[name] ?: MotdLayoutConfiguration()
+ private fun createAndSaveDefaultLayout(layoutName: String) {
+ val defaultLayout = MotdLayoutConfiguration()
+ yamlConfig.save("layout/$layoutName", defaultLayout)
+ loadedMotdLayouts[layoutName] = defaultLayout
+ logger.info("Created and saved default layout: $layoutName")
}
- fun getCurrentMotdLayout(): MotdLayoutConfiguration {
- return getMotdLayout(generalConfig.currentLayout)
+ private fun initializeCloudProperties() = runBlocking {
+ with(cloudControllerHandler) {
+ if (getServiceProperties(RANDOM_MOTD_KEY).isEmpty()) {
+ setServiceProperties(RANDOM_MOTD_KEY, "false")
+ setGroupProperties(RANDOM_MOTD_KEY, "false")
+ logger.info("No random MOTD layout key found in service properties, setting to false.")
+ }
+
+ if (getServiceProperties(CURRENT_LAYOUT_KEY).isEmpty()) {
+ setServiceProperties(CURRENT_LAYOUT_KEY, getAllNoneMaintenanceLayouts().first())
+ setGroupProperties(CURRENT_LAYOUT_KEY, getAllNoneMaintenanceLayouts().first())
+ logger.info("No current MOTD layout key found in service properties, setting to first layout.")
+ }
+
+ if (getServiceProperties(CURRENT_MAINTENANCE_LAYOUT_KEY).isEmpty()) {
+ setServiceProperties(CURRENT_MAINTENANCE_LAYOUT_KEY, getAllMaintenanceLayouts().first())
+ setGroupProperties(CURRENT_MAINTENANCE_LAYOUT_KEY, getAllMaintenanceLayouts().first())
+ logger.info("No current maintenance MOTD layout key found in service properties, setting to first layout.")
+ }
+ }
}
- fun getLoadedMotdLayouts(): List {
- return loadedMotdLayouts.keys.toList()
+ fun getMotdLayout(name: String): MotdLayoutConfiguration =
+ loadedMotdLayouts[name] ?: MotdLayoutConfiguration()
+
+ suspend fun getMaintenanceLayout(): MotdLayoutConfiguration {
+ val maintenanceLayoutName = cloudControllerHandler.getServiceProperties(CURRENT_MAINTENANCE_LAYOUT_KEY).ifEmpty {
+ logger.warning("No current maintenance layout found, using default maintenance layout as fallback.")
+ DEFAULT_MAINTENANCE_LAYOUT_NAME
+ }
+ return getMotdLayout(maintenanceLayoutName)
}
- fun setMotdLayout(name: String) {
- generalConfig.currentLayout = name
- yamlConfig.save("general", generalConfig)
+ suspend fun getNonMaintenanceLayout(): MotdLayoutConfiguration {
+ val nonMaintenanceLayoutName = cloudControllerHandler.getServiceProperties(CURRENT_LAYOUT_KEY).ifEmpty {
+ logger.warning("No current non-maintenance layout found, using default layout as fallback.")
+ DEFAULT_LAYOUT_NAME
+ }
+ return getMotdLayout(nonMaintenanceLayoutName)
}
- /*fun convirtImageFileToBase64Url(imageFile: File): String {
- /*if (!imageFile.exists()) {
- println("Image file not found: ${imageFile.absolutePath}")
- return ""
+ suspend fun getCurrentMotdLayout(): MotdLayoutConfiguration {
+ val layouts = if (proxyPlugin.maintenance) getAllMaintenanceLayouts() else getAllNoneMaintenanceLayouts()
+ val useRandomLayouts = cloudControllerHandler.getServiceProperties(RANDOM_MOTD_KEY).toBoolean()
+ val currentLayoutKey = if (proxyPlugin.maintenance) CURRENT_MAINTENANCE_LAYOUT_KEY else CURRENT_LAYOUT_KEY
+ val selectedLayout = cloudControllerHandler.getServiceProperties(currentLayoutKey).ifEmpty {
+ logger.warning("No current layout found, using random layout as fallback.")
+ layouts.firstOrNull() ?: DEFAULT_LAYOUT_NAME
}
- val serverIcon: BufferedImage = ImageIO.read(imageFile)
- val outputStream = ByteArrayOutputStream()
- ImageIO.write(serverIcon, "png", outputStream)
- val iconBytes = outputStream.toByteArray()
- return Base64.getEncoder().encodeToString(iconBytes)
- */
- return imageFile.name
- }*/
-}
\ No newline at end of file
+ return getMotdLayout(if (useRandomLayouts) layouts.random() else selectedLayout)
+ }
+
+ fun getAllMaintenanceLayouts(): List = loadedMotdLayouts.keys.filter { it.contains("maintenance") }
+
+ fun getAllNoneMaintenanceLayouts(): List = loadedMotdLayouts.keys.filter { !it.contains("maintenance") }
+}
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/CommandSender.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/CommandSender.kt
new file mode 100644
index 0000000..32dd79f
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/CommandSender.kt
@@ -0,0 +1,6 @@
+package app.simplecloud.plugin.proxy.shared.handler.command
+
+interface CommandSender {
+
+ fun sendMessage(message: String)
+}
\ No newline at end of file
diff --git a/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/ProxyCommandHandler.kt b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/ProxyCommandHandler.kt
new file mode 100644
index 0000000..185eb2b
--- /dev/null
+++ b/proxy-shared/src/main/kotlin/app/simplecloud/plugin/proxy/shared/handler/command/ProxyCommandHandler.kt
@@ -0,0 +1,164 @@
+package app.simplecloud.plugin.proxy.shared.handler.command
+
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
+import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+import kotlinx.coroutines.runBlocking
+import org.incendo.cloud.CommandManager
+import org.incendo.cloud.context.CommandContext
+import org.incendo.cloud.parser.standard.StringParser.stringParser
+import org.incendo.cloud.suggestion.Suggestion
+import org.incendo.cloud.suggestion.SuggestionProvider
+import java.util.concurrent.CompletableFuture
+
+class ProxyCommandHandler(
+ val commandManager: CommandManager,
+ val proxyPlugin: ProxyPlugin
+) {
+
+ fun loadCommands() {
+ loadToggleMaintenanceCommand()
+ loadActivateMaintenanceCommand()
+ loadDeactivateMaintenanceCommand()
+
+ loadLayoutMaintenanceSetCommand()
+ loadLayoutNonMaintenanceSetCommand()
+ }
+
+ private fun loadToggleMaintenanceCommand() {
+ commandManager.command(
+ commandManager.commandBuilder("proxy")
+ .literal("maintenance")
+ .literal("toggle")
+ .permission("simplecloud.command.proxy.maintenance.toggle")
+ .handler { context: CommandContext ->
+ runBlocking {
+ val mode = !proxyPlugin.cloudControllerHandler.getGroupProperties("maintenance").toBoolean()
+ setProxyMaintenanceMode(mode)
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.toggleMaintenance)
+ }
+ }
+ .build()
+ )
+ }
+
+ private fun loadActivateMaintenanceCommand() {
+ commandManager.command(
+ commandManager.commandBuilder("proxy")
+ .literal("maintenance")
+ .literal("activate")
+ .permission("simplecloud.command.proxy.maintenance.activate")
+ .handler { context: CommandContext ->
+ runBlocking {
+ if (!proxyPlugin.cloudControllerHandler.getGroupProperties("maintenance").toBoolean()) {
+ setProxyMaintenanceMode(true)
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.activateMaintenance)
+ } else {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.maintenanceAlreadyActivated)
+ }
+ }
+ }
+ .build()
+ )
+ }
+
+ private fun loadDeactivateMaintenanceCommand() {
+ commandManager.command(
+ commandManager.commandBuilder("proxy")
+ .literal("maintenance")
+ .literal("deactivate")
+ .permission("simplecloud.command.proxy.maintenance.deactivate")
+ .handler { context: CommandContext ->
+ runBlocking {
+ if (proxyPlugin.cloudControllerHandler.getGroupProperties("maintenance").toBoolean()) {
+ setProxyMaintenanceMode(false)
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.deactivateMaintenance)
+ } else {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.maintenanceAlreadyDeactivated)
+ }
+ }
+ }
+ .build()
+ )
+ }
+
+ private fun loadLayoutMaintenanceSetCommand() {
+ commandManager.command(
+ commandManager.commandBuilder("proxy")
+ .literal("layout")
+ .literal("maintenance")
+ .literal("set")
+ .required(
+ "layout",
+ stringParser()
+ ) { _, _ ->
+ val suggestionList = proxyPlugin.motdLayoutHandler.getAllMaintenanceLayouts().map { Suggestion.suggestion(it) }
+ CompletableFuture.completedFuture(suggestionList)
+ }
+ .permission("simplecloud.command.proxy.layout.maintenance.set")
+ .handler { context: CommandContext ->
+ val layout = context.get("layout")
+
+ if (!proxyPlugin.motdLayoutHandler.getAllMaintenanceLayouts().contains(layout)) {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutNotFound)
+ return@handler
+ }
+
+ if (MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY == layout) {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutMaintenanceAlreadySet)
+ return@handler
+ }
+
+ runBlocking {
+ proxyPlugin.cloudControllerHandler.setServicePropertiesOnAllGroupServices(MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY, layout)
+ proxyPlugin.cloudControllerHandler.setGroupProperties(MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY, layout)
+
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutMaintenanceSet)
+ }
+ }
+ .build()
+ )
+ }
+
+ private fun loadLayoutNonMaintenanceSetCommand() {
+ commandManager.command(
+ commandManager.commandBuilder("proxy")
+ .literal("layout")
+ .literal("nonmaintenance")
+ .literal("set")
+ .required(
+ "layout",
+ stringParser()
+ ) { _, _ ->
+ val suggestionList = proxyPlugin.motdLayoutHandler.getAllNoneMaintenanceLayouts().map { Suggestion.suggestion(it) }
+ CompletableFuture.completedFuture(suggestionList)
+ }
+ .permission("simplecloud.command.proxy.layout.nonmaintenance.set")
+ .handler { context: CommandContext ->
+ val layout = context.get("layout")
+
+ if (!proxyPlugin.motdLayoutHandler.getAllNoneMaintenanceLayouts().contains(layout)) {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutNotFound)
+ return@handler
+ }
+
+ if (MotdLayoutHandler.CURRENT_LAYOUT_KEY == layout) {
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutNonMaintenanceAlreadySet)
+ return@handler
+ }
+
+ runBlocking {
+ proxyPlugin.cloudControllerHandler.setServicePropertiesOnAllGroupServices(MotdLayoutHandler.CURRENT_LAYOUT_KEY, layout)
+ proxyPlugin.cloudControllerHandler.setGroupProperties(MotdLayoutHandler.CURRENT_LAYOUT_KEY, layout)
+
+ context.sender().sendMessage(proxyPlugin.messagesConfiguration.commandMessage.layoutNonMaintenanceSet)
+ }
+ }
+ .build()
+ )
+ }
+
+ private suspend fun setProxyMaintenanceMode(mode: Boolean) {
+ this.proxyPlugin.cloudControllerHandler.setGroupProperties("maintenance", mode.toString())
+ this.proxyPlugin.cloudControllerHandler.setServicePropertiesOnAllGroupServices("maintenance", mode.toString())
+ }
+}
\ No newline at end of file
diff --git a/proxy-velocity/build.gradle.kts b/proxy-velocity/build.gradle.kts
index ec2315d..a01c7f5 100644
--- a/proxy-velocity/build.gradle.kts
+++ b/proxy-velocity/build.gradle.kts
@@ -3,12 +3,14 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
dependencies {
api(project(":proxy-shared"))
- compileOnly(rootProject.libs.adventureMinimessage)
+ compileOnly(rootProject.libs.adventure.minimessage)
- compileOnly(rootProject.libs.velocityApi)
- annotationProcessor(rootProject.libs.velocityApi)
-}
+ compileOnly(rootProject.libs.velocity)
+ annotationProcessor(rootProject.libs.velocity)
-tasks.named("shadowJar", ShadowJar::class) {
- //relocate("kotlin.", "app.simplecloud.plugin.libs.kotlin.")
+ compileOnly(rootProject.libs.simplecloud.event.wrapper.velocity)
+ compileOnly(rootProject.libs.simplecloud.controller)
+
+ implementation(rootProject.libs.command.cloud.core)
+ implementation(rootProject.libs.command.cloud.velocity)
}
\ No newline at end of file
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/ProxyVelocityPlugin.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/ProxyVelocityPlugin.kt
index 817c63c..1db1fce 100644
--- a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/ProxyVelocityPlugin.kt
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/ProxyVelocityPlugin.kt
@@ -1,23 +1,37 @@
package app.simplecloud.plugin.proxy.velocity
-import app.simplecloud.plugin.proxy.shared.config.GeneralConfig
+import app.simplecloud.controller.api.ControllerApi
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
import app.simplecloud.plugin.proxy.shared.config.YamlConfig
import app.simplecloud.plugin.proxy.shared.config.placeholder.PlaceHolderConfiguration
import app.simplecloud.plugin.proxy.shared.config.tablis.TabListConfiguration
import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+import app.simplecloud.plugin.proxy.shared.handler.command.CommandSender
+import app.simplecloud.plugin.proxy.shared.handler.command.ProxyCommandHandler
import app.simplecloud.plugin.proxy.velocity.event.ConfigureTagResolversEvent
import app.simplecloud.plugin.proxy.velocity.handler.TabListHandler
+import app.simplecloud.plugin.proxy.velocity.listener.CloudListener
import app.simplecloud.plugin.proxy.velocity.listener.ConfigureTagResolversListener
import app.simplecloud.plugin.proxy.velocity.listener.ProxyPingListener
+import app.simplecloud.plugin.proxy.velocity.listener.ServerPreConnectListener
import com.google.inject.Inject
+import com.velocitypowered.api.command.CommandSource
+import com.velocitypowered.api.command.RawCommand
+import com.velocitypowered.api.command.SimpleCommand
+import com.velocitypowered.api.command.SimpleCommand.Invocation
import com.velocitypowered.api.event.Subscribe
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent
+import com.velocitypowered.api.plugin.PluginContainer
import com.velocitypowered.api.plugin.annotation.DataDirectory
import com.velocitypowered.api.proxy.Player
import com.velocitypowered.api.proxy.ProxyServer
+import kotlinx.coroutines.runBlocking
import net.kyori.adventure.text.Component
import net.kyori.adventure.text.minimessage.MiniMessage
+import org.incendo.cloud.SenderMapper
+import org.incendo.cloud.execution.ExecutionCoordinator
+import org.incendo.cloud.velocity.VelocityCommandManager
import org.slf4j.Logger
import java.nio.file.Path
import kotlin.io.path.pathString
@@ -25,33 +39,53 @@ import kotlin.io.path.pathString
class ProxyVelocityPlugin @Inject constructor(
val proxyServer: ProxyServer,
@DataDirectory val dataDirectory: Path,
- val logger: Logger
-) {
+ val logger: Logger,
+ val pluginContainer: PluginContainer
+): ProxyPlugin(dataDirectory.pathString) {
val tabListHandler = TabListHandler(this)
- private val config = YamlConfig(this.dataDirectory.pathString)
- val tabListConfiguration = config.load("tablist")!!
- val placeHolderConfiguration = config.load("placeholder")!!
- val generalConfig = config.load("general")!!
- val motdLayoutHandler = MotdLayoutHandler(config, generalConfig)
-
+ private lateinit var commandManager: VelocityCommandManager
private val miniMessage = MiniMessage.miniMessage()
@Subscribe
fun onProxyInitialize(event: ProxyInitializeEvent) {
config.save("tablist", this.tabListConfiguration)
config.save("placeholder", this.placeHolderConfiguration)
- config.save("general", this.generalConfig)
+ config.save("messages", this.messagesConfiguration)
this.motdLayoutHandler.loadMotdLayouts()
this.proxyServer.eventManager.register(this, ProxyPingListener(this))
this.proxyServer.eventManager.register(this, ConfigureTagResolversListener(this))
+ this.proxyServer.eventManager.register(this, CloudListener(this))
+ this.proxyServer.eventManager.register(this, ServerPreConnectListener(this))
+
if (this.tabListConfiguration.tabListUpdateTime > 0)
this.tabListHandler.startTabListTask()
else
this.logger.info("Tablist update time is set to 0, tablist will not be updated automatically")
+
+ val executionCoordinator = ExecutionCoordinator.simpleCoordinator()
+
+ val senderMapper = SenderMapper.create(
+ { commandSender -> VelocityCommandSender(commandSender, this) },
+ { commandSender -> (commandSender as VelocityCommandSender).getCommandSource() }
+ )
+
+ commandManager = VelocityCommandManager(
+ pluginContainer,
+ proxyServer,
+ executionCoordinator,
+ senderMapper
+ )
+
+ val proxyCommandHandler = ProxyCommandHandler(commandManager, this)
+ proxyCommandHandler.loadCommands()
+
+ System.getenv("SIMPLECLOUD_MAINTENANCE")?.let {
+ this.maintenance = it == "true"
+ }
}
@Subscribe
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/VelocityCommandSender.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/VelocityCommandSender.kt
new file mode 100644
index 0000000..a594946
--- /dev/null
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/VelocityCommandSender.kt
@@ -0,0 +1,18 @@
+package app.simplecloud.plugin.proxy.velocity
+
+import app.simplecloud.plugin.proxy.shared.handler.command.CommandSender
+import com.velocitypowered.api.command.CommandSource
+
+class VelocityCommandSender(
+ private val commandSource: CommandSource,
+ val proxyVelocityPlugin: ProxyVelocityPlugin
+) : CommandSender {
+
+ fun getCommandSource(): CommandSource {
+ return commandSource
+ }
+
+ override fun sendMessage(message: String) {
+ commandSource.sendMessage(this.proxyVelocityPlugin.deserializeToComponent(message))
+ }
+}
\ No newline at end of file
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/handler/TabListHandler.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/handler/TabListHandler.kt
index aeafce1..36a1d4e 100644
--- a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/handler/TabListHandler.kt
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/handler/TabListHandler.kt
@@ -15,14 +15,14 @@ class TabListHandler(
private lateinit var task: ScheduledTask
fun startTabListTask() {
- this.task = this.plugin.proxyServer.scheduler.buildTask(plugin) {
+ this.task = this.plugin.proxyServer.scheduler.buildTask(plugin, Runnable {
this.plugin.proxyServer.allPlayers.forEach {
this.updateTabListForPlayer(it)
}
this.tabListIndex.forEach { (key, value) ->
this.tabListIndex[key] = value + 1
}
- }.repeat(this.plugin.tabListConfiguration.tabListUpdateTime, java.util.concurrent.TimeUnit.MILLISECONDS).schedule()
+ }).repeat(this.plugin.tabListConfiguration.tabListUpdateTime, java.util.concurrent.TimeUnit.MILLISECONDS).schedule()
}
fun stopTabListTask() {
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/CloudListener.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/CloudListener.kt
new file mode 100644
index 0000000..c554fd2
--- /dev/null
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/CloudListener.kt
@@ -0,0 +1,66 @@
+package app.simplecloud.plugin.proxy.velocity.listener
+
+import app.simplecloud.event.velocity.mapping.CloudServerUpdateEvent
+import app.simplecloud.plugin.proxy.shared.handler.MotdLayoutHandler
+import app.simplecloud.plugin.proxy.velocity.ProxyVelocityPlugin
+import com.velocitypowered.api.event.Subscribe
+import java.util.logging.Logger
+
+class CloudListener(
+ private val plugin: ProxyVelocityPlugin
+) {
+
+ private val logger = Logger.getLogger(CloudListener::class.java.name)
+
+ @Subscribe
+ fun test(event: CloudServerUpdateEvent) {
+
+ if (event.getTo().uniqueId != System.getenv("SIMPLECLOUD_UNIQUE_ID")) return
+
+ checkMaintenanceChance(event)
+ checkLayoutMaintenanceChance(event)
+ checkLayoutChance(event)
+ }
+
+ private fun checkMaintenanceChance(event: CloudServerUpdateEvent) {
+ val isMaintenance = event.getTo().properties["maintenance"]
+
+ if (isMaintenance == event.getFrom().properties["maintenance"]) return
+
+ val newMaintenanceState = isMaintenance == "true"
+
+ if (this.plugin.maintenance == newMaintenanceState) return
+
+ this.plugin.maintenance = newMaintenanceState
+
+ this.logger.info("Maintenance mode has been toggled to $newMaintenanceState")
+ }
+
+ private fun checkLayoutMaintenanceChance(event: CloudServerUpdateEvent) {
+ val layout = event.getTo().properties[MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY]
+
+ if (layout == event.getFrom().properties[MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY]) return
+
+ val newLayout = layout ?: MotdLayoutHandler.DEFAULT_MAINTENANCE_LAYOUT_NAME
+
+ if (MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY == newLayout) return
+
+ MotdLayoutHandler.CURRENT_MAINTENANCE_LAYOUT_KEY = newLayout
+
+ this.logger.info("Layout has been changed to $newLayout")
+ }
+
+ private fun checkLayoutChance(event: CloudServerUpdateEvent) {
+ val layout = event.getTo().properties[MotdLayoutHandler.CURRENT_LAYOUT_KEY]
+
+ if (layout == event.getFrom().properties[MotdLayoutHandler.CURRENT_LAYOUT_KEY]) return
+
+ val newLayout = layout ?: MotdLayoutHandler.DEFAULT_LAYOUT_NAME
+
+ if (MotdLayoutHandler.CURRENT_LAYOUT_KEY == newLayout) return
+
+ MotdLayoutHandler.CURRENT_LAYOUT_KEY = newLayout
+
+ this.logger.info("Layout has been changed to $newLayout")
+ }
+}
\ No newline at end of file
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ConfigureTagResolversListener.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ConfigureTagResolversListener.kt
index 3fa2d3d..ede5a77 100644
--- a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ConfigureTagResolversListener.kt
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ConfigureTagResolversListener.kt
@@ -5,6 +5,7 @@ import app.simplecloud.plugin.proxy.velocity.ProxyVelocityPlugin
import app.simplecloud.plugin.proxy.velocity.event.ConfigureTagResolversEvent
import com.velocitypowered.api.event.PostOrder
import com.velocitypowered.api.event.Subscribe
+import kotlinx.coroutines.runBlocking
import kotlin.jvm.optionals.getOrNull
class ConfigureTagResolversListener(
@@ -13,25 +14,27 @@ class ConfigureTagResolversListener(
@Subscribe(order = PostOrder.FIRST)
fun onConfigureTagResolvers(event: ConfigureTagResolversEvent) {
- val player = event.player
- val serverName = player?.currentServer?.getOrNull()?.serverInfo?.name ?: "unknown"
+ runBlocking {
+ val player = event.player
+ val serverName = player?.currentServer?.getOrNull()?.serverInfo?.name ?: "unknown"
- val ping = player?.ping ?: -1
- val pingColors = plugin.placeHolderConfiguration.pingColors
+ val ping = player?.ping ?: -1
+ val pingColors = plugin.placeHolderConfiguration.pingColors
- val onlinePlayers = this.plugin.proxyServer.allPlayers.size
- val realMaxPlayers = this.plugin.proxyServer.configuration.showMaxPlayers
+ val onlinePlayers = plugin.proxyServer.allPlayers.size
+ val realMaxPlayers = plugin.proxyServer.configuration.showMaxPlayers
- event.withTagResolvers(
- TagResolverHelper.getDefaultTagResolvers(
- serverName,
- ping,
- pingColors,
- onlinePlayers,
- realMaxPlayers,
- this.plugin.motdLayoutHandler.getCurrentMotdLayout()
+ event.withTagResolvers(
+ TagResolverHelper.getDefaultTagResolvers(
+ serverName,
+ ping,
+ pingColors,
+ onlinePlayers,
+ realMaxPlayers,
+ plugin.motdLayoutHandler.getCurrentMotdLayout()
+ )
)
- )
+ }
}
}
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ProxyPingListener.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ProxyPingListener.kt
index 7d1c7d8..4268045 100644
--- a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ProxyPingListener.kt
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ProxyPingListener.kt
@@ -7,9 +7,11 @@ import com.velocitypowered.api.event.proxy.ProxyPingEvent
import com.velocitypowered.api.proxy.server.ServerPing
import com.velocitypowered.api.proxy.server.ServerPing.SamplePlayer
import com.velocitypowered.api.util.Favicon
+import kotlinx.coroutines.runBlocking
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
+import java.io.IOException
import java.net.InetAddress
import java.util.*
import javax.imageio.ImageIO
@@ -22,58 +24,66 @@ class ProxyPingListener(
@Subscribe
fun onProxyPing(event: ProxyPingEvent) {
- val hostStringFromConnection = event.connection.remoteAddress.address.hostAddress
- val hostStringFromServer = InetAddress.getLocalHost().hostAddress
-
- if (hostStringFromConnection == hostStringFromServer) {
- return
- }
-
- val serverPing = event.ping
-
- val motdConfiguration = this.plugin.motdLayoutHandler.getCurrentMotdLayout()
-
- val firstLine = motdConfiguration.firstLines.random()
- val secondLine = motdConfiguration.secondLines.random()
-
- val messageOfTheDay = this.plugin.deserializeToComponent("$firstLine\n$secondLine")
-
- val playerList = motdConfiguration.playerInfo.map { SamplePlayer(it, UUID.randomUUID()) }
-
- val players = serverPing.players.getOrNull()
- val onlinePlayers = players?.online ?: 0
- val realMaxPlayers = players?.max ?: 0
- val maxPlayers = when (motdConfiguration.maxPlayerDisplayType) {
- MaxPlayerDisplayType.REAL -> realMaxPlayers
- MaxPlayerDisplayType.DYNAMIC -> onlinePlayers + motdConfiguration.dynamicPlayerRange
- null -> realMaxPlayers
- }
-
- val samplePlayers = playerList.ifEmpty { players?.sample ?: emptyList() }
-
- val versions: ServerPing.Version = when (motdConfiguration.versionName) {
- "" -> serverPing.version
- else -> ServerPing.Version(
- -1,
- motdConfiguration.versionName
- )
+ runBlocking {
+ val hostStringFromConnection = event.connection.remoteAddress.address.hostAddress
+ val hostStringFromServer = InetAddress.getLocalHost().hostAddress
+
+ if (hostStringFromConnection == hostStringFromServer) {
+ return@runBlocking
+ }
+
+ val serverPing = event.ping
+
+ val motdConfiguration = plugin.motdLayoutHandler.getCurrentMotdLayout()
+
+ val firstLine = motdConfiguration.firstLines.random()
+ val secondLine = motdConfiguration.secondLines.random()
+
+ val messageOfTheDay = plugin.deserializeToComponent("$firstLine\n$secondLine")
+
+ val playerList = motdConfiguration.playerInfo.map { SamplePlayer(it, UUID.randomUUID()) }
+
+ val players = serverPing.players.getOrNull()
+ val onlinePlayers = players?.online ?: 0
+ val realMaxPlayers = players?.max ?: 0
+ val maxPlayers = when (motdConfiguration.maxPlayerDisplayType) {
+ MaxPlayerDisplayType.REAL -> realMaxPlayers
+ MaxPlayerDisplayType.DYNAMIC -> onlinePlayers + motdConfiguration.dynamicPlayerRange
+ null -> realMaxPlayers
+ }
+
+ val samplePlayers = playerList.ifEmpty { players?.sample ?: emptyList() }
+
+ val versions: ServerPing.Version = when (motdConfiguration.versionName) {
+ "" -> serverPing.version
+ else -> ServerPing.Version(
+ -1,
+ motdConfiguration.versionName
+ )
+ }
+
+ val favicon = if (motdConfiguration.serverIcon == "") {
+ serverPing.favicon.orElse(null)
+ } else {
+ try {
+ val serverIcon: BufferedImage = ImageIO.read(File(motdConfiguration.serverIcon))
+ Favicon.create(serverIcon)
+ } catch (e: IOException) {
+ null
+ }
+ }
+
+ val builder = event.ping.asBuilder()
+ .version(versions)
+ .onlinePlayers(onlinePlayers)
+ .maximumPlayers(maxPlayers)
+ .samplePlayers(*samplePlayers.toTypedArray())
+ .description(messageOfTheDay)
+
+ favicon?.let { builder.favicon(it) }
+
+ event.ping = builder.build()
}
-
- val favicon = if (motdConfiguration.serverIcon == "") {
- serverPing.favicon.orElse(null)
- } else {
- val serverIcon: BufferedImage = ImageIO.read(File(motdConfiguration.serverIcon))
- Favicon.create(serverIcon)
- }
-
- event.ping = event.ping.asBuilder()
- .version(versions)
- .onlinePlayers(onlinePlayers)
- .maximumPlayers(maxPlayers)
- .samplePlayers(*samplePlayers.toTypedArray())
- .description(messageOfTheDay)
- .favicon(favicon)
- .build()
}
}
\ No newline at end of file
diff --git a/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ServerPreConnectListener.kt b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ServerPreConnectListener.kt
new file mode 100644
index 0000000..d8c8ed0
--- /dev/null
+++ b/proxy-velocity/src/main/kotlin/app/simplecloud/plugin/proxy/velocity/listener/ServerPreConnectListener.kt
@@ -0,0 +1,53 @@
+package app.simplecloud.plugin.proxy.velocity.listener
+
+import app.simplecloud.plugin.proxy.shared.ProxyPlugin
+import app.simplecloud.plugin.proxy.velocity.ProxyVelocityPlugin
+import com.velocitypowered.api.event.PostOrder
+import com.velocitypowered.api.event.Subscribe
+import com.velocitypowered.api.event.player.ServerPreConnectEvent
+import com.velocitypowered.api.proxy.Player
+import kotlinx.coroutines.runBlocking
+import java.util.logging.Logger
+
+class ServerPreConnectListener(
+ private val proxyPlugin: ProxyVelocityPlugin,
+) {
+ private val logger = Logger.getLogger(ServerPreConnectListener::class.java.name)
+
+ @Subscribe(order = PostOrder.EARLY)
+ fun handle(event: ServerPreConnectEvent) {
+ val player = event.player
+
+ if (proxyPlugin.maintenance && !player.hasPermission(ProxyPlugin.JOIN_MAINTENANCE_PERMISSION)) {
+ denyAccess(
+ player,
+ this.proxyPlugin.messagesConfiguration.kickMessage.networkMaintenance,
+ event
+ )
+ return
+ }
+
+ runBlocking {
+ try {
+ if (!isServerFull(player)) {
+ return@runBlocking
+ }
+ denyAccess(player, proxyPlugin.messagesConfiguration.kickMessage.networkFull, event)
+ } catch (e: Exception) {
+ logger.severe("Error checking player limits: ${e.message}")
+ }
+ }
+ }
+
+ private suspend fun isServerFull(player: Player): Boolean {
+ val maxPlayers = proxyPlugin.cloudControllerHandler.getMaxPlayersInGroup()
+ val onlinePlayers = proxyPlugin.cloudControllerHandler.getOnlinePlayersInGroup()
+
+ return onlinePlayers >= maxPlayers && !player.hasPermission(ProxyPlugin.JOIN_FULL_PERMISSION)
+ }
+
+ private fun denyAccess(player: Player, message: String, event: ServerPreConnectEvent) {
+ player.disconnect(proxyPlugin.deserializeToComponent(message, player))
+ event.result = ServerPreConnectEvent.ServerResult.denied()
+ }
+}