feat: chef commands moved to admin + PlaceholderAPI integration

Chef → admin: the chef role no longer grants any command privilege.
All team-management subcommands now take <team> as an argument and are
gated by their crcore.team.<action> permission only:
- add <team> <player>
- remove <team> <player>
- transfer <team> <player>
- visibility <team> <PUBLIC|PRIVATE>
- setspawn <team> (still player-only — needs admin's location)

The LEADER role is kept in the data model (Team / TeamMember) and remains
usable by game plugins via the API, but does not unlock any default
command. Future work can re-introduce chef-specific commands if needed.

PlaceholderAPI: auto-detected at CRCore.enable(). If the PAPI plugin is
present on the server, CRCorePlaceholderExpansion registers automatically;
otherwise the lib runs without it (no NoClassDefFoundError thanks to the
indirection through doRegisterPlaceholderHook).

Placeholders exposed:
- Team: %crcore_team%, %crcore_team_name/tag/color/color_chat/size/
  visibility/leader_name/total_score%, %crcore_team_score_<name>%
- Player: %crcore_player_score_<name>%, %crcore_player_score_total%

Dependency: me.clip:placeholderapi:2.11.6, scope provided. New repo:
https://repo.extendedclip.com/content/repositories/placeholderapi/.

docs/features.md, decisions.md and the builtin-commands diagram updated to
reflect the simpler admin/player two-tier model and the PAPI section.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Antone Barbaud
2026-06-09 15:05:02 +02:00
parent 002fefdc02
commit 8b7cad3fce
12 changed files with 376 additions and 108 deletions
+46
View File
@@ -367,6 +367,52 @@ Format léger : une décision = un titre + contexte + choix + raison.
bases existantes, ALTER TABLE manuel ou suppression du fichier
(les bases d'event sont jetables).
## 2026-06-09 — Toutes les commandes "chef" deviennent admin (révision)
- **Révision** de la décision "Refonte permissions + modèle admin/chef/joueur"
prise plus tôt aujourd'hui.
- **Choix** : le rôle chef n'apporte plus aucun privilège de commande pour
l'instant. Toutes les opérations de gestion d'équipe (`add`, `remove`,
`transfer`, `visibility`, `setspawn`) deviennent **admin** :
- Signature avec `<team>` en argument (au lieu d'implicite "ma team").
- Permission `crcore.team.<action>` requise.
- Plus de check `isLeader(...)` dans `execute()`.
- **Raison** : le user a explicitement décidé que pour l'instant le chef
n'a pas plus de privilèges qu'un joueur lambda côté commandes. Le rôle
`LEADER` reste dans le modèle de données (utile pour les game plugins qui
pourraient l'exploiter via l'API, ou pour de futures commandes), mais il
ne gate plus rien au niveau du framework de commandes.
- **Conséquences** :
- `TeamRemoveSubCommand` : refuse de retirer le chef (l'admin doit
`setleader` d'abord). Pas un check chef, juste une garde de cohérence.
- `TeamTransferSubCommand` : devient l'équivalent admin "strict" de
`setleader` (membre existant uniquement). Les deux cohabitent ; doc dit
quand préférer l'un ou l'autre.
- `TeamSetSpawnSubCommand` : reste `playerOnly` car nécessite la
`Location` de l'exécutant — mais c'est désormais l'admin qui se place
à l'endroit voulu et tape `/core team setspawn <team>`.
## 2026-06-09 — Intégration PlaceholderAPI (optionnelle, auto-détectée)
- **Choix** : `CRCore.enable()` détecte la présence du plugin PlaceholderAPI
via `pluginManager.getPlugin("PlaceholderAPI")` et enregistre
automatiquement `CRCorePlaceholderExpansion` si présent. Aucune action
requise côté plugin de jeu.
- **Dépendance Maven** : `me.clip:placeholderapi:2.11.6` en scope
`provided` (depuis `https://repo.extendedclip.com/...`). Le jar PAPI
n'est PAS embarqué — c'est un plugin runtime indépendant.
- **Indirection de chargement** : la méthode privée
`doRegisterPlaceholderHook()` isole la référence à
`CRCorePlaceholderExpansion`. Si PAPI est absent, la méthode n'est jamais
appelée et le bytecode référençant `me.clip.placeholderapi.*` n'est pas
vérifié → pas de `NoClassDefFoundError`.
- **Placeholders exposés** :
- Team : `%crcore_team%`, `%crcore_team_name/tag/color/color_chat/size/`
`visibility/leader_name/total_score%`, `%crcore_team_score_<name>%`
- Player : `%crcore_player_score_<name>%`, `%crcore_player_score_total%`
- **Override** : `CRCore.registerPlaceholderHook()` est `protected` — une
sous-classe peut ajouter des placeholders ou skipper la hook.
## 2026-06-09 — Nouvelle commande `/core team setleader`
- **Choix** : ajout de `TeamSetLeaderSubCommand` (`/core team setleader
+15 -15
View File
@@ -1,5 +1,5 @@
@startuml builtin-commands-diagram
title CR-Core — Default /core team commands (admin / chef / joueur)
title CR-Core — Default /core team commands (admin / joueur)
skinparam classAttributeIconSize 0
hide empty members
@@ -11,16 +11,13 @@ package "fr.luc.crcore.command" {
package "fr.luc.crcore.command.builtin" {
class CoreCommand {
+ CoreCommand(teamSvc, playerSvc)
}
class CoreCommand
CoreCommand --|> BaseCommand
package "fr.luc.crcore.command.builtin.team" {
class TeamGroupSubCommand {
+ TeamGroupSubCommand(service)
# registerDefaults(): void
}
TeamGroupSubCommand --|> SubCommand
@@ -28,7 +25,7 @@ package "fr.luc.crcore.command.builtin" {
+ {static} teamByName(service): ArgumentType<Team>
}
' === ADMIN commands (permission seule) ===
' ─── ADMIN commands (permission seule, team par argument) ───
package "admin" <<Rectangle>> {
class TeamCreateSubCommand {
perm: crcore.team.create
@@ -46,32 +43,30 @@ package "fr.luc.crcore.command.builtin" {
perm: crcore.team.score
args: <team> <name> <add|set> <value>
}
}
' === CHEF commands (permission + check chef) ===
package "chef" <<Rectangle>> {
class TeamAddSubCommand {
perm: crcore.team.add
args: <player>
args: <team> <player>
}
class TeamRemoveSubCommand {
perm: crcore.team.remove
args: <player>
args: <team> <player>
}
class TeamTransferSubCommand {
perm: crcore.team.transfer
args: <player>
args: <team> <player>
}
class TeamVisibilitySubCommand {
perm: crcore.team.visibility
args: <vis>
args: <team> <vis>
}
class TeamSetSpawnSubCommand {
perm: crcore.team.setspawn
args: <team>
playerOnly
}
}
' === PLAYER commands ===
' ─── PLAYER commands ───
package "player" <<Rectangle>> {
class TeamJoinSubCommand {
perm: crcore.team.join
@@ -114,6 +109,11 @@ package "fr.luc.crcore.command.builtin" {
}
note bottom of TeamGroupSubCommand
Le rôle LEADER reste dans le modèle Team
(utilisable par les game plugins via l'API)
mais n'accorde aucun privilège de commande
dans le set par défaut.
Override d'une feuille :
core.getCoreCommand()
.findSubCommand("team")
+73 -15
View File
@@ -335,8 +335,14 @@ Voir [setup.md](setup.md#utilisation-depuis-un-plugin-de-jeu).
sous-classe ou via `replaceSubCommand`.
**Pas d'aliases courts** : les commandes ont leur nom long uniquement
(`/core team create` et pas `/core team c`). Les aliases ont été retirés
pour réduire la friction de découverte et la confusion.
(`/core team create` et pas `/core team c`).
**Modèle simplifié à 2 rôles** : toutes les opérations de gestion d'équipe
sont **admin** (perm requise + team passée en argument). Les opérations
joueur (`join`, `leave`, `info`, `list`, `top`) sont gated par permission
mais ne nécessitent pas le rôle chef. Le rôle `LEADER` reste présent dans
le modèle de données (utilisable par les game plugins via l'API) mais
n'accorde aucun privilège de commande pour l'instant.
### Arborescence
@@ -347,12 +353,12 @@ pour réduire la friction de découverte et la confusion.
├── delete <team> [admin] dissoudre une équipe
├── setleader <team> <player> [admin] (re)assigner le chef
├── score <team> <name> <add|set> <value> [admin] modifier un score
├── add <player> [chef] ajouter à son équipe
├── remove <player> [chef] retirer de son équipe
├── transfer <player> [chef] transférer leadership
├── visibility <PUBLIC|PRIVATE> [chef] changer visibilité
├── setspawn [chef] définir le spawn
├── join <team> [joueur] rejoindre PUBLIC
├── add <team> <player> [admin] ajouter un joueur
├── remove <team> <player> [admin] retirer un joueur
├── transfer <team> <player> [admin] transfert chef→membre existant
├── visibility <team> <PUBLIC|PRIVATE> [admin] changer visibilité
├── setspawn <team> [admin] spawn à la position de l'admin
├── join <team> [joueur] rejoindre une PUBLIC
├── leave [joueur] quitter son équipe
├── info [team] [joueur] infos
├── list [joueur] toutes les équipes
@@ -361,13 +367,12 @@ pour réduire la friction de découverte et la confusion.
### Permissions
Chaque sous-commande a une permission `crcore.team.<action>`. Modèle à 3 niveaux :
Chaque sous-commande a une permission `crcore.team.<action>` :
| Niveau | Commandes | Comportement |
|---|---|---|
| **Admin** | `create`, `delete`, `setleader`, `score` | Permission seule (pas de check chef). Cible une team via argument. |
| **Chef** | `add`, `remove`, `transfer`, `visibility`, `setspawn` | Permission **ET** check chef de sa propre équipe en plus. Cible la team de l'exécutant. |
| **Joueur** | `join`, `leave`, `info`, `list`, `top` | Permission seule (à granter par défaut côté LuckPerms si on veut que tout le monde y ait accès). |
| Niveau | Commandes |
|---|---|
| **Admin** | `create`, `delete`, `setleader`, `score`, `add`, `remove`, `transfer`, `visibility`, `setspawn` |
| **Joueur** | `join`, `leave`, `info`, `list`, `top` |
| Sous-commande | Permission |
|---|---|
@@ -512,7 +517,60 @@ db.table("my_kills")
---
## 7. Bootstrap `CRCore`
## 7. Intégration PlaceholderAPI (optionnelle)
**Statut** : implémentée. Auto-détectée par `CRCore.enable()` — si le plugin
**PlaceholderAPI** est installé sur le serveur, les placeholders `%crcore_*%`
sont enregistrés automatiquement. Si PAPI est absent, la lib reste
fonctionnelle, juste sans placeholders.
### Placeholders Team
Renvoient vides si le joueur n'est dans aucune équipe.
| Placeholder | Renvoie | Exemple |
|---|---|---|
| `%crcore_team%` | récap formaté coloré | `§c[#WOLF] Wolves` |
| `%crcore_team_name%` | nom de l'équipe | `Wolves` |
| `%crcore_team_tag%` | tag court | `WOLF` |
| `%crcore_team_color%` | nom de la couleur | `Red` |
| `%crcore_team_color_chat%` | code couleur ChatColor | `§c` |
| `%crcore_team_size%` | nombre de membres | `5` |
| `%crcore_team_visibility%` | `PUBLIC` ou `PRIVATE` | `PRIVATE` |
| `%crcore_team_leader_name%` | nom du chef (vide si leaderless) | `Alice` |
| `%crcore_team_total_score%` | somme des scores de l'équipe | `42` |
| `%crcore_team_score_<name>%` | score nommé de l'équipe | `%crcore_team_score_kills%``12` |
### Placeholders Player
| Placeholder | Renvoie |
|---|---|
| `%crcore_player_score_<name>%` | score nommé du joueur (0 si pas set) |
| `%crcore_player_score_total%` | somme de tous les scores du joueur |
### Usage côté plugin de jeu / config
Pas d'action à faire côté plugin de jeu — la hook s'enregistre toute seule.
Les placeholders sont disponibles partout où PAPI les résout (scoreboard,
tablist, chat, hologrammes via DecentHolograms, etc.) :
```yaml
# Exemple de scoreboard config (FeatherBoard / Scoreboard plugin)
lines:
- "&aÉquipe : %crcore_team%"
- "&aChef : %crcore_team_leader_name%"
- "&aKills : %crcore_player_score_kills% (total équipe %crcore_team_score_kills%)"
```
### Override
`CRCore.registerPlaceholderHook()` est `protected`. Override dans une
sous-classe de `CRCore` pour ajouter ses propres placeholders ou désactiver
la hook.
---
## 8. Bootstrap `CRCore`
**Statut** : implémenté. Point d'entrée unique pour les plugins de jeu.
+1
View File
@@ -5,6 +5,7 @@
- **Type** : librairie Java (`jar`) — pas un plugin Bukkit
- **artifactId Maven** : `CR-Core`
- **Build** : Maven, Java 11
- **Intégrations optionnelles** : PlaceholderAPI (auto-détectée si installée)
- **API serveur (provided)** : Paper 1.16.5
- **SQLite (compile)** : `org.xerial:sqlite-jdbc:3.45.3.0`
- **Package racine** : `fr.luc.crcore`