feat: admin/chef/player permission model + leaderless teams + setleader
Commands: - Drop all short aliases (c/i/t/j/vis/disband/...) — long names only. - Every /core team <action> now has a crcore.team.<action> permission. - Three-tier model: * Admin (perm only): create, delete, setleader, score * Chef (perm + chef-check in execute): add, remove, transfer, visibility, setspawn * Player (perm): join, leave, info, list, top - delete now takes <team> as argument (admin); no more chef-disband shortcut. New /core team setleader <team> <player>: - Admin assigns or replaces a team's leader. - More permissive than transfer: target may not yet be a member (auto-add), and works on leaderless teams. Leaderless teams: - Team.leaderId is now nullable. - getLeaderId() and getLeader() return Optional<...>. - hasLeader() and isLeader(UUID) helpers added. - New constructors Team(id, name, tag, color [, visibility]) for leaderless. - TeamService.createTeam overloads without leaderId. - TeamService.setLeader(teamId, playerId): assigns/replaces leader (auto-adds as member if needed). Fires TeamLeadershipTransferEvent with optional old. - TeamLeadershipTransferEvent.getOldLeaderId() returns Optional<UUID>. - SqliteTeamRepository: leader_id column no longer NOT NULL. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -323,6 +323,66 @@ Format léger : une décision = un titre + contexte + choix + raison.
|
|||||||
même fichier SQLite (par défaut `<dataFolder>/crcore.db`) ; le préfixe
|
même fichier SQLite (par défaut `<dataFolder>/crcore.db`) ; le préfixe
|
||||||
isole proprement.
|
isole proprement.
|
||||||
|
|
||||||
|
## 2026-06-09 — Refonte permissions + modèle admin/chef/joueur
|
||||||
|
|
||||||
|
- **Choix** : chaque sous-commande `/core team <action>` a sa propre permission
|
||||||
|
`crcore.team.<action>`. Trois niveaux fonctionnels :
|
||||||
|
- **Admin** (permission seule, cible une team par argument) : `create`,
|
||||||
|
`delete`, `setleader`, `score`.
|
||||||
|
- **Chef** (permission + check chef dans `execute()`) : `add`, `remove`,
|
||||||
|
`transfer`, `visibility`, `setspawn`.
|
||||||
|
- **Joueur** (permission seule, défaut « tout le monde » côté LuckPerms si
|
||||||
|
voulu) : `join`, `leave`, `info`, `list`, `top`.
|
||||||
|
- **Aliases courts supprimés** : `c` (create), `i` (info), `t` (team), `j`
|
||||||
|
(join), `vis` (visibility), `disband`/`dissolve` (delete), `kick`/`expel`
|
||||||
|
(remove), etc. Plus que les noms longs. Raison : réduire la friction
|
||||||
|
d'apprentissage et la confusion (les game plugins ont leurs propres noms,
|
||||||
|
l'aliasing devient un bruit).
|
||||||
|
- **`delete` devient admin** : `/core team delete <team>` (au lieu de
|
||||||
|
l'ancien `/core team delete` qui ciblait l'équipe du chef). Cohérent avec
|
||||||
|
`create` qui est aussi admin.
|
||||||
|
|
||||||
|
## 2026-06-09 — Team peut être leaderless
|
||||||
|
|
||||||
|
- **Choix** : `Team.leaderId` devient nullable. `getLeaderId()` renvoie
|
||||||
|
`Optional<UUID>`, `getLeader()` renvoie `Optional<TeamMember>`. Nouveau
|
||||||
|
`hasLeader()` et `isLeader(UUID)` pour les checks.
|
||||||
|
- **Raison** : le modèle admin requiert qu'on puisse créer une équipe sans
|
||||||
|
chef et l'assigner ensuite via {@code setLeader}. Avant, créer une équipe
|
||||||
|
imposait de connaître l'UUID du chef.
|
||||||
|
- **Constructeurs ajoutés** :
|
||||||
|
- `new Team(id, name, tag, color)` — leaderless, PRIVATE
|
||||||
|
- `new Team(id, name, tag, color, visibility)` — leaderless avec visibilité
|
||||||
|
- Les constructeurs avec leaderId acceptent maintenant `null`.
|
||||||
|
- **`Team.setLeader(playerId)`** : assigne un chef à n'importe quel moment.
|
||||||
|
Si la team a déjà un chef, il est démis en MEMBER. Si le nouveau n'est
|
||||||
|
pas membre, il est auto-ajouté.
|
||||||
|
- **`Team.transferLeadership(playerId)`** : conserve sa sémantique stricte
|
||||||
|
(chef→chef, membre déjà existant). Lève `IllegalStateException` si la team
|
||||||
|
est leaderless. Utilisé par la commande `/core team transfer` (chef).
|
||||||
|
- **`TeamLeadershipTransferEvent.getOldLeaderId()`** renvoie maintenant
|
||||||
|
`Optional<UUID>` (vide si la team était leaderless avant l'opération).
|
||||||
|
- **Schéma SQLite** : la colonne `crcore_teams.leader_id` n'a plus la
|
||||||
|
contrainte `NOT NULL`. Migration automatique sur nouvelle base — pour les
|
||||||
|
bases existantes, ALTER TABLE manuel ou suppression du fichier
|
||||||
|
(les bases d'event sont jetables).
|
||||||
|
|
||||||
|
## 2026-06-09 — Nouvelle commande `/core team setleader`
|
||||||
|
|
||||||
|
- **Choix** : ajout de `TeamSetLeaderSubCommand` (`/core team setleader
|
||||||
|
<team> <player>`). Permission `crcore.team.setleader`. Délègue à
|
||||||
|
`TeamService.setLeader(...)`.
|
||||||
|
- **Différence avec `/core team transfer`** :
|
||||||
|
- `transfer` : action **chef**, cible son équipe, le nouveau chef doit déjà
|
||||||
|
être membre.
|
||||||
|
- `setleader` : action **admin**, cible n'importe quelle équipe, le nouveau
|
||||||
|
chef peut être non-membre (auto-ajouté).
|
||||||
|
- **Use cases couverts** :
|
||||||
|
- Admin assigne un chef à une équipe leaderless fraîchement créée.
|
||||||
|
- Admin remplace le chef d'une équipe (le membre target est déjà dans
|
||||||
|
l'équipe ou pas — peu importe).
|
||||||
|
- Admin "promote member up to leader" (cas explicitement demandé).
|
||||||
|
|
||||||
## 2026-06-09 — Bascule Java 16 → Java 11 (révision)
|
## 2026-06-09 — Bascule Java 16 → Java 11 (révision)
|
||||||
|
|
||||||
- **Révision** de la décision "Java 16" du 2026-06-08.
|
- **Révision** de la décision "Java 16" du 2026-06-08.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@startuml builtin-commands-diagram
|
@startuml builtin-commands-diagram
|
||||||
title CR-Core — Default /core team commands
|
title CR-Core — Default /core team commands (admin / chef / joueur)
|
||||||
|
|
||||||
skinparam classAttributeIconSize 0
|
skinparam classAttributeIconSize 0
|
||||||
hide empty members
|
hide empty members
|
||||||
@@ -13,7 +13,6 @@ package "fr.luc.crcore.command.builtin" {
|
|||||||
|
|
||||||
class CoreCommand {
|
class CoreCommand {
|
||||||
+ CoreCommand(teamSvc, playerSvc)
|
+ CoreCommand(teamSvc, playerSvc)
|
||||||
# registerDefaults(): void
|
|
||||||
}
|
}
|
||||||
CoreCommand --|> BaseCommand
|
CoreCommand --|> BaseCommand
|
||||||
|
|
||||||
@@ -29,44 +28,93 @@ package "fr.luc.crcore.command.builtin" {
|
|||||||
+ {static} teamByName(service): ArgumentType<Team>
|
+ {static} teamByName(service): ArgumentType<Team>
|
||||||
}
|
}
|
||||||
|
|
||||||
class TeamCreateSubCommand {
|
' === ADMIN commands (permission seule) ===
|
||||||
+ execute(ctx): CommandResult
|
package "admin" <<Rectangle>> {
|
||||||
|
class TeamCreateSubCommand {
|
||||||
|
perm: crcore.team.create
|
||||||
|
args: name, tag, color, [leader]
|
||||||
|
}
|
||||||
|
class TeamDeleteSubCommand {
|
||||||
|
perm: crcore.team.delete
|
||||||
|
args: <team>
|
||||||
|
}
|
||||||
|
class TeamSetLeaderSubCommand {
|
||||||
|
perm: crcore.team.setleader
|
||||||
|
args: <team> <player>
|
||||||
|
}
|
||||||
|
class TeamScoreSubCommand {
|
||||||
|
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>
|
||||||
|
}
|
||||||
|
class TeamRemoveSubCommand {
|
||||||
|
perm: crcore.team.remove
|
||||||
|
args: <player>
|
||||||
|
}
|
||||||
|
class TeamTransferSubCommand {
|
||||||
|
perm: crcore.team.transfer
|
||||||
|
args: <player>
|
||||||
|
}
|
||||||
|
class TeamVisibilitySubCommand {
|
||||||
|
perm: crcore.team.visibility
|
||||||
|
args: <vis>
|
||||||
|
}
|
||||||
|
class TeamSetSpawnSubCommand {
|
||||||
|
perm: crcore.team.setspawn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
' === PLAYER commands ===
|
||||||
|
package "player" <<Rectangle>> {
|
||||||
|
class TeamJoinSubCommand {
|
||||||
|
perm: crcore.team.join
|
||||||
|
args: <team>
|
||||||
|
}
|
||||||
|
class TeamLeaveSubCommand {
|
||||||
|
perm: crcore.team.leave
|
||||||
|
}
|
||||||
|
class TeamInfoSubCommand {
|
||||||
|
perm: crcore.team.info
|
||||||
|
args: [team]
|
||||||
|
}
|
||||||
|
class TeamListSubCommand {
|
||||||
|
perm: crcore.team.list
|
||||||
|
}
|
||||||
|
class TeamTopSubCommand {
|
||||||
|
perm: crcore.team.top
|
||||||
|
args: [score]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class TeamDeleteSubCommand
|
|
||||||
class TeamAddSubCommand
|
|
||||||
class TeamRemoveSubCommand
|
|
||||||
class TeamJoinSubCommand
|
|
||||||
class TeamLeaveSubCommand
|
|
||||||
class TeamInfoSubCommand
|
|
||||||
class TeamListSubCommand
|
|
||||||
class TeamTransferSubCommand
|
|
||||||
class TeamVisibilitySubCommand
|
|
||||||
class TeamScoreSubCommand
|
|
||||||
class TeamTopSubCommand
|
|
||||||
class TeamSetSpawnSubCommand
|
|
||||||
|
|
||||||
TeamCreateSubCommand --|> SubCommand
|
TeamCreateSubCommand --|> SubCommand
|
||||||
TeamDeleteSubCommand --|> SubCommand
|
TeamDeleteSubCommand --|> SubCommand
|
||||||
|
TeamSetLeaderSubCommand --|> SubCommand
|
||||||
|
TeamScoreSubCommand --|> SubCommand
|
||||||
TeamAddSubCommand --|> SubCommand
|
TeamAddSubCommand --|> SubCommand
|
||||||
TeamRemoveSubCommand --|> SubCommand
|
TeamRemoveSubCommand --|> SubCommand
|
||||||
|
TeamTransferSubCommand --|> SubCommand
|
||||||
|
TeamVisibilitySubCommand --|> SubCommand
|
||||||
|
TeamSetSpawnSubCommand --|> SubCommand
|
||||||
TeamJoinSubCommand --|> SubCommand
|
TeamJoinSubCommand --|> SubCommand
|
||||||
TeamLeaveSubCommand --|> SubCommand
|
TeamLeaveSubCommand --|> SubCommand
|
||||||
TeamInfoSubCommand --|> SubCommand
|
TeamInfoSubCommand --|> SubCommand
|
||||||
TeamListSubCommand --|> SubCommand
|
TeamListSubCommand --|> SubCommand
|
||||||
TeamTransferSubCommand --|> SubCommand
|
|
||||||
TeamVisibilitySubCommand --|> SubCommand
|
|
||||||
TeamScoreSubCommand --|> SubCommand
|
|
||||||
TeamTopSubCommand --|> SubCommand
|
TeamTopSubCommand --|> SubCommand
|
||||||
TeamSetSpawnSubCommand --|> SubCommand
|
|
||||||
|
|
||||||
CoreCommand "1" *-- "1" TeamGroupSubCommand : contains
|
CoreCommand "1" *-- "1" TeamGroupSubCommand : contains
|
||||||
TeamGroupSubCommand "1" *-- "13" SubCommand : contains
|
TeamGroupSubCommand "1" *-- "14" SubCommand : contains
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
note right of CoreCommand
|
note bottom of TeamGroupSubCommand
|
||||||
Le plugin de jeu downstream
|
Override d'une feuille :
|
||||||
remplace une feuille avec :
|
|
||||||
core.getCoreCommand()
|
core.getCoreCommand()
|
||||||
.findSubCommand("team")
|
.findSubCommand("team")
|
||||||
.replaceSubCommand("create",
|
.replaceSubCommand("create",
|
||||||
|
|||||||
@@ -101,17 +101,24 @@ package "fr.luc.crcore.team" {
|
|||||||
- name: String
|
- name: String
|
||||||
- tag: String
|
- tag: String
|
||||||
- color: TeamColor
|
- color: TeamColor
|
||||||
- leaderId: UUID
|
- leaderId: UUID *(nullable)*
|
||||||
- visibility: TeamVisibility
|
- visibility: TeamVisibility
|
||||||
- members: Set<TeamMember>
|
- members: Set<TeamMember>
|
||||||
- scores: Map<String, Integer>
|
- scores: Map<String, Integer>
|
||||||
- spawnPoint: Location
|
- spawnPoint: Location
|
||||||
--
|
--
|
||||||
|
+ Team(id, name, tag, color) ' leaderless, PRIVATE
|
||||||
|
+ Team(id, name, tag, color, visibility) ' leaderless
|
||||||
|
+ Team(id, name, tag, color, leaderId) ' with leader, PRIVATE
|
||||||
|
+ Team(id, name, tag, color, leaderId, visibility)
|
||||||
|
--
|
||||||
+ getName(): String
|
+ getName(): String
|
||||||
+ getTag(): String
|
+ getTag(): String
|
||||||
+ getColor(): TeamColor
|
+ getColor(): TeamColor
|
||||||
+ getLeaderId(): UUID
|
+ getLeaderId(): Optional<UUID>
|
||||||
+ getLeader(): TeamMember
|
+ getLeader(): Optional<TeamMember>
|
||||||
|
+ hasLeader(): boolean
|
||||||
|
+ isLeader(playerId): boolean
|
||||||
+ getVisibility(): TeamVisibility
|
+ getVisibility(): TeamVisibility
|
||||||
+ setVisibility(v): void
|
+ setVisibility(v): void
|
||||||
+ isPublic(): boolean
|
+ isPublic(): boolean
|
||||||
@@ -121,7 +128,8 @@ package "fr.luc.crcore.team" {
|
|||||||
+ size(): int
|
+ size(): int
|
||||||
+ addMember(playerId): TeamMember
|
+ addMember(playerId): TeamMember
|
||||||
+ removeMember(playerId): boolean
|
+ removeMember(playerId): boolean
|
||||||
+ transferLeadership(newLeaderId): void
|
+ transferLeadership(newLeaderId): void ' strict: chef→chef
|
||||||
|
+ setLeader(newLeaderId): void ' permissive: assign
|
||||||
--
|
--
|
||||||
+ getScore(name): int
|
+ getScore(name): int
|
||||||
+ hasScore(name): boolean
|
+ hasScore(name): boolean
|
||||||
@@ -157,12 +165,16 @@ package "fr.luc.crcore.team" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface TeamService {
|
interface TeamService {
|
||||||
+ createTeam(name, tag, color, leaderId, [visibility]): Team
|
+ createTeam(name, tag, color): Team ' leaderless, PRIVATE
|
||||||
|
+ createTeam(name, tag, color, visibility): Team ' leaderless
|
||||||
|
+ createTeam(name, tag, color, leaderId): Team
|
||||||
|
+ createTeam(name, tag, color, leaderId, visibility): Team
|
||||||
+ dissolveTeam(teamId): boolean
|
+ dissolveTeam(teamId): boolean
|
||||||
+ addMember(teamId, playerId): boolean
|
+ addMember(teamId, playerId): boolean
|
||||||
+ removeMember(teamId, playerId): boolean
|
+ removeMember(teamId, playerId): boolean
|
||||||
+ joinTeam(teamId, playerId): boolean
|
+ joinTeam(teamId, playerId): boolean
|
||||||
+ transferLeadership(teamId, newLeaderId): boolean
|
+ transferLeadership(teamId, newLeaderId): boolean ' strict: chef→chef
|
||||||
|
+ setLeader(teamId, newLeaderId): boolean ' permissive (admin)
|
||||||
+ setVisibility(teamId, visibility): void
|
+ setVisibility(teamId, visibility): void
|
||||||
--
|
--
|
||||||
+ addScore(teamId, name, delta): int
|
+ addScore(teamId, name, delta): int
|
||||||
|
|||||||
+52
-38
@@ -30,7 +30,7 @@ Architecture des domaines :
|
|||||||
| `name` | `String` | Nom lisible. Unique (case-insensitive). |
|
| `name` | `String` | Nom lisible. Unique (case-insensitive). |
|
||||||
| `tag` | `String` | Tag court (le « # »). Unique. Affiché entre `[# … ]`. |
|
| `tag` | `String` | Tag court (le « # »). Unique. Affiché entre `[# … ]`. |
|
||||||
| `color` | `TeamColor` | Couleur associée (enum). |
|
| `color` | `TeamColor` | Couleur associée (enum). |
|
||||||
| `leaderId` | `UUID` | Identifiant du joueur **chef d'équipe**. |
|
| `leaderId` | `UUID` *(nullable)* | Identifiant du joueur **chef d'équipe**. Peut être `null` (équipe leaderless — typique après création par admin). |
|
||||||
| `visibility` | `TeamVisibility` | `PUBLIC` (les joueurs peuvent rejoindre) ou `PRIVATE` (seul le chef ajoute). Défaut : `PRIVATE`. |
|
| `visibility` | `TeamVisibility` | `PUBLIC` (les joueurs peuvent rejoindre) ou `PRIVATE` (seul le chef ajoute). Défaut : `PRIVATE`. |
|
||||||
| `members` | `Set<TeamMember>` | Ensemble des membres (inclut le chef). |
|
| `members` | `Set<TeamMember>` | Ensemble des membres (inclut le chef). |
|
||||||
|
|
||||||
@@ -64,13 +64,16 @@ Architecture des domaines :
|
|||||||
|
|
||||||
| Opération | Description |
|
| Opération | Description |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `createTeam(name, tag, color, leaderId)` | Crée une équipe `PRIVATE` avec ce joueur comme chef. Échoue si nom/tag/joueur déjà pris. |
|
| `createTeam(name, tag, color)` | Crée une équipe **leaderless** en `PRIVATE`. |
|
||||||
| `createTeam(name, tag, color, leaderId, visibility)` | Surcharge : permet de créer directement en `PUBLIC` ou `PRIVATE`. |
|
| `createTeam(name, tag, color, visibility)` | Crée une équipe **leaderless** avec la visibilité spécifiée. |
|
||||||
|
| `createTeam(name, tag, color, leaderId)` | Crée une équipe `PRIVATE` avec ce joueur comme chef. |
|
||||||
|
| `createTeam(name, tag, color, leaderId, visibility)` | Surcharge complète. `leaderId` peut être `null` (équivalent leaderless). |
|
||||||
| `dissolveTeam(teamId)` | Supprime l'équipe. |
|
| `dissolveTeam(teamId)` | Supprime l'équipe. |
|
||||||
| `addMember(teamId, playerId)` | **Action du chef** : ajoute un joueur comme `MEMBER` (marche en PUBLIC comme en PRIVATE). |
|
| `addMember(teamId, playerId)` | **Action du chef** : ajoute un joueur comme `MEMBER` (marche en PUBLIC comme en PRIVATE). |
|
||||||
| `removeMember(teamId, playerId)` | Retire un joueur (interdit sur le chef). |
|
| `removeMember(teamId, playerId)` | Retire un joueur (interdit sur le chef). |
|
||||||
| `joinTeam(teamId, playerId)` | **Action du joueur** : auto-rejoindre une équipe `PUBLIC`. Lève `TeamAccessException` si la team est `PRIVATE` ou si le joueur est déjà dans une équipe. |
|
| `joinTeam(teamId, playerId)` | **Action du joueur** : auto-rejoindre une équipe `PUBLIC`. Lève `TeamAccessException` si la team est `PRIVATE` ou si le joueur est déjà dans une équipe. |
|
||||||
| `transferLeadership(teamId, newLeaderId)` | Le nouveau chef doit déjà être membre. |
|
| `transferLeadership(teamId, newLeaderId)` | Transfert chef→chef strict : le nouveau chef doit être membre, et la team doit avoir un chef actuel. |
|
||||||
|
| `setLeader(teamId, newLeaderId)` | Plus permissif : fonctionne sur team leaderless **et** auto-ajoute le joueur s'il n'est pas membre. C'est l'opération admin. |
|
||||||
| `setVisibility(teamId, visibility)` | Change la visibilité (typiquement appelée par le chef). |
|
| `setVisibility(teamId, visibility)` | Change la visibilité (typiquement appelée par le chef). |
|
||||||
| `addScore(teamId, name, delta)` / `setScore` / `getScore` / `resetScore` / `resetAllScores` | Gestion des scores (voir section Scores). |
|
| `addScore(teamId, name, delta)` / `setScore` / `getScore` / `resetScore` / `resetAllScores` | Gestion des scores (voir section Scores). |
|
||||||
| `getRankingByScore(name)` / `getGlobalRanking()` / `getTopRankingByScore(name, n)` / `getTopGlobalRanking(n)` | Classements (voir section Classements). |
|
| `getRankingByScore(name)` / `getGlobalRanking()` / `getTopRankingByScore(name, n)` / `getTopGlobalRanking(n)` | Classements (voir section Classements). |
|
||||||
@@ -326,54 +329,65 @@ Voir [setup.md](setup.md#utilisation-depuis-un-plugin-de-jeu).
|
|||||||
|
|
||||||
## 4. Commandes built-in `/core team ...`
|
## 4. Commandes built-in `/core team ...`
|
||||||
|
|
||||||
**Statut** : 13 sous-commandes prêtes à l'emploi, branchées par
|
**Statut** : 14 sous-commandes prêtes à l'emploi, branchées par
|
||||||
`CRCore.enable()`. Chaque sous-commande vit dans
|
`CRCore.enable()`. Chaque sous-commande vit dans
|
||||||
`fr.luc.crcore.command.builtin.team` et est substituable individuellement par
|
`fr.luc.crcore.command.builtin.team` et est substituable individuellement par
|
||||||
sous-classe ou via `replaceSubCommand`.
|
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.
|
||||||
|
|
||||||
### Arborescence
|
### Arborescence
|
||||||
|
|
||||||
```
|
```
|
||||||
/core (CoreCommand, BaseCommand racine, aliases: cr, crcore)
|
/core (CoreCommand)
|
||||||
└── team (TeamGroupSubCommand, alias: t)
|
└── team (TeamGroupSubCommand)
|
||||||
├── create <name> <tag> <color> [visibility] — créer une équipe
|
├── create <name> <tag> <color> [leader] [admin] créer (chef optionnel)
|
||||||
├── delete — dissoudre son équipe (chef)
|
├── delete <team> [admin] dissoudre une équipe
|
||||||
├── add <player> — chef ajoute un membre
|
├── setleader <team> <player> [admin] (re)assigner le chef
|
||||||
├── remove <player> — chef retire un membre
|
├── score <team> <name> <add|set> <value> [admin] modifier un score
|
||||||
├── join <name> — auto-join sur team PUBLIC
|
├── add <player> [chef] ajouter à son équipe
|
||||||
├── leave — quitter son équipe
|
├── remove <player> [chef] retirer de son équipe
|
||||||
├── info [name] — infos d'une équipe
|
├── transfer <player> [chef] transférer leadership
|
||||||
├── list — liste toutes les équipes
|
├── visibility <PUBLIC|PRIVATE> [chef] changer visibilité
|
||||||
├── transfer <player> — transfert de leadership
|
├── setspawn [chef] définir le spawn
|
||||||
├── visibility <PUBLIC|PRIVATE> — changer la visibilité
|
├── join <team> [joueur] rejoindre PUBLIC
|
||||||
├── score <team> <name> <add|set> <value> — [admin] modifier un score
|
├── leave [joueur] quitter son équipe
|
||||||
├── top [score] — classement (par score ou global)
|
├── info [team] [joueur] infos
|
||||||
└── setspawn — chef définit le spawn
|
├── list [joueur] toutes les équipes
|
||||||
|
└── top [score] [joueur] classement
|
||||||
```
|
```
|
||||||
|
|
||||||
### Aliases supplémentaires (équivalents Bukkit)
|
### Permissions
|
||||||
|
|
||||||
| Sous-commande | Aliases |
|
Chaque sous-commande a une permission `crcore.team.<action>`. Modèle à 3 niveaux :
|
||||||
|---|---|
|
|
||||||
| `create` | `c`, `new` |
|
|
||||||
| `delete` | `disband`, `dissolve` |
|
|
||||||
| `add` | `invite` |
|
|
||||||
| `remove` | `kick`, `expel` |
|
|
||||||
| `join` | `j` |
|
|
||||||
| `leave` | `quit` |
|
|
||||||
| `info` | `i` |
|
|
||||||
| `list` | `ls` |
|
|
||||||
| `visibility` | `vis` |
|
|
||||||
| `top` | `ranking`, `leaderboard` |
|
|
||||||
| `setspawn` | `spawn` |
|
|
||||||
|
|
||||||
### Permissions par défaut
|
| 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). |
|
||||||
|
|
||||||
| Sous-commande | Permission |
|
| Sous-commande | Permission |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `create` | `crcore.team.create` |
|
| `create` | `crcore.team.create` |
|
||||||
| `score` | `crcore.team.score.modify` (admin) |
|
| `delete` | `crcore.team.delete` |
|
||||||
| autres | aucune (gated par appartenance / rôle de chef) |
|
| `setleader` | `crcore.team.setleader` |
|
||||||
|
| `score` | `crcore.team.score` |
|
||||||
|
| `add` | `crcore.team.add` |
|
||||||
|
| `remove` | `crcore.team.remove` |
|
||||||
|
| `transfer` | `crcore.team.transfer` |
|
||||||
|
| `visibility` | `crcore.team.visibility` |
|
||||||
|
| `setspawn` | `crcore.team.setspawn` |
|
||||||
|
| `join` | `crcore.team.join` |
|
||||||
|
| `leave` | `crcore.team.leave` |
|
||||||
|
| `info` | `crcore.team.info` |
|
||||||
|
| `list` | `crcore.team.list` |
|
||||||
|
| `top` | `crcore.team.top` |
|
||||||
|
|
||||||
|
Le plugin de jeu ou le serveur configure les défauts via LuckPerms / Bukkit
|
||||||
|
permissions plugin.
|
||||||
|
|
||||||
### Override d'une sous-commande par défaut
|
### Override d'une sous-commande par défaut
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class CoreCommand extends BaseCommand {
|
|||||||
protected final PlayerProfileService playerProfileService;
|
protected final PlayerProfileService playerProfileService;
|
||||||
|
|
||||||
public CoreCommand(TeamService teamService, PlayerProfileService playerProfileService) {
|
public CoreCommand(TeamService teamService, PlayerProfileService playerProfileService) {
|
||||||
super("core", "cr", "crcore");
|
super("core");
|
||||||
this.teamService = Objects.requireNonNull(teamService, "teamService");
|
this.teamService = Objects.requireNonNull(teamService, "teamService");
|
||||||
this.playerProfileService = Objects.requireNonNull(playerProfileService, "playerProfileService");
|
this.playerProfileService = Objects.requireNonNull(playerProfileService, "playerProfileService");
|
||||||
description("Commandes du noyau CR-Core");
|
description("Commandes du noyau CR-Core");
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ public class TeamAddSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamAddSubCommand(TeamService service) {
|
public TeamAddSubCommand(TeamService service) {
|
||||||
super("add", "invite");
|
super("add");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Ajouter un joueur à son équipe (chef uniquement)");
|
description("Ajouter un joueur à son équipe (chef uniquement)");
|
||||||
|
permission("crcore.team.add");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
argument("player", ArgumentTypes.ONLINE_PLAYER);
|
argument("player", ArgumentTypes.ONLINE_PLAYER);
|
||||||
}
|
}
|
||||||
@@ -37,7 +38,7 @@ public class TeamAddSubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (!team.getLeaderId().equals(executor.getUniqueId())) {
|
if (!team.isLeader(executor.getUniqueId())) {
|
||||||
return CommandResult.failure("Seul le chef peut ajouter des membres.");
|
return CommandResult.failure("Seul le chef peut ajouter des membres.");
|
||||||
}
|
}
|
||||||
if (service.getTeamOfPlayer(target.getUniqueId()).isPresent()) {
|
if (service.getTeamOfPlayer(target.getUniqueId()).isPresent()) {
|
||||||
|
|||||||
@@ -8,45 +8,58 @@ import fr.luc.crcore.team.Team;
|
|||||||
import fr.luc.crcore.team.TeamColor;
|
import fr.luc.crcore.team.TeamColor;
|
||||||
import fr.luc.crcore.team.TeamException;
|
import fr.luc.crcore.team.TeamException;
|
||||||
import fr.luc.crcore.team.TeamService;
|
import fr.luc.crcore.team.TeamService;
|
||||||
import fr.luc.crcore.team.TeamVisibility;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code /core team create <name> <tag> <color> [visibility]}
|
* {@code /core team create <name> <tag> <color> [leader]}
|
||||||
*
|
*
|
||||||
* <p>Crée une équipe dont l'exécutant devient le chef. Visibilité par défaut :
|
* <p><b>Admin uniquement</b>. Crée une équipe en {@link
|
||||||
* {@link TeamVisibility#PRIVATE}.
|
* fr.luc.crcore.team.TeamVisibility#PRIVATE} par défaut.
|
||||||
|
*
|
||||||
|
* <p>Le chef est <b>optionnel</b> :
|
||||||
|
* <ul>
|
||||||
|
* <li>Sans argument {@code leader} → équipe leaderless. L'admin assignera
|
||||||
|
* plus tard via {@code /core team setleader}.</li>
|
||||||
|
* <li>Avec argument {@code leader} (nom d'un joueur connecté) → ce joueur
|
||||||
|
* devient chef et membre de l'équipe.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>La visibilité (PUBLIC/PRIVATE) se change ensuite via {@code /core team
|
||||||
|
* visibility} (action du chef).
|
||||||
*/
|
*/
|
||||||
public class TeamCreateSubCommand extends SubCommand {
|
public class TeamCreateSubCommand extends SubCommand {
|
||||||
|
|
||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamCreateSubCommand(TeamService service) {
|
public TeamCreateSubCommand(TeamService service) {
|
||||||
super("create", "c", "new");
|
super("create");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Créer une équipe");
|
description("Créer une équipe (admin)");
|
||||||
permission("crcore.team.create");
|
permission("crcore.team.create");
|
||||||
playerOnly();
|
|
||||||
argument("name", ArgumentTypes.STRING);
|
argument("name", ArgumentTypes.STRING);
|
||||||
argument("tag", ArgumentTypes.STRING);
|
argument("tag", ArgumentTypes.STRING);
|
||||||
argument("color", ArgumentTypes.enumOf(TeamColor.class));
|
argument("color", ArgumentTypes.enumOf(TeamColor.class));
|
||||||
optionalArgument("visibility", ArgumentTypes.enumOf(TeamVisibility.class));
|
optionalArgument("leader", ArgumentTypes.ONLINE_PLAYER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandResult execute(CommandContext ctx) {
|
public CommandResult execute(CommandContext ctx) {
|
||||||
Player player = ctx.requirePlayer();
|
|
||||||
String name = ctx.get("name");
|
String name = ctx.get("name");
|
||||||
String tag = ctx.get("tag");
|
String tag = ctx.get("tag");
|
||||||
TeamColor color = ctx.get("color");
|
TeamColor color = ctx.get("color");
|
||||||
TeamVisibility visibility = ctx.<TeamVisibility>getOptional("visibility")
|
Optional<Player> leaderOpt = ctx.getOptional("leader");
|
||||||
.orElse(TeamVisibility.PRIVATE);
|
UUID leaderId = leaderOpt.map(Player::getUniqueId).orElse(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Team team = service.createTeam(name, tag, color, player.getUniqueId(), visibility);
|
Team team = service.createTeam(name, tag, color, leaderId);
|
||||||
return CommandResult.success("Équipe " + team.getName() + " [#" + team.getTag() + "] créée.");
|
String suffix = leaderOpt.isPresent()
|
||||||
|
? " (chef : " + leaderOpt.get().getName() + ")"
|
||||||
|
: " (sans chef)";
|
||||||
|
return CommandResult.success("Équipe " + team.getName() + " [#" + team.getTag() + "] créée" + suffix + ".");
|
||||||
} catch (TeamException ex) {
|
} catch (TeamException ex) {
|
||||||
return CommandResult.failure(ex.getMessage());
|
return CommandResult.failure(ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,40 +5,30 @@ import fr.luc.crcore.command.CommandResult;
|
|||||||
import fr.luc.crcore.command.SubCommand;
|
import fr.luc.crcore.command.SubCommand;
|
||||||
import fr.luc.crcore.team.Team;
|
import fr.luc.crcore.team.Team;
|
||||||
import fr.luc.crcore.team.TeamService;
|
import fr.luc.crcore.team.TeamService;
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code /core team delete}
|
* {@code /core team delete <team>}
|
||||||
*
|
*
|
||||||
* <p>Dissout l'équipe de l'exécutant. Réservé au chef. Pas d'argument :
|
* <p><b>Admin uniquement</b>. Dissout l'équipe spécifiée. Aucun check de chef
|
||||||
* l'équipe ciblée est déduite du joueur.
|
* — l'action est gated par la permission {@code crcore.team.delete}.
|
||||||
*
|
|
||||||
* <p>Aliases : {@code disband}, {@code dissolve}.
|
|
||||||
*/
|
*/
|
||||||
public class TeamDeleteSubCommand extends SubCommand {
|
public class TeamDeleteSubCommand extends SubCommand {
|
||||||
|
|
||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamDeleteSubCommand(TeamService service) {
|
public TeamDeleteSubCommand(TeamService service) {
|
||||||
super("delete", "disband", "dissolve");
|
super("delete");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Dissoudre son équipe (chef uniquement)");
|
description("Dissoudre une équipe (admin)");
|
||||||
playerOnly();
|
permission("crcore.team.delete");
|
||||||
|
argument("team", TeamArgumentTypes.teamByName(service));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandResult execute(CommandContext ctx) {
|
public CommandResult execute(CommandContext ctx) {
|
||||||
Player player = ctx.requirePlayer();
|
Team team = ctx.get("team");
|
||||||
Team team = service.getTeamOfPlayer(player.getUniqueId())
|
|
||||||
.orElse(null);
|
|
||||||
if (team == null) {
|
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
|
||||||
}
|
|
||||||
if (!team.getLeaderId().equals(player.getUniqueId())) {
|
|
||||||
return CommandResult.failure("Seul le chef peut dissoudre l'équipe.");
|
|
||||||
}
|
|
||||||
service.dissolveTeam(team.getId());
|
service.dissolveTeam(team.getId());
|
||||||
return CommandResult.success("Équipe " + team.getName() + " dissoute.");
|
return CommandResult.success("Équipe " + team.getName() + " dissoute.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class TeamGroupSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamGroupSubCommand(TeamService service) {
|
public TeamGroupSubCommand(TeamService service) {
|
||||||
super("team", "t");
|
super("team");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Gestion des équipes");
|
description("Gestion des équipes");
|
||||||
registerDefaults();
|
registerDefaults();
|
||||||
@@ -43,6 +43,7 @@ public class TeamGroupSubCommand extends SubCommand {
|
|||||||
addSubCommand(new TeamInfoSubCommand(service));
|
addSubCommand(new TeamInfoSubCommand(service));
|
||||||
addSubCommand(new TeamListSubCommand(service));
|
addSubCommand(new TeamListSubCommand(service));
|
||||||
addSubCommand(new TeamTransferSubCommand(service));
|
addSubCommand(new TeamTransferSubCommand(service));
|
||||||
|
addSubCommand(new TeamSetLeaderSubCommand(service));
|
||||||
addSubCommand(new TeamVisibilitySubCommand(service));
|
addSubCommand(new TeamVisibilitySubCommand(service));
|
||||||
addSubCommand(new TeamScoreSubCommand(service));
|
addSubCommand(new TeamScoreSubCommand(service));
|
||||||
addSubCommand(new TeamTopSubCommand(service));
|
addSubCommand(new TeamTopSubCommand(service));
|
||||||
|
|||||||
@@ -24,9 +24,10 @@ public class TeamInfoSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamInfoSubCommand(TeamService service) {
|
public TeamInfoSubCommand(TeamService service) {
|
||||||
super("info", "i");
|
super("info");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Afficher les infos d'une équipe");
|
description("Afficher les infos d'une équipe");
|
||||||
|
permission("crcore.team.info");
|
||||||
optionalArgument("name", TeamArgumentTypes.teamByName(service));
|
optionalArgument("name", TeamArgumentTypes.teamByName(service));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ public class TeamJoinSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamJoinSubCommand(TeamService service) {
|
public TeamJoinSubCommand(TeamService service) {
|
||||||
super("join", "j");
|
super("join");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Rejoindre une équipe publique");
|
description("Rejoindre une équipe publique");
|
||||||
|
permission("crcore.team.join");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
argument("name", TeamArgumentTypes.teamByName(service));
|
argument("name", TeamArgumentTypes.teamByName(service));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ public class TeamLeaveSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamLeaveSubCommand(TeamService service) {
|
public TeamLeaveSubCommand(TeamService service) {
|
||||||
super("leave", "quit");
|
super("leave");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Quitter son équipe");
|
description("Quitter son équipe");
|
||||||
|
permission("crcore.team.leave");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ public class TeamLeaveSubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (team.getLeaderId().equals(player.getUniqueId())) {
|
if (team.isLeader(player.getUniqueId())) {
|
||||||
return CommandResult.failure(
|
return CommandResult.failure(
|
||||||
"Vous êtes le chef. Transférez le leadership avec /core team transfer <player>, ou dissolvez avec /core team delete.");
|
"Vous êtes le chef. Transférez le leadership avec /core team transfer <player>, ou dissolvez avec /core team delete.");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ public class TeamListSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamListSubCommand(TeamService service) {
|
public TeamListSubCommand(TeamService service) {
|
||||||
super("list", "ls");
|
super("list");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Lister toutes les équipes");
|
description("Lister toutes les équipes");
|
||||||
|
permission("crcore.team.list");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ public class TeamRemoveSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamRemoveSubCommand(TeamService service) {
|
public TeamRemoveSubCommand(TeamService service) {
|
||||||
super("remove", "kick", "expel");
|
super("remove");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Retirer un joueur de son équipe (chef uniquement)");
|
description("Retirer un joueur de son équipe (chef uniquement)");
|
||||||
|
permission("crcore.team.remove");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
argument("player", ArgumentTypes.STRING);
|
argument("player", ArgumentTypes.STRING);
|
||||||
}
|
}
|
||||||
@@ -39,7 +40,7 @@ public class TeamRemoveSubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (!team.getLeaderId().equals(executor.getUniqueId())) {
|
if (!team.isLeader(executor.getUniqueId())) {
|
||||||
return CommandResult.failure("Seul le chef peut retirer des membres.");
|
return CommandResult.failure("Seul le chef peut retirer des membres.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package fr.luc.crcore.command.builtin.team;
|
||||||
|
|
||||||
|
import fr.luc.crcore.command.ArgumentTypes;
|
||||||
|
import fr.luc.crcore.command.CommandContext;
|
||||||
|
import fr.luc.crcore.command.CommandResult;
|
||||||
|
import fr.luc.crcore.command.SubCommand;
|
||||||
|
import fr.luc.crcore.team.Team;
|
||||||
|
import fr.luc.crcore.team.TeamService;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code /core team setleader <team> <player>}
|
||||||
|
*
|
||||||
|
* <p><b>Admin uniquement</b>. Assigne un joueur comme chef d'une équipe :
|
||||||
|
* <ul>
|
||||||
|
* <li>Si l'équipe est leaderless → le joueur devient chef (auto-ajouté
|
||||||
|
* comme membre s'il ne l'est pas).</li>
|
||||||
|
* <li>Si l'équipe a déjà un chef → l'ancien chef est démis en simple
|
||||||
|
* membre, le nouveau prend le rôle.</li>
|
||||||
|
* <li>Si {@code <player>} n'est pas encore membre → il est auto-ajouté
|
||||||
|
* à l'équipe en tant que chef.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>L'évènement {@code TeamLeadershipTransferEvent} est tiré dans tous les
|
||||||
|
* cas (avec {@code oldLeaderId} vide si la team était leaderless).
|
||||||
|
*/
|
||||||
|
public class TeamSetLeaderSubCommand extends SubCommand {
|
||||||
|
|
||||||
|
protected final TeamService service;
|
||||||
|
|
||||||
|
public TeamSetLeaderSubCommand(TeamService service) {
|
||||||
|
super("setleader");
|
||||||
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
|
description("Assigner / changer le chef d'une équipe (admin)");
|
||||||
|
permission("crcore.team.setleader");
|
||||||
|
argument("team", TeamArgumentTypes.teamByName(service));
|
||||||
|
argument("player", ArgumentTypes.ONLINE_PLAYER);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandResult execute(CommandContext ctx) {
|
||||||
|
Team team = ctx.get("team");
|
||||||
|
Player target = ctx.get("player");
|
||||||
|
|
||||||
|
boolean changed = service.setLeader(team.getId(), target.getUniqueId());
|
||||||
|
if (!changed) {
|
||||||
|
return CommandResult.success(target.getName() + " est déjà chef de " + team.getName() + ".");
|
||||||
|
}
|
||||||
|
return CommandResult.success(target.getName() + " est désormais chef de " + team.getName() + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,9 +19,10 @@ public class TeamSetSpawnSubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamSetSpawnSubCommand(TeamService service) {
|
public TeamSetSpawnSubCommand(TeamService service) {
|
||||||
super("setspawn", "spawn");
|
super("setspawn");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Définir le point de spawn de l'équipe (chef uniquement)");
|
description("Définir le point de spawn de l'équipe (chef uniquement)");
|
||||||
|
permission("crcore.team.setspawn");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ public class TeamSetSpawnSubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (!team.getLeaderId().equals(player.getUniqueId())) {
|
if (!team.isLeader(player.getUniqueId())) {
|
||||||
return CommandResult.failure("Seul le chef peut définir le spawn.");
|
return CommandResult.failure("Seul le chef peut définir le spawn.");
|
||||||
}
|
}
|
||||||
service.setSpawnPoint(team.getId(), player.getLocation());
|
service.setSpawnPoint(team.getId(), player.getLocation());
|
||||||
|
|||||||
@@ -28,10 +28,11 @@ public class TeamTopSubCommand extends SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TeamTopSubCommand(TeamService service, int limit) {
|
public TeamTopSubCommand(TeamService service, int limit) {
|
||||||
super("top", "ranking", "leaderboard");
|
super("top");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
this.limit = limit;
|
this.limit = limit;
|
||||||
description("Classement des équipes");
|
description("Classement des équipes");
|
||||||
|
permission("crcore.team.top");
|
||||||
optionalArgument("score", ArgumentTypes.STRING);
|
optionalArgument("score", ArgumentTypes.STRING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ public class TeamTransferSubCommand extends SubCommand {
|
|||||||
public TeamTransferSubCommand(TeamService service) {
|
public TeamTransferSubCommand(TeamService service) {
|
||||||
super("transfer");
|
super("transfer");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Transférer le rôle de chef à un autre membre");
|
description("Transférer le rôle de chef à un autre membre (chef uniquement)");
|
||||||
|
permission("crcore.team.transfer");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
argument("player", ArgumentTypes.STRING);
|
argument("player", ArgumentTypes.STRING);
|
||||||
}
|
}
|
||||||
@@ -37,7 +38,7 @@ public class TeamTransferSubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (!team.getLeaderId().equals(executor.getUniqueId())) {
|
if (!team.isLeader(executor.getUniqueId())) {
|
||||||
return CommandResult.failure("Seul le chef peut transférer le leadership.");
|
return CommandResult.failure("Seul le chef peut transférer le leadership.");
|
||||||
}
|
}
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
|||||||
@@ -22,9 +22,10 @@ public class TeamVisibilitySubCommand extends SubCommand {
|
|||||||
protected final TeamService service;
|
protected final TeamService service;
|
||||||
|
|
||||||
public TeamVisibilitySubCommand(TeamService service) {
|
public TeamVisibilitySubCommand(TeamService service) {
|
||||||
super("visibility", "vis");
|
super("visibility");
|
||||||
this.service = Objects.requireNonNull(service, "service");
|
this.service = Objects.requireNonNull(service, "service");
|
||||||
description("Changer la visibilité de son équipe");
|
description("Changer la visibilité de son équipe (chef uniquement)");
|
||||||
|
permission("crcore.team.visibility");
|
||||||
playerOnly();
|
playerOnly();
|
||||||
argument("visibility", ArgumentTypes.enumOf(TeamVisibility.class));
|
argument("visibility", ArgumentTypes.enumOf(TeamVisibility.class));
|
||||||
}
|
}
|
||||||
@@ -37,7 +38,7 @@ public class TeamVisibilitySubCommand extends SubCommand {
|
|||||||
if (team == null) {
|
if (team == null) {
|
||||||
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
return CommandResult.failure("Vous n'appartenez à aucune équipe.");
|
||||||
}
|
}
|
||||||
if (!team.getLeaderId().equals(player.getUniqueId())) {
|
if (!team.isLeader(player.getUniqueId())) {
|
||||||
return CommandResult.failure("Seul le chef peut changer la visibilité.");
|
return CommandResult.failure("Seul le chef peut changer la visibilité.");
|
||||||
}
|
}
|
||||||
service.setVisibility(team.getId(), visibility);
|
service.setVisibility(team.getId(), visibility);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public class SqliteTeamRepository extends InMemoryTeamRepository {
|
|||||||
.column("name", ColumnType.TEXT).notNull().unique()
|
.column("name", ColumnType.TEXT).notNull().unique()
|
||||||
.column("tag", ColumnType.TEXT).notNull().unique()
|
.column("tag", ColumnType.TEXT).notNull().unique()
|
||||||
.column("color", ColumnType.TEXT).notNull()
|
.column("color", ColumnType.TEXT).notNull()
|
||||||
.column("leader_id", ColumnType.UUID).notNull()
|
.column("leader_id", ColumnType.UUID) // nullable — équipe leaderless autorisée
|
||||||
.column("visibility", ColumnType.TEXT).notNull()
|
.column("visibility", ColumnType.TEXT).notNull()
|
||||||
.column("spawn_world", ColumnType.TEXT)
|
.column("spawn_world", ColumnType.TEXT)
|
||||||
.column("spawn_x", ColumnType.REAL)
|
.column("spawn_x", ColumnType.REAL)
|
||||||
@@ -83,7 +83,7 @@ public class SqliteTeamRepository extends InMemoryTeamRepository {
|
|||||||
rs.getString("name"),
|
rs.getString("name"),
|
||||||
rs.getString("tag"),
|
rs.getString("tag"),
|
||||||
TeamColor.valueOf(rs.getString("color")),
|
TeamColor.valueOf(rs.getString("color")),
|
||||||
UUID.fromString(rs.getString("leader_id")),
|
rs.getString("leader_id") == null ? null : UUID.fromString(rs.getString("leader_id")),
|
||||||
TeamVisibility.valueOf(rs.getString("visibility")),
|
TeamVisibility.valueOf(rs.getString("visibility")),
|
||||||
rs.getString("spawn_world"),
|
rs.getString("spawn_world"),
|
||||||
(Double) rs.getObject("spawn_x"),
|
(Double) rs.getObject("spawn_x"),
|
||||||
@@ -109,9 +109,9 @@ public class SqliteTeamRepository extends InMemoryTeamRepository {
|
|||||||
// Le leader est ajouté par le constructeur de Team avec role LEADER.
|
// Le leader est ajouté par le constructeur de Team avec role LEADER.
|
||||||
// On ajoute les autres membres manuellement via addMember (qui les marque MEMBER).
|
// On ajoute les autres membres manuellement via addMember (qui les marque MEMBER).
|
||||||
for (MemberRow m : members) {
|
for (MemberRow m : members) {
|
||||||
if (!m.playerId.equals(row.leaderId)) {
|
// Skip le leader — il est déjà ajouté par le constructeur de Team.
|
||||||
team.addMember(m.playerId);
|
if (row.leaderId != null && m.playerId.equals(row.leaderId)) continue;
|
||||||
}
|
team.addMember(m.playerId);
|
||||||
}
|
}
|
||||||
// Scores
|
// Scores
|
||||||
db.query(
|
db.query(
|
||||||
@@ -174,7 +174,7 @@ public class SqliteTeamRepository extends InMemoryTeamRepository {
|
|||||||
" spawn_world, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch) " +
|
" spawn_world, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch) " +
|
||||||
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
team.getId(), team.getName(), team.getTag(), team.getColor(),
|
team.getId(), team.getName(), team.getTag(), team.getColor(),
|
||||||
team.getLeaderId(), team.getVisibility(),
|
team.getLeaderId().orElse(null), team.getVisibility(),
|
||||||
spawnWorld, spawnX, spawnY, spawnZ, spawnYaw, spawnPitch
|
spawnWorld, spawnX, spawnY, spawnZ, spawnYaw, spawnPitch
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -40,21 +40,39 @@ public class Team extends AbstractEntity implements Named, ScoreHolder {
|
|||||||
private TeamVisibility visibility;
|
private TeamVisibility visibility;
|
||||||
private Location spawnPoint;
|
private Location spawnPoint;
|
||||||
|
|
||||||
|
/** Crée une équipe <b>sans chef</b>, visibilité {@link TeamVisibility#PRIVATE}. */
|
||||||
|
public Team(UUID id, String name, String tag, TeamColor color) {
|
||||||
|
this(id, name, tag, color, null, TeamVisibility.PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Crée une équipe <b>sans chef</b> avec la visibilité spécifiée. */
|
||||||
|
public Team(UUID id, String name, String tag, TeamColor color, TeamVisibility visibility) {
|
||||||
|
this(id, name, tag, color, null, visibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Crée une équipe avec chef, visibilité {@link TeamVisibility#PRIVATE}. */
|
||||||
public Team(UUID id, String name, String tag, TeamColor color, UUID leaderId) {
|
public Team(UUID id, String name, String tag, TeamColor color, UUID leaderId) {
|
||||||
this(id, name, tag, color, leaderId, TeamVisibility.PRIVATE);
|
this(id, name, tag, color, leaderId, TeamVisibility.PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une équipe. {@code leaderId} peut être {@code null} pour créer une
|
||||||
|
* équipe leaderless (cas typique : l'admin crée une équipe et assignera le
|
||||||
|
* chef plus tard via {@code setLeader}).
|
||||||
|
*/
|
||||||
public Team(UUID id, String name, String tag, TeamColor color, UUID leaderId,
|
public Team(UUID id, String name, String tag, TeamColor color, UUID leaderId,
|
||||||
TeamVisibility visibility) {
|
TeamVisibility visibility) {
|
||||||
super(id);
|
super(id);
|
||||||
this.name = Objects.requireNonNull(name, "name");
|
this.name = Objects.requireNonNull(name, "name");
|
||||||
this.tag = Objects.requireNonNull(tag, "tag");
|
this.tag = Objects.requireNonNull(tag, "tag");
|
||||||
this.color = Objects.requireNonNull(color, "color");
|
this.color = Objects.requireNonNull(color, "color");
|
||||||
this.leaderId = Objects.requireNonNull(leaderId, "leaderId");
|
|
||||||
this.visibility = Objects.requireNonNull(visibility, "visibility");
|
this.visibility = Objects.requireNonNull(visibility, "visibility");
|
||||||
|
this.leaderId = leaderId; // nullable
|
||||||
this.members = new HashSet<>();
|
this.members = new HashSet<>();
|
||||||
this.scores = new HashMap<>();
|
this.scores = new HashMap<>();
|
||||||
this.members.add(newMember(leaderId, TeamRole.LEADER));
|
if (leaderId != null) {
|
||||||
|
this.members.add(newMember(leaderId, TeamRole.LEADER));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Override to instantiate a custom TeamMember subclass. */
|
/** Override to instantiate a custom TeamMember subclass. */
|
||||||
@@ -75,8 +93,19 @@ public class Team extends AbstractEntity implements Named, ScoreHolder {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getLeaderId() {
|
/** L'UUID du chef si la team en a un, sinon {@link Optional#empty()}. */
|
||||||
return leaderId;
|
public Optional<UUID> getLeaderId() {
|
||||||
|
return Optional.ofNullable(leaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@code true} si la team a un chef défini. */
|
||||||
|
public boolean hasLeader() {
|
||||||
|
return leaderId != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** {@code true} si {@code playerId} est l'UUID du chef actuel. */
|
||||||
|
public boolean isLeader(UUID playerId) {
|
||||||
|
return leaderId != null && leaderId.equals(playerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TeamVisibility getVisibility() {
|
public TeamVisibility getVisibility() {
|
||||||
@@ -91,9 +120,10 @@ public class Team extends AbstractEntity implements Named, ScoreHolder {
|
|||||||
return visibility.isPublic();
|
return visibility.isPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TeamMember getLeader() {
|
/** Le {@link TeamMember} chef si la team en a un, sinon {@link Optional#empty()}. */
|
||||||
return getMember(leaderId).orElseThrow(
|
public Optional<TeamMember> getLeader() {
|
||||||
() -> new IllegalStateException("Team has no leader: " + getId()));
|
if (leaderId == null) return Optional.empty();
|
||||||
|
return getMember(leaderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<TeamMember> getMembers() {
|
public Set<TeamMember> getMembers() {
|
||||||
@@ -135,15 +165,26 @@ public class Team extends AbstractEntity implements Named, ScoreHolder {
|
|||||||
return members.removeIf(member -> member.getPlayerId().equals(playerId));
|
return members.removeIf(member -> member.getPlayerId().equals(playerId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfert classique du leadership : le nouveau chef doit <b>déjà</b> être
|
||||||
|
* membre de l'équipe, et l'équipe doit avoir un chef actuel.
|
||||||
|
*
|
||||||
|
* <p>Pour un cas plus général (équipe leaderless, ou nouveau chef non
|
||||||
|
* encore membre), utiliser {@link #setLeader(UUID)}.
|
||||||
|
*/
|
||||||
public void transferLeadership(UUID newLeaderId) {
|
public void transferLeadership(UUID newLeaderId) {
|
||||||
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
||||||
|
if (leaderId == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot transfer leadership on a leaderless team — use setLeader instead.");
|
||||||
|
}
|
||||||
if (newLeaderId.equals(leaderId)) {
|
if (newLeaderId.equals(leaderId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TeamMember newLeader = getMember(newLeaderId).orElseThrow(
|
TeamMember newLeader = getMember(newLeaderId).orElseThrow(
|
||||||
() -> new IllegalArgumentException(
|
() -> new IllegalArgumentException(
|
||||||
"New leader must already be a member of the team."));
|
"New leader must already be a member of the team."));
|
||||||
TeamMember oldLeader = getLeader();
|
TeamMember oldLeader = getLeader().orElseThrow();
|
||||||
members.remove(oldLeader);
|
members.remove(oldLeader);
|
||||||
members.remove(newLeader);
|
members.remove(newLeader);
|
||||||
members.add(oldLeader.withRole(TeamRole.MEMBER));
|
members.add(oldLeader.withRole(TeamRole.MEMBER));
|
||||||
@@ -151,6 +192,39 @@ public class Team extends AbstractEntity implements Named, ScoreHolder {
|
|||||||
this.leaderId = newLeaderId;
|
this.leaderId = newLeaderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigne un chef à l'équipe, plus flexible que {@link #transferLeadership} :
|
||||||
|
* <ul>
|
||||||
|
* <li>Si la team est leaderless → ajoute {@code playerId} comme chef
|
||||||
|
* (en tant que membre s'il ne l'est pas déjà).</li>
|
||||||
|
* <li>Si la team a déjà un chef → démet l'ancien en {@link TeamRole#MEMBER},
|
||||||
|
* promeut {@code playerId} en {@link TeamRole#LEADER} (auto-ajout
|
||||||
|
* si pas membre).</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public void setLeader(UUID newLeaderId) {
|
||||||
|
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
||||||
|
if (newLeaderId.equals(leaderId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Démet l'ancien chef si présent.
|
||||||
|
if (leaderId != null) {
|
||||||
|
TeamMember oldLeader = getLeader().orElseThrow();
|
||||||
|
members.remove(oldLeader);
|
||||||
|
members.add(oldLeader.withRole(TeamRole.MEMBER));
|
||||||
|
}
|
||||||
|
// Ajoute ou promeut le nouveau chef.
|
||||||
|
Optional<TeamMember> existing = getMember(newLeaderId);
|
||||||
|
if (existing.isPresent()) {
|
||||||
|
TeamMember newLeader = existing.get();
|
||||||
|
members.remove(newLeader);
|
||||||
|
members.add(newLeader.withRole(TeamRole.LEADER));
|
||||||
|
} else {
|
||||||
|
members.add(newMember(newLeaderId, TeamRole.LEADER));
|
||||||
|
}
|
||||||
|
this.leaderId = newLeaderId;
|
||||||
|
}
|
||||||
|
|
||||||
// ---- Scores ----
|
// ---- Scores ----
|
||||||
|
|
||||||
public int getScore(String scoreName) {
|
public int getScore(String scoreName) {
|
||||||
|
|||||||
@@ -25,8 +25,19 @@ public interface TeamService {
|
|||||||
|
|
||||||
// ---- Lifecycle ----
|
// ---- Lifecycle ----
|
||||||
|
|
||||||
|
/** Crée une équipe <b>sans chef</b>, visibilité PRIVATE. */
|
||||||
|
Team createTeam(String name, String tag, TeamColor color);
|
||||||
|
|
||||||
|
/** Crée une équipe <b>sans chef</b> avec la visibilité spécifiée. */
|
||||||
|
Team createTeam(String name, String tag, TeamColor color, TeamVisibility visibility);
|
||||||
|
|
||||||
|
/** Crée une équipe avec chef, visibilité PRIVATE. */
|
||||||
Team createTeam(String name, String tag, TeamColor color, UUID leaderId);
|
Team createTeam(String name, String tag, TeamColor color, UUID leaderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée une équipe. {@code leaderId} peut être {@code null} (équipe
|
||||||
|
* leaderless — l'admin assignera plus tard via {@link #setLeader}).
|
||||||
|
*/
|
||||||
Team createTeam(String name, String tag, TeamColor color, UUID leaderId,
|
Team createTeam(String name, String tag, TeamColor color, UUID leaderId,
|
||||||
TeamVisibility visibility);
|
TeamVisibility visibility);
|
||||||
|
|
||||||
@@ -42,6 +53,17 @@ public interface TeamService {
|
|||||||
|
|
||||||
boolean transferLeadership(UUID teamId, UUID newLeaderId);
|
boolean transferLeadership(UUID teamId, UUID newLeaderId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigne un chef à l'équipe (admin). Plus permissif que
|
||||||
|
* {@link #transferLeadership} : accepte un nouveau chef qui n'est pas
|
||||||
|
* encore membre (il est auto-ajouté), et fonctionne aussi sur une équipe
|
||||||
|
* leaderless.
|
||||||
|
*
|
||||||
|
* @return {@code true} si le chef a changé, {@code false} si
|
||||||
|
* {@code newLeaderId} était déjà le chef actuel.
|
||||||
|
*/
|
||||||
|
boolean setLeader(UUID teamId, UUID newLeaderId);
|
||||||
|
|
||||||
void setVisibility(UUID teamId, TeamVisibility visibility);
|
void setVisibility(UUID teamId, TeamVisibility visibility);
|
||||||
|
|
||||||
// ---- Scores ----
|
// ---- Scores ----
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ public class TeamServiceImpl implements TeamService {
|
|||||||
|
|
||||||
// ---- Lifecycle ----
|
// ---- Lifecycle ----
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Team createTeam(String name, String tag, TeamColor color) {
|
||||||
|
return createTeam(name, tag, color, null, TeamVisibility.PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Team createTeam(String name, String tag, TeamColor color, TeamVisibility visibility) {
|
||||||
|
return createTeam(name, tag, color, null, visibility);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Team createTeam(String name, String tag, TeamColor color, UUID leaderId) {
|
public Team createTeam(String name, String tag, TeamColor color, UUID leaderId) {
|
||||||
return createTeam(name, tag, color, leaderId, TeamVisibility.PRIVATE);
|
return createTeam(name, tag, color, leaderId, TeamVisibility.PRIVATE);
|
||||||
@@ -37,7 +47,9 @@ public class TeamServiceImpl implements TeamService {
|
|||||||
TeamVisibility visibility) {
|
TeamVisibility visibility) {
|
||||||
validateName(name);
|
validateName(name);
|
||||||
validateTag(tag);
|
validateTag(tag);
|
||||||
validateLeader(leaderId);
|
if (leaderId != null) {
|
||||||
|
validateLeader(leaderId);
|
||||||
|
}
|
||||||
Objects.requireNonNull(visibility, "visibility");
|
Objects.requireNonNull(visibility, "visibility");
|
||||||
|
|
||||||
Team team = newTeam(UUID.randomUUID(), name, tag, color, leaderId, visibility);
|
Team team = newTeam(UUID.randomUUID(), name, tag, color, leaderId, visibility);
|
||||||
@@ -102,13 +114,25 @@ public class TeamServiceImpl implements TeamService {
|
|||||||
public boolean transferLeadership(UUID teamId, UUID newLeaderId) {
|
public boolean transferLeadership(UUID teamId, UUID newLeaderId) {
|
||||||
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
||||||
Team team = requireTeam(teamId);
|
Team team = requireTeam(teamId);
|
||||||
UUID oldLeaderId = team.getLeaderId();
|
UUID oldLeaderId = team.getLeaderId().orElse(null);
|
||||||
team.transferLeadership(newLeaderId);
|
team.transferLeadership(newLeaderId);
|
||||||
repository.save(team);
|
repository.save(team);
|
||||||
onLeadershipTransferred(team, oldLeaderId, newLeaderId);
|
onLeadershipTransferred(team, oldLeaderId, newLeaderId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setLeader(UUID teamId, UUID newLeaderId) {
|
||||||
|
Objects.requireNonNull(newLeaderId, "newLeaderId");
|
||||||
|
Team team = requireTeam(teamId);
|
||||||
|
UUID oldLeaderId = team.getLeaderId().orElse(null);
|
||||||
|
if (newLeaderId.equals(oldLeaderId)) return false;
|
||||||
|
team.setLeader(newLeaderId);
|
||||||
|
repository.save(team);
|
||||||
|
onLeadershipTransferred(team, oldLeaderId, newLeaderId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setVisibility(UUID teamId, TeamVisibility visibility) {
|
public void setVisibility(UUID teamId, TeamVisibility visibility) {
|
||||||
Objects.requireNonNull(visibility, "visibility");
|
Objects.requireNonNull(visibility, "visibility");
|
||||||
|
|||||||
@@ -4,9 +4,16 @@ import fr.luc.crcore.team.Team;
|
|||||||
import org.bukkit.event.HandlerList;
|
import org.bukkit.event.HandlerList;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/** Déclenché après un transfert de leadership. {@link #getOldLeaderId()} et {@link #getNewLeaderId()} renvoient les UUID des deux joueurs. */
|
/**
|
||||||
|
* Déclenché après changement du chef d'une équipe.
|
||||||
|
*
|
||||||
|
* <p>{@code oldLeaderId} peut être {@code null} si l'équipe était leaderless
|
||||||
|
* avant l'opération (cas typique : admin assigne un premier chef après
|
||||||
|
* création via {@code setLeader}). {@code newLeaderId} est toujours non-null.
|
||||||
|
*/
|
||||||
public class TeamLeadershipTransferEvent extends TeamEvent {
|
public class TeamLeadershipTransferEvent extends TeamEvent {
|
||||||
|
|
||||||
private static final HandlerList HANDLERS = new HandlerList();
|
private static final HandlerList HANDLERS = new HandlerList();
|
||||||
@@ -16,12 +23,18 @@ public class TeamLeadershipTransferEvent extends TeamEvent {
|
|||||||
|
|
||||||
public TeamLeadershipTransferEvent(Team team, UUID oldLeaderId, UUID newLeaderId) {
|
public TeamLeadershipTransferEvent(Team team, UUID oldLeaderId, UUID newLeaderId) {
|
||||||
super(team);
|
super(team);
|
||||||
this.oldLeaderId = Objects.requireNonNull(oldLeaderId, "oldLeaderId");
|
this.oldLeaderId = oldLeaderId; // nullable
|
||||||
this.newLeaderId = Objects.requireNonNull(newLeaderId, "newLeaderId");
|
this.newLeaderId = Objects.requireNonNull(newLeaderId, "newLeaderId");
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getOldLeaderId() { return oldLeaderId; }
|
/** L'ancien chef. Vide si l'équipe était leaderless avant. */
|
||||||
public UUID getNewLeaderId() { return newLeaderId; }
|
public Optional<UUID> getOldLeaderId() {
|
||||||
|
return Optional.ofNullable(oldLeaderId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getNewLeaderId() {
|
||||||
|
return newLeaderId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HandlerList getHandlers() {
|
public HandlerList getHandlers() {
|
||||||
|
|||||||
Reference in New Issue
Block a user