From d2b206f7f25e615673500175e09cb7c3140c5b81 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 12:09:23 +0000 Subject: [PATCH 1/2] Add basics-god module with /god Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EE9ZFuiZvhoWuGuZx6MPxV --- modules/basics-god/README.md | 7 ++ modules/basics-god/build.gradle.kts | 3 + .../modules/basicsgod/BasicsGodModule.kt | 97 +++++++++++++++++++ .../src/main/resources/basics-module.yml | 3 + .../src/main/resources/messages.yml | 4 + 5 files changed, 114 insertions(+) create mode 100644 modules/basics-god/README.md create mode 100644 modules/basics-god/build.gradle.kts create mode 100644 modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt create mode 100644 modules/basics-god/src/main/resources/basics-module.yml create mode 100644 modules/basics-god/src/main/resources/messages.yml diff --git a/modules/basics-god/README.md b/modules/basics-god/README.md new file mode 100644 index 00000000..6541a92f --- /dev/null +++ b/modules/basics-god/README.md @@ -0,0 +1,7 @@ +| Command | Permission | +|-----------------|-------------------| +| `/god` | basics.god | +| `/god ` | basics.god.others | + +> [!TIP] +> Players in god mode take no damage and don't get hungry, until they log out. diff --git a/modules/basics-god/build.gradle.kts b/modules/basics-god/build.gradle.kts new file mode 100644 index 00000000..c1fb740e --- /dev/null +++ b/modules/basics-god/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("basics.module") +} diff --git a/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt b/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt new file mode 100644 index 00000000..a1eeee5a --- /dev/null +++ b/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt @@ -0,0 +1,97 @@ +package com.github.spigotbasics.modules.basicsgod + +import com.github.spigotbasics.core.command.parsed.CommandContextExecutor +import com.github.spigotbasics.core.command.parsed.arguments.SelectorSinglePlayerArg +import com.github.spigotbasics.core.command.parsed.context.MapContext +import com.github.spigotbasics.core.module.AbstractBasicsModule +import com.github.spigotbasics.core.module.loader.ModuleInstantiationContext +import org.bukkit.command.CommandSender +import org.bukkit.entity.Player +import org.bukkit.event.EventPriority +import org.bukkit.event.entity.EntityDamageEvent +import org.bukkit.event.entity.FoodLevelChangeEvent +import org.bukkit.event.player.PlayerQuitEvent +import java.util.UUID + +class BasicsGodModule(context: ModuleInstantiationContext) : AbstractBasicsModule(context) { + private val permission = + permissionManager.createSimplePermission( + "basics.god", + "Allows the player to toggle god mode", + ) + private val permissionOthers = + permissionManager.createSimplePermission( + "basics.god.others", + "Allows the player to toggle god mode for other players", + ) + + private val msgEnabled + get() = messages.getMessage("god-enabled") + private val msgDisabled + get() = messages.getMessage("god-disabled") + + private fun msgEnabledOthers(player: Player) = messages.getMessage("god-enabled-others").concerns(player) + + private fun msgDisabledOthers(player: Player) = messages.getMessage("god-disabled-others").concerns(player) + + private val godPlayers = mutableSetOf() + + override fun onEnable() { + eventBus.subscribe(EntityDamageEvent::class.java, ::onDamage, EventPriority.HIGH) + eventBus.subscribe(FoodLevelChangeEvent::class.java, ::onFoodLevelChange, EventPriority.HIGH) + eventBus.subscribe(PlayerQuitEvent::class.java) { godPlayers.remove(it.player.uniqueId) } + + commandFactory.parsedCommandBuilder("god", permission).mapContext { + usage = "[player]" + description("Toggles god mode") + + path { + playerOnly() + } + + path { + permissions(permissionOthers) + arguments { + named("player", SelectorSinglePlayerArg("player")) + } + } + }.executor( + object : CommandContextExecutor { + override fun execute( + sender: CommandSender, + context: MapContext, + ) { + val target = context["player"] as Player? ?: sender as Player + val enabled = !godPlayers.remove(target.uniqueId) + if (enabled) { + godPlayers.add(target.uniqueId) + target.fireTicks = 0 + } + + val message = + when { + enabled && sender == target -> msgEnabled + !enabled && sender == target -> msgDisabled + enabled -> msgEnabledOthers(target) + else -> msgDisabledOthers(target) + } + message.sendToSender(sender) + } + }, + ).register() + } + + override fun onDisable() { + godPlayers.clear() + } + + private fun onDamage(event: EntityDamageEvent) { + val player = event.entity as? Player ?: return + if (player.uniqueId in godPlayers) event.isCancelled = true + } + + private fun onFoodLevelChange(event: FoodLevelChangeEvent) { + val player = event.entity as? Player ?: return + if (player.uniqueId in godPlayers && event.foodLevel < player.foodLevel) event.isCancelled = true + } +} diff --git a/modules/basics-god/src/main/resources/basics-module.yml b/modules/basics-god/src/main/resources/basics-module.yml new file mode 100644 index 00000000..880e42d5 --- /dev/null +++ b/modules/basics-god/src/main/resources/basics-module.yml @@ -0,0 +1,3 @@ +main-class: com.github.spigotbasics.modules.basicsgod.BasicsGodModule +name: basics-god +version: ${moduleVersion} diff --git a/modules/basics-god/src/main/resources/messages.yml b/modules/basics-god/src/main/resources/messages.yml new file mode 100644 index 00000000..f7d4aaa9 --- /dev/null +++ b/modules/basics-god/src/main/resources/messages.yml @@ -0,0 +1,4 @@ +god-enabled: "God mode enabled." +god-disabled: "God mode disabled." +god-enabled-others: "Enabled god mode for ." +god-disabled-others: "Disabled god mode for ." From 46918d28402304c30b81b8932b71955da6d18e04 Mon Sep 17 00:00:00 2001 From: JanTuck Date: Wed, 14 Oct 2026 17:53:25 +0200 Subject: [PATCH 2/2] Persist god mode in module storage God mode is now loaded and saved through the player data lifecycle, so it survives disconnects and restarts. Only changes are written: enabling stores an entry and disabling removes it. --- modules/basics-god/README.md | 2 +- .../modules/basicsgod/BasicsGodModule.kt | 31 ++++++++++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/basics-god/README.md b/modules/basics-god/README.md index 6541a92f..d6fcf128 100644 --- a/modules/basics-god/README.md +++ b/modules/basics-god/README.md @@ -4,4 +4,4 @@ | `/god ` | basics.god.others | > [!TIP] -> Players in god mode take no damage and don't get hungry, until they log out. +> Players in god mode take no damage and don't get hungry. God mode is saved, so it stays on across relogs and restarts. diff --git a/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt b/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt index a1eeee5a..a9fd44f5 100644 --- a/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt +++ b/modules/basics-god/src/main/kotlin/com/github/spigotbasics/modules/basicsgod/BasicsGodModule.kt @@ -5,13 +5,16 @@ import com.github.spigotbasics.core.command.parsed.arguments.SelectorSinglePlaye import com.github.spigotbasics.core.command.parsed.context.MapContext import com.github.spigotbasics.core.module.AbstractBasicsModule import com.github.spigotbasics.core.module.loader.ModuleInstantiationContext +import com.github.spigotbasics.core.storage.NamespacedStorage +import com.google.gson.JsonPrimitive import org.bukkit.command.CommandSender import org.bukkit.entity.Player import org.bukkit.event.EventPriority import org.bukkit.event.entity.EntityDamageEvent import org.bukkit.event.entity.FoodLevelChangeEvent -import org.bukkit.event.player.PlayerQuitEvent import java.util.UUID +import java.util.concurrent.CompletableFuture +import java.util.concurrent.ConcurrentHashMap class BasicsGodModule(context: ModuleInstantiationContext) : AbstractBasicsModule(context) { private val permission = @@ -34,12 +37,15 @@ class BasicsGodModule(context: ModuleInstantiationContext) : AbstractBasicsModul private fun msgDisabledOthers(player: Player) = messages.getMessage("god-disabled-others").concerns(player) - private val godPlayers = mutableSetOf() + private lateinit var storage: NamespacedStorage + private val godPlayers = ConcurrentHashMap.newKeySet() + private val storedPlayers = ConcurrentHashMap.newKeySet() // Players with god mode saved in storage override fun onEnable() { + storage = createStorage() + eventBus.subscribe(EntityDamageEvent::class.java, ::onDamage, EventPriority.HIGH) eventBus.subscribe(FoodLevelChangeEvent::class.java, ::onFoodLevelChange, EventPriority.HIGH) - eventBus.subscribe(PlayerQuitEvent::class.java) { godPlayers.remove(it.player.uniqueId) } commandFactory.parsedCommandBuilder("god", permission).mapContext { usage = "[player]" @@ -81,8 +87,23 @@ class BasicsGodModule(context: ModuleInstantiationContext) : AbstractBasicsModul ).register() } - override fun onDisable() { - godPlayers.clear() + override fun loadPlayerData(uuid: UUID): CompletableFuture = + storage.getJsonElement(uuid.toString()).thenAccept { json -> + if (json != null) { + storedPlayers += uuid + godPlayers += uuid + } + } + + override fun savePlayerData(uuid: UUID): CompletableFuture { + val god = uuid in godPlayers + if (god == (uuid in storedPlayers)) return CompletableFuture.completedFuture(null) + return storage.setJsonElement(uuid.toString(), if (god) JsonPrimitive(true) else null) + } + + override fun forgetPlayerData(uuid: UUID) { + godPlayers -= uuid + storedPlayers -= uuid } private fun onDamage(event: EntityDamageEvent) {