From b3facbb59a28773e66c394e620d1e554a4b19f21 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Thu, 15 Oct 2026 11:53:46 +0200 Subject: [PATCH] feat: add configurable stat synchronization (#33) (#44) Add /sharedgroup enable and disable to toggle health synchronization, stored in configuration.yml. Both commands are refused during a game. When health is disabled, damage, regeneration, lethal hits and deaths stay individual: SharedHealthService, the resurrect listener and the death fallback all check the toggle. Co-authored-by: AryanGupta833 <206203305+AryanGupta833@users.noreply.github.com> --- README.md | 2 + .../fr/korastrip/paperplugin/SharedStats.java | 15 ++-- .../paperplugin/SynchronizationConfig.java | 77 +++++++++++++++++++ .../commands/group/GroupCommand.java | 2 + .../commands/group/GroupSubCommand.java | 4 +- .../commands/group/sub/Disable.java | 55 +++++++++++++ .../commands/group/sub/Enable.java | 55 +++++++++++++ .../paperplugin/commands/group/sub/Help.java | 4 +- .../paperplugin/enums/ErrorMessage.java | 5 +- .../paperplugin/listener/DamageListener.java | 2 +- .../listener/EntityResurrectListener.java | 2 + .../listener/PlayerDeathListener.java | 2 +- .../listener/PlayerRespawnListener.java | 6 +- .../listener/RegenerationListener.java | 2 + .../services/SharedHealthService.java | 2 +- src/main/resources/configuration.yml | 4 + 16 files changed, 220 insertions(+), 19 deletions(-) create mode 100644 src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java create mode 100644 src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java create mode 100644 src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java create mode 100644 src/main/resources/configuration.yml diff --git a/README.md b/README.md index 495ad20..c503960 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ | `/sharedgroup stop` | Stop an active game without deleting the group | Group owner | | `/sharedgroup players` | List the online players in your group | Group member | | `/sharedgroup leaderboard` | Show the group's death leaderboard during a game | Group member | +| `/sharedgroup enable ` | Enable synchronization for a stat, outside a game | OP | +| `/sharedgroup disable ` | Disable synchronization for a stat, outside a game | OP | | `/sharedgroup version` | Display the plugin version | None | ## What remains to be done diff --git a/src/main/java/fr/korastrip/paperplugin/SharedStats.java b/src/main/java/fr/korastrip/paperplugin/SharedStats.java index aeffa18..7ea87e7 100644 --- a/src/main/java/fr/korastrip/paperplugin/SharedStats.java +++ b/src/main/java/fr/korastrip/paperplugin/SharedStats.java @@ -13,14 +13,18 @@ public final class SharedStats extends JavaPlugin implements Listener { private final SharedGroup group = new SharedGroup(); GroupCommand groupCommand = new GroupCommand(this); + private SynchronizationConfig synchronizationConfig; public SharedGroup getGroup() { return group; } + public SynchronizationConfig getSynchronizationConfig() { + return synchronizationConfig; + } @Override public void onEnable() { - + synchronizationConfig = new SynchronizationConfig(this); this.getServer().getPluginManager().registerEvents(this, this); this.getServer().getPluginManager().registerEvents(new DamageListener(this), this); this.getServer().getPluginManager().registerEvents(new PlayerQuitListener(this), this); @@ -34,13 +38,4 @@ public void onEnable() { event -> event.registrar().register(groupCommand.create()) ); } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - event.getPlayer().sendPlainMessage("Hello, " + event.getPlayer().getName()); - this.getLogger().info("Health: " + event.getPlayer().getHealth()); - this.getLogger().info("Food Level: " + event.getPlayer().getFoodLevel()); - this.getLogger().info("Saturation: " + event.getPlayer().getSaturation()); - } - } diff --git a/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java b/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java new file mode 100644 index 0000000..0f98c05 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/SynchronizationConfig.java @@ -0,0 +1,77 @@ +package fr.korastrip.paperplugin; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; + +public class SynchronizationConfig { + + private final SharedStats plugin; + private final File file; + private YamlConfiguration config; + + public SynchronizationConfig(SharedStats plugin) { + this.plugin = plugin; + this.file = new File(plugin.getDataFolder(), "configuration.yml"); + + if (!file.exists()) { + plugin.saveResource("configuration.yml", false); + } + + reload(); + } + + public void reload() { + config = YamlConfiguration.loadConfiguration(file); + + config.addDefault("synchronization.health", true); + config.addDefault("synchronization.food", true); + config.addDefault("synchronization.potions", true); + config.options().copyDefaults(true); + + try { + config.save(file); + } catch (IOException exception) { + plugin.getLogger().severe( + "Failed to save default configuration: " + exception.getMessage() + ); + } + } + + public boolean isEnabled(String stat) { + String key = getKey(stat); + return config.getBoolean(key); + } + + public boolean setEnabled(String stat, boolean enabled) { + String key = getKey(stat); + boolean previousValue = config.getBoolean(key); + + config.set(key, enabled); + + try { + config.save(file); + return true; + } catch (IOException exception) { + config.set(key, previousValue); + plugin.getLogger().severe( + "Failed to save configuration.yml: " + exception.getMessage() + ); + return false; + } + } + + private String getKey(String stat) { + String normalizedStat = stat.toLowerCase(Locale.ROOT); + + if (!normalizedStat.equals("health") + && !normalizedStat.equals("food") + && !normalizedStat.equals("potions")) { + throw new IllegalArgumentException("Unknown synchronization stat: " + stat); + } + + return "synchronization." + normalizedStat; + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java index f90d5f2..70c97ca 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java @@ -26,6 +26,8 @@ public LiteralCommandNode create() { .then(new Revoke(plugin).build()) .then(new Players(plugin).build()) .then(new Stats(plugin).build()) + .then(new Enable(plugin).build()) + .then(new Disable(plugin).build()) .build(); } diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java index 5e01e2d..d85bd54 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java @@ -45,8 +45,8 @@ public int generateError(String message, CommandSender player, TagResolver... re return 0; } - public int generateError(ErrorMessage error, CommandSender player) { - return this.generateError(error.getMessage(), player); + public int generateError(ErrorMessage error, CommandSender player, TagResolver... resolvers) { + return this.generateError(error.getMessage(), player, resolvers); } public String getUsage() { diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java new file mode 100644 index 0000000..fa6781f --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Disable.java @@ -0,0 +1,55 @@ + +package fr.korastrip.paperplugin.commands.group.sub; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; + +public class Disable extends GroupSubCommand { + + public Disable(SharedStats plugin) { + super(plugin, "disable", "Disable synchronization for a stat", "disable "); + } + + @Override + public LiteralArgumentBuilder build() { + return Commands.literal("disable") + .requires(source -> source.getSender().isOp()) + .then(Commands.literal("health") + .executes(context -> disable("health", context.getSource()))) + .then(Commands.literal("food") + .executes(context -> disable("food", context.getSource()))) + .then(Commands.literal("potions") + .executes(context -> disable("potions", context.getSource()))); + } + + private int disable(String stat, CommandSourceStack source) { + var config = plugin.getSynchronizationConfig(); + var group = plugin.getGroup(); + + if (source.getSender() instanceof Player player) { + + if (group.getIsPlaying()) { + return this.generateError(ErrorMessage.GAME_RUNNING, player); + } + + if (!config.isEnabled(stat)) { + return this.generateError(ErrorMessage.SYNC_ALREADY_DISABLED, player, Placeholder.unparsed("stat", stat)); + } + + if (!config.setEnabled(stat, false)) { + return this.generateError(ErrorMessage.CONFIG_SAVE_FAILED, player); + } + + player.sendRichMessage("Disabled " + stat + " synchronization."); + return Command.SINGLE_SUCCESS; + } + return 0; + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java new file mode 100644 index 0000000..d3d40c1 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Enable.java @@ -0,0 +1,55 @@ + +package fr.korastrip.paperplugin.commands.group.sub; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import io.papermc.paper.command.brigadier.Commands; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.entity.Player; + +public class Enable extends GroupSubCommand { + public Enable(SharedStats plugin) { + super(plugin, "enable", "Enable synchronization for a stat", "enable "); + } + + @Override + public LiteralArgumentBuilder build() { + return Commands.literal("enable") + .requires(source -> source.getSender().isOp()) + .then(Commands.literal("health") + .executes(context -> enable("health", context.getSource()))) + .then(Commands.literal("food") + .executes(context -> enable("food", context.getSource()))) + .then(Commands.literal("potions") + .executes(context -> enable("potions", context.getSource()))); + } + + private int enable(String stat, CommandSourceStack source) { + var config = plugin.getSynchronizationConfig(); + var group = plugin.getGroup(); + + if (source.getSender() instanceof Player player) { + + if (group.getIsPlaying()) { + return this.generateError(ErrorMessage.GAME_RUNNING, player); + } + + if (config.isEnabled(stat)) { + return this.generateError(ErrorMessage.SYNC_ALREADY_ENABLED, player, Placeholder.unparsed("stat", stat)); + } + + if (!config.setEnabled(stat, true)) { + return this.generateError(ErrorMessage.CONFIG_SAVE_FAILED, player); + } + + player.sendRichMessage("Enabled " + stat + " synchronization."); + return Command.SINGLE_SUCCESS; + } + return 0; + } + +} diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java index e7a0330..b7dc988 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Help.java @@ -37,7 +37,9 @@ public LiteralArgumentBuilder build() { new Stop(plugin), new Players(plugin), new Version(plugin), - new Stats(plugin) + new Stats(plugin), + new Enable(plugin), + new Disable(plugin) ); commands.forEach(command -> ctx.sender().sendRichMessage(": ", Placeholder.unparsed("description", command.getDescription()), Placeholder.unparsed("usage", command.getUsage()))); diff --git a/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java b/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java index 73036a7..0c7fb8c 100644 --- a/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java +++ b/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java @@ -13,7 +13,10 @@ public enum ErrorMessage { PLAYER_ALREADY_IN_GROUP("This player is already in the group."), PLAYER_NOT_IN_GROUP("This player is not in the group."), CANNOT_REMOVE_YOURSELF("You cannot remove yourself. Use /" + Config.PREFIX + " revoke instead."), - NOT_ENOUGH_PLAYERS("You cannot start a game alone. Invite at least one player."); + NOT_ENOUGH_PLAYERS("You cannot start a game alone. Invite at least one player."), + SYNC_ALREADY_ENABLED(" synchronization is already enabled."), + SYNC_ALREADY_DISABLED(" synchronization is already disabled."), + CONFIG_SAVE_FAILED("Could not save the configuration."); private final String message; diff --git a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java index eadc2d4..434d510 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java @@ -23,7 +23,7 @@ public void onDamageReceive(EntityDamageEvent event) { if (event.getEntity() instanceof Player player) { SharedGroup group = this.plugin.getGroup(); - + if (!plugin.getSynchronizationConfig().isEnabled("health")) return; if (!group.getIsPlaying()) return; if (!group.getPlayersId().contains(player.getUniqueId())) return; diff --git a/src/main/java/fr/korastrip/paperplugin/listener/EntityResurrectListener.java b/src/main/java/fr/korastrip/paperplugin/listener/EntityResurrectListener.java index 4ad934f..27a658a 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/EntityResurrectListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/EntityResurrectListener.java @@ -20,6 +20,8 @@ public EntityResurrectListener(SharedStats plugin) { @EventHandler(priority = EventPriority.HIGHEST) public void onEntityResurrect(EntityResurrectEvent e) { + if(!this.plugin.getSynchronizationConfig().isEnabled("health")) return; + if (e.getEntity() instanceof Player player) { TotemService totemService = new TotemService(this.plugin); diff --git a/src/main/java/fr/korastrip/paperplugin/listener/PlayerDeathListener.java b/src/main/java/fr/korastrip/paperplugin/listener/PlayerDeathListener.java index c12b39c..651e6db 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/PlayerDeathListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/PlayerDeathListener.java @@ -25,7 +25,7 @@ public void onPlayerDeath(PlayerDeathEvent event) { if (!group.getIsPlaying()) return; if (!group.getPlayersId().contains(player.getUniqueId())) return; - if(!group.isGroupDying) { + if(!group.isGroupDying && this.plugin.getSynchronizationConfig().isEnabled("health")) { SharedHealthService sharedHealthService = new SharedHealthService(this.plugin); sharedHealthService.groupDeath(player); } diff --git a/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java b/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java index f508857..1595c64 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/PlayerRespawnListener.java @@ -28,8 +28,10 @@ public void onPlayerRespawn(PlayerRespawnEvent event) { plugin, () -> { if (group.getOnlinePlayers().size() == group.getRespawnedPlayers().size()) { - group.setHealth(20); - group.syncHealth(); + if (plugin.getSynchronizationConfig().isEnabled("health")) { + group.setHealth(20); + group.syncHealth(); + } group.clearRespawnedPlayers(); group.isGroupDying = false; } diff --git a/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java b/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java index c801ec5..dfffe1e 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/RegenerationListener.java @@ -20,6 +20,8 @@ public RegenerationListener(SharedStats plugin) { public void onRegeneration(EntityRegainHealthEvent event) { SharedGroup group = this.plugin.getGroup(); + if (!plugin.getSynchronizationConfig().isEnabled("health")) return; + if (event.getEntity() instanceof Player player) { if(group.getPlayersId().contains(player.getUniqueId()) && group.getIsPlaying()) { diff --git a/src/main/java/fr/korastrip/paperplugin/services/SharedHealthService.java b/src/main/java/fr/korastrip/paperplugin/services/SharedHealthService.java index 336d689..cf38dc7 100644 --- a/src/main/java/fr/korastrip/paperplugin/services/SharedHealthService.java +++ b/src/main/java/fr/korastrip/paperplugin/services/SharedHealthService.java @@ -56,7 +56,7 @@ public void groupDeath(Player deathPlayer) { public void shareLoss() { SharedGroup group = this.plugin.getGroup(); - + if(!plugin.getSynchronizationConfig().isEnabled("health")) return; if (!group.getIsPlaying() || group.isGroupDying) return; double totalLost = 0; diff --git a/src/main/resources/configuration.yml b/src/main/resources/configuration.yml new file mode 100644 index 0000000..1a243ce --- /dev/null +++ b/src/main/resources/configuration.yml @@ -0,0 +1,4 @@ +synchronization: + health: true + food: true + potions: true \ No newline at end of file