From 350e8f4ce32ce7e2a89767a625598b132b88e1a8 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Mon, 12 Oct 2026 19:08:16 +0200 Subject: [PATCH 1/9] docs: add CurseForge and Modrinth badges (#20) --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 2bdda5b..a48244c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@

SharedStats

Share your health and food level with the players in your group.

+

+ Available on CurseForge + Available on Modrinth +

## Commands From 923aef6ed89a6d09df3f8b00a7617d1439b790ac Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:57:25 +0200 Subject: [PATCH 2/9] fix: when a user has a totem in the group has the group is revived (#27) * feat: add a new totem service that manage when a user has a totem in his inventory * feat: add support to totemservice to cancel the players death if someone has a totem * feat: add utils static class with a shorted method to convert seconds to tick --- .../java/fr/korastrip/paperplugin/Utils.java | 14 +++ .../paperplugin/listener/DamageListener.java | 9 ++ .../paperplugin/services/TotemService.java | 90 +++++++++++++++++++ 3 files changed, 113 insertions(+) create mode 100644 src/main/java/fr/korastrip/paperplugin/Utils.java create mode 100644 src/main/java/fr/korastrip/paperplugin/services/TotemService.java diff --git a/src/main/java/fr/korastrip/paperplugin/Utils.java b/src/main/java/fr/korastrip/paperplugin/Utils.java new file mode 100644 index 0000000..7717542 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/Utils.java @@ -0,0 +1,14 @@ +package fr.korastrip.paperplugin; + +import io.papermc.paper.util.Tick; + +import java.time.Duration; + +public class Utils +{ + + public static int toTick(int seconds) { + return Tick.tick().fromDuration(Duration.ofSeconds(seconds)); + } + +} diff --git a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java index f40f1e1..bb71e4a 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java @@ -2,6 +2,7 @@ import fr.korastrip.paperplugin.SharedStats; import fr.korastrip.paperplugin.model.SharedGroup; +import fr.korastrip.paperplugin.services.TotemService; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.title.Title; @@ -43,6 +44,14 @@ public void onDamageReceive(EntityDamageEvent event) { List players = group.getOnlinePlayers(); if (newHealth <= 0) { + + TotemService totemService = new TotemService(plugin); + + if(totemService.check()) { + totemService.applyEffect(); + return; + } + group.isGroupDying = true; Bukkit.getScheduler().runTask( diff --git a/src/main/java/fr/korastrip/paperplugin/services/TotemService.java b/src/main/java/fr/korastrip/paperplugin/services/TotemService.java new file mode 100644 index 0000000..1bdaf9d --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/services/TotemService.java @@ -0,0 +1,90 @@ +package fr.korastrip.paperplugin.services; + +import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.Utils; +import fr.korastrip.paperplugin.model.SharedGroup; +import org.bukkit.EntityEffect; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.logging.Level; + +public class TotemService { + + private final SharedStats plugin; + + public TotemService(SharedStats plugin) { + this.plugin = plugin; + } + + public boolean check() { + SharedGroup group = this.plugin.getGroup(); + + for (Player player : group.getOnlinePlayers()) { + if (this.removeTotemFromUser(player)) { + return true; + } + } + return false; + } + + public void applyEffect() { + SharedGroup group = this.plugin.getGroup(); + + group.setHealth(1); + group.syncHealth(); + + group.getOnlinePlayers().forEach(p -> { + + //Apply the same effect as vanilla + p.clearActivePotionEffects(); + + p.playSound(p.getLocation(), Sound.ITEM_TOTEM_USE, 1f, 1f); + p.playEffect(EntityEffect.PROTECTED_FROM_DEATH); + p.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Utils.toTick(45), 1)); + p.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, Utils.toTick(40), 0)); + p.addPotionEffect(new PotionEffect(PotionEffectType.ABSORPTION, Utils.toTick(5), 1)); + }); + } + + private boolean removeTotemFromUser(Player player) { + + PlayerInventory playerInventory = player.getInventory(); + int slotID = -1; + for (int i = 0; i < playerInventory.getSize(); i++) { + ItemStack item = playerInventory.getItem(i); + if (item != null && item.getType() == Material.TOTEM_OF_UNDYING) { + slotID = i; + break; + } + } + + if (slotID == -1) { + this.plugin.getLogger().log(Level.FINE, "No totem find on this player"); + return false; + } + + + ItemStack item = playerInventory.getItem(slotID); + if (item == null) return false; + + int itemAmount = item.getAmount(); + if (itemAmount < 1) return false; + + if (itemAmount == 1) { + playerInventory.setItem(slotID, null); + } else { + item.setAmount(itemAmount - 1); + playerInventory.setItem(slotID, item); + } + + + return true; + } + +} From 6d1387bd718b3eafeec3caedcca14d50c5426a03 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:05:35 +0200 Subject: [PATCH 3/9] refactor(group-command): correct command sequence (#30) --- .../fr/korastrip/paperplugin/commands/group/GroupCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 4eec59f..c59f097 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java @@ -23,7 +23,7 @@ public LiteralCommandNode create() { .then(new Stop(plugin).build()) .then(new Version(plugin).build()) .then(new Remove(plugin).build()) - .then(new Remove(plugin).build()) + .then(new Revoke(plugin).build()) .then(new Players(plugin).build()) .build(); From dd7de436cdc3b732e7dbb4e0f0899e40efda947a Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:46:58 +0200 Subject: [PATCH 4/9] feat(subcommand): add new method to create error (#31) --- .../paperplugin/commands/group/GroupSubCommand.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 f0c85e7..2980429 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java @@ -4,6 +4,8 @@ import fr.korastrip.paperplugin.Config; import fr.korastrip.paperplugin.SharedStats; import io.papermc.paper.command.brigadier.CommandSourceStack; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.command.CommandSender; public abstract class GroupSubCommand { protected final SharedStats plugin; @@ -28,6 +30,11 @@ public String getDescription() { return description; } + public int generateError(String message, CommandSender player) { + player.sendRichMessage("", Placeholder.unparsed("message", message)); + return 0; + } + public String getUsage() { return usage; } From b9afaf8822f10217c95dd2f2b690f207e350133f Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 16:48:13 +0200 Subject: [PATCH 5/9] feat: add a new death leaderboard and /sharedgroup leaderboard command (#29) * feat: add an leaderboard and method to update and read it * feat(commands): add new stats command to see, what user is the most dead users * feat(help): add stats command to help menu * feat(commands): add commands to minecraft commands * feat(listener): increment player death count on death --- .../commands/group/GroupCommand.java | 1 + .../paperplugin/commands/group/sub/Help.java | 3 +- .../paperplugin/commands/group/sub/Stats.java | 51 +++++++++++++++++++ .../paperplugin/listener/DamageListener.java | 2 + .../paperplugin/model/SharedGroup.java | 27 +++++++++- 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java 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 c59f097..f90d5f2 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupCommand.java @@ -25,6 +25,7 @@ public LiteralCommandNode create() { .then(new Remove(plugin).build()) .then(new Revoke(plugin).build()) .then(new Players(plugin).build()) + .then(new Stats(plugin).build()) .build(); } 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 36a95d2..067f6e5 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 @@ -39,7 +39,8 @@ public LiteralArgumentBuilder build() { new Start(plugin), new Stop(plugin), new Players(plugin), - new Version(plugin) + new Version(plugin), + new Stats(plugin) ); commands.forEach(command -> sender.sendRichMessage(": ", Placeholder.unparsed("description", command.getDescription()), Placeholder.unparsed("usage", command.getUsage()))); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java new file mode 100644 index 0000000..9f369e5 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java @@ -0,0 +1,51 @@ +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.model.SharedGroup; +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.Entity; +import org.bukkit.entity.Player; + +import java.util.Comparator; + +public class Stats extends GroupSubCommand { + + public Stats(SharedStats plugin) { + super(plugin, "leaderboard", "Get the group death leaderboards.", ""); + } + + @Override + public LiteralArgumentBuilder build() { + return Commands.literal("leaderboard").executes(context -> { + + Entity executor = context.getSource().getExecutor(); + SharedGroup group = this.plugin.getGroup(); + + if (!(executor instanceof Player player)) { + return 0; + } + + if(!group.getPlayersId().contains(player.getUniqueId())) { + return this.generateError("You do not have permission to use this command.", player); + } + + if(!group.getIsPlaying()) { + return this.generateError("Currently there not game launched.", player); + } + + var playerPerScore = group.getOnlinePlayers().stream().sorted(Comparator.comparingInt(group::getPlayerDeaths).reversed()).toList(); + + player.sendRichMessage("Group Leaderboard: \nYour team got death.", Placeholder.unparsed("totaldeath", String.valueOf(group.getDeadCount()))); + playerPerScore.forEach(p -> { + player.sendRichMessage(" got death", Placeholder.component("player", p.displayName()), Placeholder.unparsed("death", String.valueOf(group.getPlayerDeaths(p)))); + }); + + return Command.SINGLE_SUCCESS; + }); + } +} diff --git a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java index bb71e4a..938e98c 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java @@ -68,6 +68,8 @@ public void onDamageReceive(EntityDamageEvent event) { final Component subtitle = Component.text("is dead", NamedTextColor.WHITE); final Title title = Title.title(mainTitle, subtitle); + group.addPlayerDeath(player); + players.forEach(p -> { p.showTitle(title); diff --git a/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java b/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java index af719bb..f71ad9d 100644 --- a/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java +++ b/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java @@ -8,28 +8,36 @@ public class SharedGroup { private final Set playersId = new HashSet<>(); + public Set getPlayersId() { return Collections.unmodifiableSet(this.playersId); } + private final HashMap deathLeaderBoard = new HashMap<>(); + private UUID ownerUuid; + public void setOwnerUuid(UUID ownerUuid) { this.ownerUuid = ownerUuid; } + public UUID getOwnerUuid() { return this.ownerUuid; } private boolean isPlaying = false; + public boolean getIsPlaying() { return this.isPlaying; } private double groupHealth = 20.0; + public void setHealth(double health) { this.groupHealth = health; } + public double getHealth() { return this.groupHealth; } @@ -37,17 +45,21 @@ public double getHealth() { private double groupFood = 20.0; private int deadCount = 0; + public int getDeadCount() { return this.deadCount; } private final Set respawnedPlayers = new HashSet<>(); + public void addRespawnedPlayer(UUID playerId) { this.respawnedPlayers.add(playerId); } + public Set getRespawnedPlayers() { return Collections.unmodifiableSet(this.respawnedPlayers); } + public void clearRespawnedPlayers() { this.respawnedPlayers.clear(); } @@ -64,10 +76,11 @@ public void addPlayer(UUID uuid) { public void removePlayer(UUID uuid) { this.playersId.remove(uuid); + this.deathLeaderBoard.remove(uuid); } public void startGame() { - if(this.isPlaying) { + if (this.isPlaying) { throw new IllegalStateException(); } @@ -75,7 +88,7 @@ public void startGame() { } public void incrementsDeadCount() { - if(this.isPlaying) this.deadCount++; + if (this.isPlaying) this.deadCount++; } public void resetGroup() { @@ -89,6 +102,7 @@ public void resetStats() { this.isGroupDying = false; this.isPlaying = false; this.respawnedPlayers.clear(); + this.deathLeaderBoard.clear(); } public List getOnlinePlayers() { @@ -104,4 +118,13 @@ public void syncHealth() { }); } + public void addPlayerDeath(Player player) { + UUID playerUUID = player.getUniqueId(); + deathLeaderBoard.merge(playerUUID, 1, Integer::sum); + } + + public int getPlayerDeaths(Player player) { + return deathLeaderBoard.getOrDefault(player.getUniqueId(), 0); + } + } From 394081b3c7337bd5c4e4b3912ab29c3202d812e0 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 17:20:31 +0200 Subject: [PATCH 6/9] fix(knockback): fix vanilla work again now (#32) --- .../java/fr/korastrip/paperplugin/listener/DamageListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java index 938e98c..c8539ca 100644 --- a/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java +++ b/src/main/java/fr/korastrip/paperplugin/listener/DamageListener.java @@ -38,7 +38,8 @@ public void onDamageReceive(EntityDamageEvent event) { } var finalDamage = event.getFinalDamage(); - event.setCancelled(true); + event.setDamage(0); + var newHealth = group.getHealth() - finalDamage; List players = group.getOnlinePlayers(); From 082910f61d80035b502ce895b0761023be31fac3 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 18:04:16 +0200 Subject: [PATCH 7/9] refactor/error message (#37) * refactor(subcommand): send every command error through generateError generateError now parses MiniMessage and accepts tag resolvers, so messages with tags or placeholders render correctly. * refactor(errors): centralize and unify error messages in ErrorMessage enum * refactor(subcommand): use getContext in every subcommand (#36) --- .../commands/group/GroupSubCommand.java | 20 +++++++++++-- .../commands/group/sub/Create.java | 13 ++++----- .../paperplugin/commands/group/sub/Help.java | 9 ++---- .../commands/group/sub/Invite.java | 25 ++++++---------- .../commands/group/sub/Players.java | 9 +++--- .../commands/group/sub/Remove.java | 29 +++++++------------ .../commands/group/sub/Revoke.java | 9 +++--- .../paperplugin/commands/group/sub/Start.java | 19 ++++-------- .../paperplugin/commands/group/sub/Stats.java | 10 +++---- .../paperplugin/commands/group/sub/Stop.java | 17 ++++------- .../commands/group/sub/Version.java | 5 ++-- .../paperplugin/enums/ErrorMessage.java | 27 +++++++++++++++++ 12 files changed, 98 insertions(+), 94 deletions(-) create mode 100644 src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java 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 2980429..5e01e2d 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/GroupSubCommand.java @@ -1,13 +1,17 @@ package fr.korastrip.paperplugin.commands.group; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; import fr.korastrip.paperplugin.Config; import fr.korastrip.paperplugin.SharedStats; +import fr.korastrip.paperplugin.enums.ErrorMessage; import io.papermc.paper.command.brigadier.CommandSourceStack; -import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Entity; public abstract class GroupSubCommand { + public record ContextResult(CommandSender sender, Entity entity) {} protected final SharedStats plugin; protected final String name; protected final String description; @@ -30,11 +34,21 @@ public String getDescription() { return description; } - public int generateError(String message, CommandSender player) { - player.sendRichMessage("", Placeholder.unparsed("message", message)); + public ContextResult getContext(CommandContext context) { + CommandSender sender = context.getSource().getSender(); + Entity executor = context.getSource().getExecutor(); + return new ContextResult(sender, executor); + } + + public int generateError(String message, CommandSender player, TagResolver... resolvers) { + player.sendRichMessage("" + message + "", resolvers); return 0; } + public int generateError(ErrorMessage error, CommandSender player) { + return this.generateError(error.getMessage(), player); + } + public String getUsage() { return usage; } diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Create.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Create.java index a893a8e..2171b40 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Create.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Create.java @@ -4,11 +4,10 @@ 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 fr.korastrip.paperplugin.model.SharedGroup; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.UUID; @@ -24,24 +23,22 @@ public LiteralArgumentBuilder build() { return Commands.literal(this.name) .requires(source -> source.getSender().isOp()) .executes(context -> { - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if(!group.getPlayersId().isEmpty()) { - sender.sendRichMessage("A group already exists. You cannot create another one."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.GROUP_ALREADY_EXISTS, ctx.sender()); } UUID playerUUID = player.getUniqueId(); group.addPlayer(playerUUID); group.setOwnerUuid(playerUUID); - sender.sendRichMessage("Your group has been created.\nInvite players with /sharedgroup invite [player]."); + ctx.sender().sendRichMessage("Your group has been created.\nInvite players with /sharedgroup invite [player]."); return Command.SINGLE_SUCCESS; }); } 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 067f6e5..e7a0330 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 @@ -7,8 +7,6 @@ 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.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.List; @@ -24,10 +22,9 @@ public LiteralArgumentBuilder build() { return Commands.literal("help").executes(context -> { - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); - if (!(executor instanceof Player)) { + if (!(ctx.entity() instanceof Player)) { return Command.SINGLE_SUCCESS; } @@ -43,7 +40,7 @@ public LiteralArgumentBuilder build() { new Stats(plugin) ); - commands.forEach(command -> sender.sendRichMessage(": ", Placeholder.unparsed("description", command.getDescription()), Placeholder.unparsed("usage", command.getUsage()))); + commands.forEach(command -> ctx.sender().sendRichMessage(": ", Placeholder.unparsed("description", command.getDescription()), Placeholder.unparsed("usage", command.getUsage()))); return Command.SINGLE_SUCCESS; }); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Invite.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Invite.java index 33a28a9..837587e 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Invite.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Invite.java @@ -5,6 +5,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import fr.korastrip.paperplugin.SharedStats; import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; import fr.korastrip.paperplugin.model.SharedGroup; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; @@ -12,8 +13,6 @@ import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.NoSuchElementException; @@ -30,27 +29,23 @@ public LiteralArgumentBuilder build() { return Commands.literal("invite") .then(Commands.argument("player", ArgumentTypes.player()) .executes(context -> { - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if (group.getPlayersId().isEmpty()) { - sender.sendRichMessage("There is no group. Create one with /sharedgroup create."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NO_GROUP, ctx.sender()); } if (!player.getUniqueId().equals(group.getOwnerUuid())) { - sender.sendRichMessage("You cannot invite a player because you are not the owner."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NOT_OWNER, ctx.sender()); } if (group.getIsPlaying()) { - sender.sendRichMessage("You cannot invite a player after the game has started."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.GAME_RUNNING, ctx.sender()); } final PlayerSelectorArgumentResolver resolver = context.getArgument("player", PlayerSelectorArgumentResolver.class); @@ -59,13 +54,11 @@ public LiteralArgumentBuilder build() { try { target = resolver.resolve(context.getSource()).getFirst(); } catch (CommandSyntaxException | NoSuchElementException e) { - sender.sendRichMessage("Player not found."); - return 0; + return this.generateError(ErrorMessage.PLAYER_NOT_FOUND, ctx.sender()); } if (group.getPlayersId().contains(target.getUniqueId())) { - sender.sendRichMessage("This player is already in the group."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.PLAYER_ALREADY_IN_GROUP, ctx.sender()); } group.addPlayer(target.getUniqueId()); @@ -73,7 +66,7 @@ public LiteralArgumentBuilder build() { TagResolver.Single playerName = Placeholder.component("player", target.displayName()); target.sendRichMessage("You have been added to the group. Wait for the owner to start the game."); - sender.sendRichMessage(" has been added to the group.", playerName); + ctx.sender().sendRichMessage(" has been added to the group.", playerName); return Command.SINGLE_SUCCESS; })) diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Players.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Players.java index b638154..5ffdcd8 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Players.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Players.java @@ -4,11 +4,11 @@ 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 fr.korastrip.paperplugin.model.SharedGroup; 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.Entity; import org.bukkit.entity.Player; public class Players extends GroupSubCommand { @@ -21,16 +21,15 @@ public Players(SharedStats plugin) { public LiteralArgumentBuilder build() { return Commands.literal("players").executes(context -> { - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if (!group.getPlayersId().contains(player.getUniqueId())) { - player.sendRichMessage("You do not have permission to use this command."); - return 0; + return this.generateError(ErrorMessage.NOT_IN_GROUP, player); } var players = group.getOnlinePlayers(); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Remove.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Remove.java index 1a12351..5be8682 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Remove.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Remove.java @@ -3,17 +3,15 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import fr.korastrip.paperplugin.Config; import fr.korastrip.paperplugin.SharedStats; import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; import fr.korastrip.paperplugin.model.SharedGroup; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.argument.ArgumentTypes; import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.NoSuchElementException; @@ -29,27 +27,23 @@ public LiteralArgumentBuilder build() { return Commands.literal("remove") .then(Commands.argument("player", ArgumentTypes.player()) .executes(context -> { - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if (group.getPlayersId().isEmpty()) { - sender.sendRichMessage("There is no group. Create one with /sharedgroup create."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NO_GROUP, ctx.sender()); } if (!group.getOwnerUuid().equals(player.getUniqueId())) { - sender.sendRichMessage("You do not have permission to use this command."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NOT_OWNER, ctx.sender()); } if (group.getIsPlaying()) { - sender.sendRichMessage("You cannot remove a player during the game."); - return 0; + return this.generateError(ErrorMessage.GAME_RUNNING, ctx.sender()); } final PlayerSelectorArgumentResolver resolver = context.getArgument("player", PlayerSelectorArgumentResolver.class); @@ -58,22 +52,19 @@ public LiteralArgumentBuilder build() { try { target = resolver.resolve(context.getSource()).getFirst(); } catch (CommandSyntaxException | NoSuchElementException e) { - sender.sendRichMessage("Player not found."); - return 0; + return this.generateError(ErrorMessage.PLAYER_NOT_FOUND, ctx.sender()); } if (target.getUniqueId().equals(player.getUniqueId())) { - sender.sendRichMessage("You cannot remove yourself. Use /" + Config.PREFIX + " revoke instead."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.CANNOT_REMOVE_YOURSELF, ctx.sender()); } if (!group.getPlayersId().contains(target.getUniqueId())) { - sender.sendRichMessage("This player is not in the group."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.PLAYER_NOT_IN_GROUP, ctx.sender()); } group.removePlayer(target.getUniqueId()); - sender.sendRichMessage(" has been removed from your group.", Placeholder.component("player", target.displayName())); + ctx.sender().sendRichMessage(" has been removed from your group.", Placeholder.component("player", target.displayName())); target.sendRichMessage("You have been removed from the group by .", Placeholder.component("player", player.displayName())); return Command.SINGLE_SUCCESS; diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Revoke.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Revoke.java index 3532953..fc3a992 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Revoke.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Revoke.java @@ -4,10 +4,10 @@ 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 fr.korastrip.paperplugin.model.SharedGroup; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; public class Revoke extends GroupSubCommand { @@ -21,16 +21,15 @@ public LiteralArgumentBuilder build() { return Commands.literal("revoke") .executes(context -> { - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if (!player.getUniqueId().equals(group.getOwnerUuid())) { - player.sendRichMessage("You do not have permission to use this command."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NOT_OWNER, player); } group.getOnlinePlayers().forEach(p -> { diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java index 1d68891..b37522e 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Start.java @@ -2,16 +2,13 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import fr.korastrip.paperplugin.Config; import fr.korastrip.paperplugin.SharedStats; import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; import fr.korastrip.paperplugin.model.SharedGroup; 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.Sound; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.List; @@ -27,30 +24,26 @@ public Start(SharedStats plugin) { public LiteralArgumentBuilder build() { return Commands.literal("start").executes(context -> { - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if (!player.getUniqueId().equals(group.getOwnerUuid())) { - sender.sendRichMessage("You do not have permission to use this command."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NOT_OWNER, ctx.sender()); } if (group.getIsPlaying()) { - sender.sendRichMessage("The game has already started. Stop it with / stop.", Placeholder.unparsed("prefix", Config.PREFIX)); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.GAME_RUNNING, ctx.sender()); } List players = group.getOnlinePlayers(); if (players.size() < 2) { - sender.sendRichMessage("You cannot start a game alone. Invite at least one player."); - return Command.SINGLE_SUCCESS; + return this.generateError(ErrorMessage.NOT_ENOUGH_PLAYERS, ctx.sender()); } group.setHealth(20.0); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java index 9f369e5..04c0bba 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stats.java @@ -4,11 +4,11 @@ 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 fr.korastrip.paperplugin.model.SharedGroup; 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.Entity; import org.bukkit.entity.Player; import java.util.Comparator; @@ -23,19 +23,19 @@ public Stats(SharedStats plugin) { public LiteralArgumentBuilder build() { return Commands.literal("leaderboard").executes(context -> { - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); SharedGroup group = this.plugin.getGroup(); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return 0; } if(!group.getPlayersId().contains(player.getUniqueId())) { - return this.generateError("You do not have permission to use this command.", player); + return this.generateError(ErrorMessage.NOT_IN_GROUP, player); } if(!group.getIsPlaying()) { - return this.generateError("Currently there not game launched.", player); + return this.generateError(ErrorMessage.NO_GAME_RUNNING, player); } var playerPerScore = group.getOnlinePlayers().stream().sorted(Comparator.comparingInt(group::getPlayerDeaths).reversed()).toList(); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stop.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stop.java index 1cb5d8d..03729fd 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stop.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Stop.java @@ -5,12 +5,11 @@ import fr.korastrip.paperplugin.Config; import fr.korastrip.paperplugin.SharedStats; import fr.korastrip.paperplugin.commands.group.GroupSubCommand; +import fr.korastrip.paperplugin.enums.ErrorMessage; import fr.korastrip.paperplugin.model.SharedGroup; 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.command.CommandSender; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; public class Stop extends GroupSubCommand { @@ -25,26 +24,22 @@ public LiteralArgumentBuilder build() { SharedGroup group = this.plugin.getGroup(); - CommandSender sender = context.getSource().getSender(); - Entity executor = context.getSource().getExecutor(); + ContextResult ctx = this.getContext(context); - if (!(executor instanceof Player player)) { + if (!(ctx.entity() instanceof Player player)) { return Command.SINGLE_SUCCESS; } if(!group.getPlayersId().contains(player.getUniqueId())) { - sender.sendRichMessage("You cannot interact with the group, you are not into"); - return 0; + return this.generateError(ErrorMessage.NOT_IN_GROUP, ctx.sender()); } if(!player.getUniqueId().equals(group.getOwnerUuid())) { - sender.sendRichMessage("Only owners can do this action"); - return 0; + return this.generateError(ErrorMessage.NOT_OWNER, ctx.sender()); } if(!group.getIsPlaying()) { - sender.sendRichMessage("You cannot stop a game that's not already started"); - return 0; + return this.generateError(ErrorMessage.NO_GAME_RUNNING, ctx.sender()); } player.sendRichMessage("You're game has been stopped.\nTo restart: /" + Config.PREFIX + " start"); diff --git a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Version.java b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Version.java index af72ab7..dae9179 100644 --- a/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Version.java +++ b/src/main/java/fr/korastrip/paperplugin/commands/group/sub/Version.java @@ -8,7 +8,6 @@ import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.plugin.configuration.PluginMeta; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; -import org.bukkit.command.CommandSender; public class Version extends GroupSubCommand { @@ -19,11 +18,11 @@ public Version(SharedStats plugin) { @Override public LiteralArgumentBuilder build() { return Commands.literal("version").executes(context -> { - CommandSender sender = context.getSource().getSender(); + ContextResult ctx = this.getContext(context); PluginMeta pluginMeta = plugin.getPluginMeta(); - sender.sendRichMessage( + ctx.sender().sendRichMessage( " is running version .", Placeholder.unparsed("version", pluginMeta.getVersion()), Placeholder.unparsed("pluginname", pluginMeta.getName()) diff --git a/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java b/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java new file mode 100644 index 0000000..73036a7 --- /dev/null +++ b/src/main/java/fr/korastrip/paperplugin/enums/ErrorMessage.java @@ -0,0 +1,27 @@ +package fr.korastrip.paperplugin.enums; + +import fr.korastrip.paperplugin.Config; + +public enum ErrorMessage { + NO_GROUP("There is no group. Create one with /" + Config.PREFIX + " create."), + NOT_IN_GROUP("You are not in the group."), + NOT_OWNER("Only the group owner can use this command."), + NO_GAME_RUNNING("No game is running. Start one with /" + Config.PREFIX + " start."), + GAME_RUNNING("A game is running. Stop it with /" + Config.PREFIX + " stop."), + PLAYER_NOT_FOUND("Player not found."), + GROUP_ALREADY_EXISTS("A group already exists. You cannot create another one."), + 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."); + + private final String message; + + ErrorMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} From 4a3a035eea3b7333a7a19f2ec0527bf25c916a68 Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 18:04:43 +0200 Subject: [PATCH 8/9] docs: add /sharedgroup leaderboard to the commands table (#35) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a48244c..495ad20 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ | `/sharedgroup start` | Start the game and stat synchronization | OP, group owner | | `/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 version` | Display the plugin version | None | ## What remains to be done From f4dadbc3a924f0df4db9e79986b773e9739afb8f Mon Sep 17 00:00:00 2001 From: "C.Nelhomme" <64921289+D3ller@users.noreply.github.com> Date: Wed, 14 Oct 2026 18:05:11 +0200 Subject: [PATCH 9/9] refactor(shared-group): make groupFood final and simplify syncHealth (#34) --- .../java/fr/korastrip/paperplugin/model/SharedGroup.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java b/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java index f71ad9d..04612fa 100644 --- a/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java +++ b/src/main/java/fr/korastrip/paperplugin/model/SharedGroup.java @@ -43,7 +43,7 @@ public double getHealth() { } - private double groupFood = 20.0; + private final double groupFood = 20.0; private int deadCount = 0; public int getDeadCount() { @@ -113,9 +113,7 @@ public List getOnlinePlayers() { } public void syncHealth() { - this.getOnlinePlayers().forEach(p -> { - p.setHealth(this.getHealth()); - }); + this.getOnlinePlayers().forEach(p -> p.setHealth(this.getHealth())); } public void addPlayerDeath(Player player) {