diff --git a/.idea/detekt.xml b/.idea/detekt.xml new file mode 100644 index 00000000..9f881422 --- /dev/null +++ b/.idea/detekt.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.iml b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.iml index 31a4a13c..25a80f82 100644 --- a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.iml +++ b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.main.iml b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.main.iml index 84d2f144..d6721729 100644 --- a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.main.iml +++ b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.main.iml @@ -1,5 +1,5 @@ - + @@ -13,7 +13,7 @@ - + diff --git a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.test.iml b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.test.iml index f4347692..55c95ae4 100644 --- a/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.test.iml +++ b/.idea/modules/packages/google-agones-sdk/bindings/java/shulker.packages.google-agones-sdk.test.iml @@ -1,5 +1,5 @@ - + @@ -8,7 +8,7 @@ - + diff --git a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.iml b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.iml index 531e9858..95782c19 100644 --- a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.iml +++ b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.main.iml b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.main.iml index c4b33082..46e80816 100644 --- a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.main.iml +++ b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.test.iml b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.test.iml index 8a874b99..23b596ae 100644 --- a/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.test.iml +++ b/.idea/modules/packages/google-open-match-sdk/bindings/java/shulker.packages.google-open-match-sdk.test.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.bungeecord.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.bungeecord.iml index e9318eb8..ca5ee5f5 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.bungeecord.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.bungeecord.iml @@ -1,5 +1,5 @@ - + @@ -42,7 +42,7 @@ - + @@ -165,7 +165,7 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.common.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.common.iml index c114c524..3d64ef56 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.common.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.common.iml @@ -1,5 +1,5 @@ - + @@ -42,7 +42,7 @@ - + @@ -136,7 +136,7 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.iml index 3020a7ac..ad0ffaa3 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.main.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.main.iml index b90877ea..02f6b167 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.main.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.test.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.test.iml index 2f44fe9d..fda204af 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.test.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.test.iml @@ -1,5 +1,5 @@ - + @@ -40,9 +40,11 @@ + + diff --git a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.velocity.iml b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.velocity.iml index 33499c1a..bb8fd741 100644 --- a/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.velocity.iml +++ b/.idea/modules/packages/shulker-proxy-agent/shulker.packages.shulker-proxy-agent.velocity.iml @@ -1,5 +1,5 @@ - + @@ -43,7 +43,7 @@ - + @@ -160,7 +160,7 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.iml b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.iml index 43e7219f..579768bd 100644 --- a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.iml +++ b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.main.iml b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.main.iml index ef630304..7fccee43 100644 --- a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.main.iml +++ b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.test.iml b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.test.iml index 2e4ea85b..80430550 100644 --- a/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.test.iml +++ b/.idea/modules/packages/shulker-proxy-api/shulker.packages.shulker-proxy-api.test.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.iml b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.iml index 4db87b06..7bb3c800 100644 --- a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.iml +++ b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.main.iml b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.main.iml index f86fd051..885f438c 100644 --- a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.main.iml +++ b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.test.iml b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.test.iml index 6e31e8d1..ce6aecab 100644 --- a/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.test.iml +++ b/.idea/modules/packages/shulker-sdk/bindings/java/shulker.packages.shulker-sdk.test.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.common.iml b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.common.iml index bc647473..1d05c50e 100644 --- a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.common.iml +++ b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.common.iml @@ -1,5 +1,5 @@ - + @@ -64,7 +64,7 @@ - + diff --git a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.iml b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.iml index b08a78a7..2e88936e 100644 --- a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.iml +++ b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.main.iml b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.main.iml index 2e14b8a7..14c02bd8 100644 --- a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.main.iml +++ b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.paper.iml b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.paper.iml index 536ad3f1..37f4fa94 100644 --- a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.paper.iml +++ b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.paper.iml @@ -1,5 +1,5 @@ - + @@ -103,7 +103,7 @@ - + diff --git a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.test.iml b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.test.iml index c168f15b..0657c66a 100644 --- a/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.test.iml +++ b/.idea/modules/packages/shulker-server-agent/shulker.packages.shulker-server-agent.test.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.iml b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.iml index 1d5a1ce4..5170ce49 100644 --- a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.iml +++ b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.main.iml b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.main.iml index 2f3884a1..05fe1795 100644 --- a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.main.iml +++ b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.test.iml b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.test.iml index 0a56b717..aa3b3abf 100644 --- a/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.test.iml +++ b/.idea/modules/packages/shulker-server-api/shulker.packages.shulker-server-api.test.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/packages/shulker.packages.iml b/.idea/modules/packages/shulker.packages.iml index 51c3b371..6ce413c8 100644 --- a/.idea/modules/packages/shulker.packages.iml +++ b/.idea/modules/packages/shulker.packages.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/shulker.iml b/.idea/modules/shulker.iml index b450d7ea..2cf47fc4 100644 --- a/.idea/modules/shulker.iml +++ b/.idea/modules/shulker.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/shulker.main.iml b/.idea/modules/shulker.main.iml index 271efd57..601e085f 100644 --- a/.idea/modules/shulker.main.iml +++ b/.idea/modules/shulker.main.iml @@ -1,5 +1,5 @@ - + diff --git a/.idea/modules/shulker.test.iml b/.idea/modules/shulker.test.iml index 2f9b9f91..70c487a5 100644 --- a/.idea/modules/shulker.test.iml +++ b/.idea/modules/shulker.test.iml @@ -1,5 +1,5 @@ - + diff --git a/build.gradle.kts b/build.gradle.kts index ca7e6a89..c9804a3b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,6 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import io.gitlab.arturbosch.detekt.Detekt -import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask import io.gitlab.arturbosch.detekt.report.ReportMergeTask -import org.jetbrains.kotlin.utils.addToStdlib.safeAs plugins { id("idea") @@ -96,13 +94,12 @@ subprojects { detekt { buildUponDefaultConfig = true ignoreFailures = true - baseline = file("$rootDir/gradle/detekt/baseline.xml") + config.setFrom("$rootDir/gradle/detekt-config.yml") + baseline = file("$rootDir/gradle/detekt-baseline.xml") basePath = rootDir.absolutePath } tasks.withType().configureEach { - jvmTarget = "1.8" - reports { sarif.required = true } @@ -110,10 +107,6 @@ subprojects { finalizedBy(detektReportMergeSarif) } - tasks.withType().configureEach { - jvmTarget = "1.8" - } - detektReportMergeSarif.configure { input.from(tasks.withType().map { it.sarifReportFile }) } @@ -189,7 +182,6 @@ subprojects { uri("https://maven.jeremylvln.fr/artifactory/shulker-releases") } - credentials { username = findProperty("artifactory.username")?.toString() ?: System.getenv("ARTIFACTORY_USERNAME") password = findProperty("artifactory.password")?.toString() ?: System.getenv("ARTIFACTORY_PASSWORD") diff --git a/docs/src/next/guide/recipes/understanding-lifecycle-strategies.md b/docs/src/next/guide/recipes/understanding-lifecycle-strategies.md new file mode 100644 index 00000000..fb5a9bd1 --- /dev/null +++ b/docs/src/next/guide/recipes/understanding-lifecycle-strategies.md @@ -0,0 +1,79 @@ +# Understanding Lifecycle Strategies + +Your proxies and servers lifecycles are _described_ by Shulker but +are actually managed by **Agones**. + +You may want to customize the lifecycle of your servers so Agones +does not disturb your players at innapropriate times. + +:::info EXAMPLE + +For instance, you may not want Agones to update your fleet of servers +because you upgraded a plugin while some players are in a mini-game. + +::: + +While Shulker's server agent marks your server as `Ready` when the +agent is fully loaded, you may want to also mark your server as +`Allocated` when an interrupted game session is needed (a mini-game +for instance). + +Shulker allows you to choose a **Lifecycle Strategy** that will +change the automatic behaviors of Agones. It can be changed on +the `MinecraftServer` and `MinecraftServerFleet`: + +```yaml +apiVersion: shulkermc.io/v1alpha1 +kind: MinecraftServerFleet +metadata: + name: dropper-game +spec: + clusterRef: + name: getting-started + replicas: 1 + template: + spec: + clusterRef: + name: getting-started + tags: + - lobby + version: + channel: Paper + name: '1.20.4' + config: // [!code focus] + lifecycleStrategy: AllocateWhenNotEmpty // [!code focus] +``` + +## `AllocateWhenNotEmpty` strategy + +With this strategy, Shulker's server agent will mark your server as +`Allocated` when at lease one player is connected on the server. + +This will disable any automatic reschedule of Agones that may be +due to a plugin upgrade. For your server to be updated, you'll have to +either: + +1. Have all the players disconnected for the server is set back to + `Ready` +2. Shutdown yourself the server (with the `/server` command for instance) + +:::info + +Shulker will still mark your server as `Ready` once the agent is +fully loaded. + +::: + +## `Manual` strategy + +With this strategy, no extra work is done apart marking the server as +`Ready` after loading. It is up to you and custom implementation to manage +the lifecycle of your server. + +:::warning + +Using this strategy with no custom implementation will keep the `Ready` +state forever. Thus, Agones will always think that the server is not used +and it may be recreated at any time for any reason. + +::: diff --git a/docs/src/next/sidebar.mts b/docs/src/next/sidebar.mts index b74f2780..8f7d7d56 100644 --- a/docs/src/next/sidebar.mts +++ b/docs/src/next/sidebar.mts @@ -52,6 +52,10 @@ export default [ text: 'Using custom server JAR', link: link('/guide/recipes/using-custom-server-jar'), }, + { + text: 'Understanding Lifecycle Strategy', + link: link('/guide/recipes/understanding-lifecycle-strategies'), + }, ], }, { diff --git a/examples/getting-started/minecraftserver.yaml b/examples/getting-started/minecraftserver.yaml index 21b106ef..765e5e46 100644 --- a/examples/getting-started/minecraftserver.yaml +++ b/examples/getting-started/minecraftserver.yaml @@ -14,5 +14,5 @@ spec: - lobby version: channel: Paper - name: "1.18.2" + name: "1.20.4" config: {} diff --git a/examples/getting-started/proxy.yaml b/examples/getting-started/proxy.yaml index 5073e0a5..0977dc92 100644 --- a/examples/getting-started/proxy.yaml +++ b/examples/getting-started/proxy.yaml @@ -7,8 +7,8 @@ spec: name: getting-started replicas: 1 service: - type: LoadBalancer - externalTrafficPolicy: Local + type: ClusterIP + # externalTrafficPolicy: Local template: spec: version: diff --git a/gradle/detekt-config.yml b/gradle/detekt-config.yml new file mode 100644 index 00000000..a702747d --- /dev/null +++ b/gradle/detekt-config.yml @@ -0,0 +1,16 @@ +complexity: + TooManyFunctions: + active: false + +naming: + MemberNameEqualsClassName: + excludes: + - "**/kubernetes/models/**" + +style: + MaxLineLength: + active: false + ForbiddenVoid: + ignoreOverridden: true + ReturnCount: + active: false diff --git a/packages/shulker-crds/src/v1alpha1/minecraft_server.rs b/packages/shulker-crds/src/v1alpha1/minecraft_server.rs index 852ead22..5801b755 100644 --- a/packages/shulker-crds/src/v1alpha1/minecraft_server.rs +++ b/packages/shulker-crds/src/v1alpha1/minecraft_server.rs @@ -114,6 +114,10 @@ pub struct MinecraftServerConfigurationSpec { /// this `MinecraftServer` #[serde(default)] pub proxy_forwarding_mode: MinecraftServerConfigurationProxyForwardingMode, + + /// Strategy to apply concerning Agones `GameServer` lifecycle management + #[serde(default)] + pub lifecycle_strategy: MinecraftServerConfigurationLifecycleStrategy, } #[cfg(not(tarpaulin_include))] @@ -140,6 +144,15 @@ pub enum MinecraftServerConfigurationProxyForwardingMode { BungeeCord, } +#[derive( + PartialEq, Deserialize, Serialize, Clone, Debug, Default, JsonSchema, IntoStaticStr, Display, +)] +pub enum MinecraftServerConfigurationLifecycleStrategy { + #[default] + AllocateWhenNotEmpty, + Manual, +} + #[derive(Deserialize, Serialize, Clone, Debug, Default, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct MinecraftServerPodOverridesSpec { diff --git a/packages/shulker-operator/src/reconcilers/minecraft_cluster/minecraft_server_role.rs b/packages/shulker-operator/src/reconcilers/minecraft_cluster/minecraft_server_role.rs index c47b66e6..03480b21 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_cluster/minecraft_server_role.rs +++ b/packages/shulker-operator/src/reconcilers/minecraft_cluster/minecraft_server_role.rs @@ -49,7 +49,10 @@ impl<'a> ResourceBuilder<'a> for MinecraftServerRoleBuilder { PolicyRule { api_groups: Some(vec!["".to_string()]), resources: Some(vec!["events".to_string()]), - verbs: vec!["create".to_string()], + verbs: vec![ + "create".to_string(), + "patch".to_string() + ], ..PolicyRule::default() }, PolicyRule { diff --git a/packages/shulker-operator/src/reconcilers/minecraft_cluster/snapshots/shulker_operator__reconcilers__minecraft_cluster__minecraft_server_role__tests__build_snapshot.snap b/packages/shulker-operator/src/reconcilers/minecraft_cluster/snapshots/shulker_operator__reconcilers__minecraft_cluster__minecraft_server_role__tests__build_snapshot.snap index ea604425..03f1284d 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_cluster/snapshots/shulker_operator__reconcilers__minecraft_cluster__minecraft_server_role__tests__build_snapshot.snap +++ b/packages/shulker-operator/src/reconcilers/minecraft_cluster/snapshots/shulker_operator__reconcilers__minecraft_cluster__minecraft_server_role__tests__build_snapshot.snap @@ -21,6 +21,7 @@ rules: - events verbs: - create + - patch - apiGroups: - agones.dev resources: diff --git a/packages/shulker-operator/src/reconcilers/minecraft_server/fixtures.rs b/packages/shulker-operator/src/reconcilers/minecraft_server/fixtures.rs index 36dcf30a..7455cdc1 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_server/fixtures.rs +++ b/packages/shulker-operator/src/reconcilers/minecraft_server/fixtures.rs @@ -10,9 +10,10 @@ use shulker_crds::{ v1alpha1::{ minecraft_cluster::MinecraftClusterRef, minecraft_server::{ - MinecraftServer, MinecraftServerConfigurationProxyForwardingMode, - MinecraftServerConfigurationSpec, MinecraftServerPodOverridesSpec, MinecraftServerSpec, - MinecraftServerVersion, MinecraftServerVersionSpec, + MinecraftServer, MinecraftServerConfigurationLifecycleStrategy, + MinecraftServerConfigurationProxyForwardingMode, MinecraftServerConfigurationSpec, + MinecraftServerPodOverridesSpec, MinecraftServerSpec, MinecraftServerVersion, + MinecraftServerVersionSpec, }, }, }; @@ -50,7 +51,9 @@ lazy_static! { disable_nether: false, disable_end: true, server_properties: None, - proxy_forwarding_mode: MinecraftServerConfigurationProxyForwardingMode::Velocity + proxy_forwarding_mode: MinecraftServerConfigurationProxyForwardingMode::Velocity, + lifecycle_strategy: + MinecraftServerConfigurationLifecycleStrategy::AllocateWhenNotEmpty }, pod_overrides: Some(MinecraftServerPodOverridesSpec { image: None, diff --git a/packages/shulker-operator/src/reconcilers/minecraft_server/gameserver.rs b/packages/shulker-operator/src/reconcilers/minecraft_server/gameserver.rs index 9b989096..eeebb288 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_server/gameserver.rs +++ b/packages/shulker-operator/src/reconcilers/minecraft_server/gameserver.rs @@ -430,6 +430,11 @@ impl<'a> GameServerBuilder { ), ..EnvVar::default() }, + EnvVar { + name: "SHULKER_SERVER_LIFECYCLE_STRATEGY".to_string(), + value: Some(minecraft_server.spec.config.lifecycle_strategy.to_string()), + ..EnvVar::default() + }, EnvVar { name: "EULA".to_string(), value: Some("TRUE".to_string()), diff --git a/packages/shulker-operator/src/reconcilers/minecraft_server/snapshots/shulker_operator__reconcilers__minecraft_server__gameserver__tests__build_snapshot.snap b/packages/shulker-operator/src/reconcilers/minecraft_server/snapshots/shulker_operator__reconcilers__minecraft_server__gameserver__tests__build_snapshot.snap index ada6f79e..507eccf8 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_server/snapshots/shulker_operator__reconcilers__minecraft_server__gameserver__tests__build_snapshot.snap +++ b/packages/shulker-operator/src/reconcilers/minecraft_server/snapshots/shulker_operator__reconcilers__minecraft_server__gameserver__tests__build_snapshot.snap @@ -50,6 +50,8 @@ spec: fieldPath: metadata.namespace - name: SHULKER_NETWORK_ADMINS value: "" + - name: SHULKER_SERVER_LIFECYCLE_STRATEGY + value: AllocateWhenNotEmpty - name: EULA value: "TRUE" - name: COPY_CONFIG_DEST diff --git a/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/fixtures.rs b/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/fixtures.rs index 552e7431..525d6122 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/fixtures.rs +++ b/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/fixtures.rs @@ -14,6 +14,7 @@ use shulker_crds::{ v1alpha1::{ minecraft_cluster::MinecraftClusterRef, minecraft_server::{ + MinecraftServerConfigurationLifecycleStrategy, MinecraftServerConfigurationProxyForwardingMode, MinecraftServerConfigurationSpec, MinecraftServerPodOverridesSpec, MinecraftServerSpec, MinecraftServerVersion, MinecraftServerVersionSpec, @@ -71,7 +72,9 @@ lazy_static! { disable_end: true, server_properties: None, proxy_forwarding_mode: - MinecraftServerConfigurationProxyForwardingMode::Velocity + MinecraftServerConfigurationProxyForwardingMode::Velocity, + lifecycle_strategy: + MinecraftServerConfigurationLifecycleStrategy::AllocateWhenNotEmpty }, pod_overrides: Some(MinecraftServerPodOverridesSpec { image: None, diff --git a/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/snapshots/shulker_operator__reconcilers__minecraft_server_fleet__fleet__tests__build_snapshot.snap b/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/snapshots/shulker_operator__reconcilers__minecraft_server_fleet__fleet__tests__build_snapshot.snap index ab5773b4..d7c5caba 100644 --- a/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/snapshots/shulker_operator__reconcilers__minecraft_server_fleet__fleet__tests__build_snapshot.snap +++ b/packages/shulker-operator/src/reconcilers/minecraft_server_fleet/snapshots/shulker_operator__reconcilers__minecraft_server_fleet__fleet__tests__build_snapshot.snap @@ -72,6 +72,8 @@ spec: fieldPath: metadata.namespace - name: SHULKER_NETWORK_ADMINS value: "" + - name: SHULKER_SERVER_LIFECYCLE_STRATEGY + value: AllocateWhenNotEmpty - name: EULA value: "TRUE" - name: COPY_CONFIG_DEST diff --git a/packages/shulker-proxy-agent/src/bungeecord/kotlin/io/shulkermc/proxyagent/bungeecord/ProxyInterfaceBungeeCord.kt b/packages/shulker-proxy-agent/src/bungeecord/kotlin/io/shulkermc/proxyagent/bungeecord/ProxyInterfaceBungeeCord.kt index a368f9c9..b9b7403e 100644 --- a/packages/shulker-proxy-agent/src/bungeecord/kotlin/io/shulkermc/proxyagent/bungeecord/ProxyInterfaceBungeeCord.kt +++ b/packages/shulker-proxy-agent/src/bungeecord/kotlin/io/shulkermc/proxyagent/bungeecord/ProxyInterfaceBungeeCord.kt @@ -31,6 +31,7 @@ import java.net.InetSocketAddress import java.util.UUID import java.util.concurrent.TimeUnit +@Suppress("TooManyFunctions") class ProxyInterfaceBungeeCord( private val plugin: Plugin, private val proxy: ProxyServer @@ -72,6 +73,7 @@ class ProxyInterfaceBungeeCord( val result = hook() if (!result.allowed) { + @Suppress("UnsafeCallOnNullableType") event.setCancelReason(*BungeeComponentSerializer.get().serialize(result.rejectComponent!!)) } } @@ -116,6 +118,7 @@ class ProxyInterfaceBungeeCord( val result = hook(wrapPlayer(event.player), event.target.name) if (result.newServerName.isPresent) { + @Suppress("UnsafeCallOnNullableType") event.target = proxy.servers[result.newServerName.get()]!! } } diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ProxyInterface.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ProxyInterface.kt index b91c49ef..736c78ba 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ProxyInterface.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ProxyInterface.kt @@ -11,6 +11,7 @@ import java.net.InetSocketAddress import java.util.UUID import java.util.concurrent.TimeUnit +@Suppress("TooManyFunctions") interface ProxyInterface { fun registerServer(name: String, address: InetSocketAddress) fun unregisterServer(name: String): Boolean diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt index ebcbbc5d..8794427d 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/ShulkerProxyAgentCommon.kt @@ -21,6 +21,7 @@ import io.shulkermc.proxyagent.tasks.HealthcheckTask import io.shulkermc.proxyagent.tasks.LostProxyPurgeTask import redis.clients.jedis.JedisPool import java.lang.Exception +import java.util.logging.Level import java.util.logging.Logger import kotlin.system.exitProcess @@ -40,6 +41,7 @@ class ShulkerProxyAgentCommon(val proxyInterface: ProxyInterface, val logger: Lo lateinit var playerMovementService: PlayerMovementService private lateinit var proxyLifecycleService: ProxyLifecycleService + // Tasks private lateinit var healthcheckTask: ProxyInterface.ScheduledTask private lateinit var lostProxyPurgeTask: ProxyInterface.ScheduledTask @@ -86,9 +88,8 @@ class ShulkerProxyAgentCommon(val proxyInterface: ProxyInterface, val logger: Lo this.cache.registerProxy(Configuration.PROXY_NAME) this.agonesGateway.setAllocated() - } catch (e: Exception) { - this.logger.severe("Shulker Agent crashed, stopping proxy") - e.printStackTrace() + } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { + this.logger.log(Level.SEVERE, "Shulker Agent crashed, stopping proxy", e) this.shutdown() } } @@ -109,8 +110,12 @@ class ShulkerProxyAgentCommon(val proxyInterface: ProxyInterface, val logger: Lo this.cache.unregisterProxy(Configuration.PROXY_NAME) this.pubSub.close() this.agonesGateway.askShutdown() - } catch (ex: Exception) { - this.logger.severe("Failed to ask Agones sidecar to shutdown properly, stopping process manually") + } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { + this.logger.log( + Level.SEVERE, + "Failed to ask Agones sidecar to shutdown properly, stopping process manually", + e + ) exitProcess(0) } } diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt index 480e233d..68c828ad 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/adapters/mojang/HttpMojangGatewayAdapter.kt @@ -8,13 +8,17 @@ import java.util.Optional import java.util.UUID class HttpMojangGatewayAdapter : MojangGatewayAdapter { + companion object { + private const val HTTP_OK = 200 + } + override fun getProfileFromName(playerName: String): Optional { val url = URI("https://api.mojang.com/users/profiles/minecraft/$playerName").toURL() val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" val status = connection.responseCode - if (status != 200) return Optional.empty() + if (status != HTTP_OK) return Optional.empty() val response = connection.inputStream.bufferedReader().use { it.readText() } val responseJson = JsonParser.parseString(response).asJsonObject @@ -28,7 +32,7 @@ class HttpMojangGatewayAdapter : MojangGatewayAdapter { connection.requestMethod = "GET" val status = connection.responseCode - if (status != 200) return Optional.empty() + if (status != HTTP_OK) return Optional.empty() val response = connection.inputStream.bufferedReader().use { it.readText() } val responseJson = JsonParser.parseString(response).asJsonObject diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/platform/HookPostOrder.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/platform/HookPostOrder.kt index 89d617d3..a6ad5335 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/platform/HookPostOrder.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/platform/HookPostOrder.kt @@ -1,5 +1,5 @@ package io.shulkermc.proxyagent.platform enum class HookPostOrder { - FIRST, EARLY, NORMAL, LATE, LAST + FIRST, EARLY, NORMAL, LATE, LAST, MONITOR } diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/services/ProxyLifecycleService.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/services/ProxyLifecycleService.kt index 53ebff98..540d763d 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/services/ProxyLifecycleService.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/services/ProxyLifecycleService.kt @@ -57,7 +57,7 @@ class ProxyLifecycleService(private val agent: ShulkerProxyAgentCommon) { this.agent.logger.info("Proxy is empty, stopping") this.agent.shutdown() } else { - this.agent.logger.info(String.format("There are still %d players connected, waiting", playerCount)) + this.agent.logger.info("There are still $playerCount players connected, waiting") } } } diff --git a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/tasks/LostProxyPurgeTask.kt b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/tasks/LostProxyPurgeTask.kt index 886c7888..1ba72092 100644 --- a/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/tasks/LostProxyPurgeTask.kt +++ b/packages/shulker-proxy-agent/src/common/kotlin/io/shulkermc/proxyagent/tasks/LostProxyPurgeTask.kt @@ -25,12 +25,12 @@ class LostProxyPurgeTask(private val agent: ShulkerProxyAgentCommon) : Runnable val maybeLock = this.agent.cache.tryLockLostProxiesPurgeTask(Configuration.PROXY_NAME) maybeLock.ifPresent { lock -> - lock.use { + lock.use { _ -> this.agent.cache.listRegisteredProxies() .filter { System.currentTimeMillis() - it.lastSeenMillis > PROXY_LOST_MILLIS_THRESHOLD } - .forEach { - this.agent.cache.unregisterProxy(it.proxyName) - this.agent.logger.info("Unregistered lost proxy ${it.proxyName}") + .forEach { proxy -> + this.agent.cache.unregisterProxy(proxy.proxyName) + this.agent.logger.info("Unregistered lost proxy ${proxy.proxyName}") } } } diff --git a/packages/shulker-proxy-agent/src/velocity/kotlin/io/shulkermc/proxyagent/velocity/ProxyInterfaceVelocity.kt b/packages/shulker-proxy-agent/src/velocity/kotlin/io/shulkermc/proxyagent/velocity/ProxyInterfaceVelocity.kt index 2ef0011f..decda2dd 100644 --- a/packages/shulker-proxy-agent/src/velocity/kotlin/io/shulkermc/proxyagent/velocity/ProxyInterfaceVelocity.kt +++ b/packages/shulker-proxy-agent/src/velocity/kotlin/io/shulkermc/proxyagent/velocity/ProxyInterfaceVelocity.kt @@ -26,6 +26,7 @@ import java.util.UUID import java.util.concurrent.TimeUnit import kotlin.jvm.optionals.getOrElse +@Suppress("TooManyFunctions") class ProxyInterfaceVelocity( private val plugin: ShulkerProxyAgentVelocity, private val proxy: ProxyServer @@ -185,6 +186,7 @@ class ProxyInterfaceVelocity( HookPostOrder.NORMAL -> PostOrder.NORMAL HookPostOrder.LATE -> PostOrder.LATE HookPostOrder.LAST -> PostOrder.LAST + HookPostOrder.MONITOR -> PostOrder.LAST } } diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/Configuration.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/Configuration.kt index 4c9cf3ef..1f9f8d04 100644 --- a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/Configuration.kt +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/Configuration.kt @@ -12,5 +12,22 @@ object Configuration { } .orElse(emptyList()) + val LIFECYCLE_STRATEGY: LifecycleStrategy = getOptionalStringEnv("SHULKER_SERVER_LIFECYCLE_STRATEGY") + .map { value -> LifecycleStrategy.byEnvValue(value) } + .orElse(LifecycleStrategy.ALLOCATE_WHEN_NOT_EMPTY) + private fun getOptionalStringEnv(name: String): Optional = Optional.ofNullable(System.getenv(name)) + + enum class LifecycleStrategy(private val strategy: String) { + ALLOCATE_WHEN_NOT_EMPTY("AllocateWhenNotEmpty"), + MANUAL("Manual"); + + companion object { + fun byEnvValue(value: String): LifecycleStrategy { + return requireNotNull(LifecycleStrategy.entries.find { it.strategy == value }) { + "Unknown lifecycle strategy: $value" + } + } + } + } } diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ServerInterface.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ServerInterface.kt index 418ffbbc..892f7be6 100644 --- a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ServerInterface.kt +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ServerInterface.kt @@ -1,11 +1,19 @@ package io.shulkermc.serveragent +import io.shulkermc.serveragent.platform.HookPostOrder +import io.shulkermc.serveragent.platform.PlayerDisconnectHook +import io.shulkermc.serveragent.platform.PlayerLoginHook import java.util.UUID import java.util.concurrent.TimeUnit interface ServerInterface { fun prepareNetworkAdminsPermissions(playerIds: List) + fun addPlayerJoinHook(hook: PlayerLoginHook, postOrder: HookPostOrder) + fun addPlayerQuitHook(hook: PlayerDisconnectHook, postOrder: HookPostOrder) + + fun getPlayerCount(): Int + fun scheduleDelayedTask(delay: Long, timeUnit: TimeUnit, runnable: Runnable): ScheduledTask fun scheduleRepeatingTask(delay: Long, interval: Long, timeUnit: TimeUnit, runnable: Runnable): ScheduledTask diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ShulkerServerAgentCommon.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ShulkerServerAgentCommon.kt index 7396182f..d7e3e99a 100644 --- a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ShulkerServerAgentCommon.kt +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/ShulkerServerAgentCommon.kt @@ -4,19 +4,26 @@ import com.agones.dev.sdk.AgonesSDK import com.agones.dev.sdk.AgonesSDKImpl import io.shulkermc.serveragent.api.ShulkerServerAPI import io.shulkermc.serveragent.api.ShulkerServerAPIImpl +import io.shulkermc.serveragent.services.PlayerMovementService import io.shulkermc.serveragent.tasks.HealthcheckTask import java.lang.Exception import java.util.concurrent.TimeUnit +import java.util.logging.Level import java.util.logging.Logger import kotlin.system.exitProcess -class ShulkerServerAgentCommon(val serverInterface: ServerInterface, private val logger: Logger) { +class ShulkerServerAgentCommon(val serverInterface: ServerInterface, val logger: Logger) { companion object { private const val SUMMON_LABEL_NAME = "shulkermc.io/summoned" private const val SUMMON_TIMEOUT_MINUTES = 5L } lateinit var agonesGateway: AgonesSDK + + // Services + lateinit var playerMovementService: PlayerMovementService + + // Tasks private lateinit var healthcheckTask: ServerInterface.ScheduledTask private var summonTimeoutTask: ServerInterface.ScheduledTask? = null @@ -31,12 +38,7 @@ class ShulkerServerAgentCommon(val serverInterface: ServerInterface, private val ShulkerServerAPI.INSTANCE = ShulkerServerAPIImpl(this) - if (gameServer.objectMeta.containsLabels(SUMMON_LABEL_NAME)) { - this.logger.info( - "This server was summoned manually, it will be shutdown automatically in $SUMMON_TIMEOUT_MINUTES minutes" // ktlint-disable standard_max-line-length - ) - this.summonTimeoutTask = this.createSummonTimeoutTask() - } + this.playerMovementService = PlayerMovementService(this) this.healthcheckTask = HealthcheckTask(this).schedule() @@ -47,10 +49,16 @@ class ShulkerServerAgentCommon(val serverInterface: ServerInterface, private val ) } + if (gameServer.objectMeta.containsLabels(SUMMON_LABEL_NAME)) { + this.logger.info( + "This server was summoned manually, it will be shutdown automatically in $SUMMON_TIMEOUT_MINUTES minutes" // ktlint-disable standard_max-line-length + ) + this.summonTimeoutTask = this.createSummonTimeoutTask() + } + this.agonesGateway.setReady() - } catch (e: Exception) { - this.logger.severe("Shulker Agent crashed, stopping server") - e.printStackTrace() + } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { + this.logger.log(Level.SEVERE, "Shulker Agent crashed, stopping server", e) this.shutdown() } } @@ -65,9 +73,12 @@ class ShulkerServerAgentCommon(val serverInterface: ServerInterface, private val fun shutdown() { try { this.agonesGateway.askShutdown() - } catch (ex: Exception) { - this.logger.severe("Failed to ask Agones sidecar to shutdown properly, stopping process manually") - ex.printStackTrace() + } catch (@Suppress("TooGenericExceptionCaught") e: Exception) { + this.logger.log( + Level.SEVERE, + "Failed to ask Agones sidecar to shutdown properly, stopping process manually", + e + ) exitProcess(0) } } diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/HookPostOrder.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/HookPostOrder.kt new file mode 100644 index 00000000..2bb29ad3 --- /dev/null +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/HookPostOrder.kt @@ -0,0 +1,5 @@ +package io.shulkermc.serveragent.platform + +enum class HookPostOrder { + FIRST, EARLY, NORMAL, LATE, LAST, MONITOR +} diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerDisconnectHook.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerDisconnectHook.kt new file mode 100644 index 00000000..4771e758 --- /dev/null +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerDisconnectHook.kt @@ -0,0 +1,3 @@ +package io.shulkermc.serveragent.platform + +typealias PlayerDisconnectHook = () -> Unit diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerLoginHook.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerLoginHook.kt new file mode 100644 index 00000000..b216be20 --- /dev/null +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/platform/PlayerLoginHook.kt @@ -0,0 +1,3 @@ +package io.shulkermc.serveragent.platform + +typealias PlayerLoginHook = () -> Unit diff --git a/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/services/PlayerMovementService.kt b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/services/PlayerMovementService.kt new file mode 100644 index 00000000..cea9e2d3 --- /dev/null +++ b/packages/shulker-server-agent/src/common/kotlin/io/shulkermc/serveragent/services/PlayerMovementService.kt @@ -0,0 +1,34 @@ +package io.shulkermc.serveragent.services + +import io.shulkermc.serveragent.Configuration +import io.shulkermc.serveragent.ShulkerServerAgentCommon +import io.shulkermc.serveragent.platform.HookPostOrder + +class PlayerMovementService(private val agent: ShulkerServerAgentCommon) { + init { + this.agent.serverInterface.addPlayerJoinHook(this::onPlayerJoin, HookPostOrder.MONITOR) + this.agent.serverInterface.addPlayerQuitHook(this::onPlayerQuit, HookPostOrder.MONITOR) + } + + private fun onPlayerJoin() { + this.updateAllocationState(triggerFromJoin = true) + } + + private fun onPlayerQuit() { + this.updateAllocationState(triggerFromJoin = false) + } + + private fun updateAllocationState(triggerFromJoin: Boolean) { + if (Configuration.LIFECYCLE_STRATEGY !== Configuration.LifecycleStrategy.ALLOCATE_WHEN_NOT_EMPTY) { + return + } + + val playerCount = this.agent.serverInterface.getPlayerCount() + + if (triggerFromJoin && playerCount == 1) { + this.agent.agonesGateway.setAllocated() + } else if (!triggerFromJoin && playerCount == 1) { + this.agent.agonesGateway.setReady() + } + } +} diff --git a/packages/shulker-server-agent/src/paper/kotlin/io/shulkermc/serveragent/paper/ServerInterfacePaper.kt b/packages/shulker-server-agent/src/paper/kotlin/io/shulkermc/serveragent/paper/ServerInterfacePaper.kt index 38fad97a..912c0f83 100644 --- a/packages/shulker-server-agent/src/paper/kotlin/io/shulkermc/serveragent/paper/ServerInterfacePaper.kt +++ b/packages/shulker-server-agent/src/paper/kotlin/io/shulkermc/serveragent/paper/ServerInterfacePaper.kt @@ -4,10 +4,16 @@ import io.shulkermc.serveragent.ServerInterface import io.shulkermc.serveragent.paper.scheduler.ServerScheduler import io.shulkermc.serveragent.paper.scheduler.ServerSchedulerFolia import io.shulkermc.serveragent.paper.scheduler.ServerSchedulerPaper -import org.bukkit.event.EventHandler +import io.shulkermc.serveragent.platform.HookPostOrder +import io.shulkermc.serveragent.platform.PlayerDisconnectHook +import io.shulkermc.serveragent.platform.PlayerLoginHook +import org.bukkit.event.Event import org.bukkit.event.EventPriority import org.bukkit.event.Listener +import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerLoginEvent +import org.bukkit.event.player.PlayerQuitEvent +import org.bukkit.plugin.EventExecutor import java.util.UUID import java.util.concurrent.TimeUnit @@ -23,6 +29,8 @@ class ServerInterfacePaper(private val plugin: ShulkerServerAgentPaper) : Server } } + private val eventListener = object : Listener {} + private val scheduler: ServerScheduler = if (isFoliaContext()) { ServerSchedulerFolia(this.plugin) } else { @@ -30,19 +38,29 @@ class ServerInterfacePaper(private val plugin: ShulkerServerAgentPaper) : Server } override fun prepareNetworkAdminsPermissions(playerIds: List) { - this.plugin.server.pluginManager.registerEvents( - object : Listener { - @EventHandler(priority = EventPriority.HIGHEST) - fun onPlayerLogin(event: PlayerLoginEvent) { - if (event.player.uniqueId in playerIds) { - event.player.isOp = true - // TODO: This may not be sufficient to give them all permissions - // but there is solution to grant all of them without knowing them - } - } - }, - this.plugin - ) + this.registerEventWithPriority(PlayerLoginEvent::class.java, HookPostOrder.FIRST) { event -> + if (event.player.uniqueId in playerIds) { + event.player.isOp = true + // TODO: This may not be sufficient to give them all permissions + // but there is solution to grant all of them without knowing them + } + } + } + + override fun addPlayerJoinHook(hook: PlayerLoginHook, postOrder: HookPostOrder) { + this.registerEventWithPriority(PlayerJoinEvent::class.java, postOrder) { + hook() + } + } + + override fun addPlayerQuitHook(hook: PlayerDisconnectHook, postOrder: HookPostOrder) { + this.registerEventWithPriority(PlayerQuitEvent::class.java, postOrder) { + hook() + } + } + + override fun getPlayerCount(): Int { + return this.plugin.server.onlinePlayers.size } override fun scheduleDelayedTask( @@ -57,4 +75,34 @@ class ServerInterfacePaper(private val plugin: ShulkerServerAgentPaper) : Server timeUnit: TimeUnit, runnable: Runnable ): ServerInterface.ScheduledTask = this.scheduler.scheduleRepeatingTask(delay, interval, timeUnit, runnable) + + private fun registerEventWithPriority( + clazz: Class, + postOrder: HookPostOrder, + callback: (event: T) -> Unit + ) { + val executor = EventExecutor { _, event -> + @Suppress("UNCHECKED_CAST") + callback(event as T) + } + + this.plugin.server.pluginManager.registerEvent( + clazz, + this.eventListener, + this.mapPostOrder(postOrder), + executor, + this.plugin + ) + } + + private fun mapPostOrder(postOrder: HookPostOrder): EventPriority { + return when (postOrder) { + HookPostOrder.FIRST -> EventPriority.LOWEST + HookPostOrder.EARLY -> EventPriority.LOW + HookPostOrder.NORMAL -> EventPriority.NORMAL + HookPostOrder.LATE -> EventPriority.HIGH + HookPostOrder.LAST -> EventPriority.HIGHEST + HookPostOrder.MONITOR -> EventPriority.MONITOR + } + } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 691672c5..0143b827 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,14 +28,14 @@ dependencyResolutionManagement { plugin("buildconfig", "com.github.gmazzo.buildconfig").version("4.1.2") plugin("shadow", "com.github.johnrengelman.shadow").version("8.1.0") plugin("ktlint", "org.jlleitschuh.gradle.ktlint").version("11.6.1") - plugin("detekt", "io.gitlab.arturbosch.detekt").version("1.23.1") + plugin("detekt", "io.gitlab.arturbosch.detekt").version("1.23.4") } } } fun includeBindingProject(name: String) { - include(":packages:${name}") - project(":packages:${name}").projectDir = file("packages/${name}/bindings/java") + include(":packages:$name") + project(":packages:$name").projectDir = file("packages/$name/bindings/java") } includeBindingProject("google-agones-sdk")