feat: typed team settings (cascade per-team → global → default) + in-game GUI
New fr.luc.crcore.team.config module: - TeamSetting<T> (typed, with key/type/default/parser/serializer; factories ofBoolean/ofInt/ofString/ofEnum). - TeamSettings registry: 8 standard settings (FRIENDLY_FIRE, PVP_PROTECTION_SECONDS, MAX_SIZE, MIN_SIZE, RESPAWN_AT_TEAM_SPAWN, TEAM_CHAT_ENABLED, SHOW_TAG_ABOVE_HEAD, TEAM_COLOR_IN_NAME), extensible via register() for game plugins. - TeamConfigService interface with cascade get(team, setting) → per-team override (SQLite) → global YAML → hard default. Persists per- team via TeamRepository.save(), global via YamlConfiguration.save(). - YamlTeamConfigService default impl with bundled crcore-team-config.yml. Storage: - Team.getSettings() Map<String, Object> for per-team overrides. - New SQLite table crcore_team_settings (team_id, key, value, type) with load + write-through persist in SqliteTeamRepository. - Global YAML <plugin>-team-config.yml in dataFolder, auto-created at first boot (template from game plugin's resource of the same name takes priority). New reusable GUI framework fr.luc.crcore.gui: - AbstractInventoryGui (implements InventoryHolder, rebuild() abstract, setButton/setDecoration/clearSlot helpers, onClose hook, openTo()). - GuiClickHandler FunctionalInterface. - GuiListener (single Bukkit listener, detects via getHolder(), ALWAYS cancels clicks even on slots without handlers). - GuiItems builder (named/of/filler + lore/amount/build, '&' color codes translated). Concrete settings GUIs (fr.luc.crcore.team.config.gui): - AbstractSettingsGui base renderer: 27 slots, settings in row 2, booleans = LIME_DYE / GRAY_DYE toggle, integers = BOOK with left +1 / right -1 (shift × 10), strings/enums display-only. - GlobalSettingsGui: writes to YAML on each change. - TeamSettingsGui: writes to per-team overrides, "override active" flag in lore when value differs from global, "Reset all overrides" footer button. New /core team settings [team] subcommand: - No arg → GlobalSettingsGui (perm crcore.team.settings.global). - With arg → TeamSettingsGui (perm crcore.team.settings). - Player-only (Bukkit needs HumanEntity to open inventory). - Lives under /core team to stay modular (objective: split into modules later; everything team-related under /core team). CRCore: buildTeamConfigService() override point, teamConfig()/getTeamConfig() getters, GuiListener.registerOn(plugin) at enable(). CoreCommand, TeamGroupSubCommand and CoreReloadSubCommand extended to receive TeamConfigService. /core reload now reloads messages + broadcasts + team-config. Docs: new section 10 "Paramètres d'équipe", new decisions logged, setup.md tree updated, two new diagrams (team-config + gui). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+114
-1
@@ -807,7 +807,120 @@ depuis les fichiers user du dataFolder. Les defaults en jar ne bougent pas
|
||||
|
||||
---
|
||||
|
||||
## 10. Bootstrap `CRCore`
|
||||
## 10. Paramètres d'équipe (`fr.luc.crcore.team.config`)
|
||||
|
||||
**Statut** : implémenté. 8 settings standards + GUI in-game + cascade
|
||||
per-team → global → default.
|
||||
|
||||
### Modèle de résolution
|
||||
|
||||
```
|
||||
1. hard default défini en code dans TeamSettings (constantes)
|
||||
2. global config <plugin>-team-config.yml ← admin via GUI ou YAML
|
||||
3. per-team override table SQLite crcore_team_settings ← admin via GUI
|
||||
```
|
||||
|
||||
`config.get(team, setting)` cascade per-team → global → default.
|
||||
`config.getGlobal(setting)` cascade global → default (skip per-team).
|
||||
Toutes les valeurs retournées sont **non-null** grâce au default en bout
|
||||
de chaîne.
|
||||
|
||||
### Settings standards (`TeamSettings`)
|
||||
|
||||
| Constante | Clé YAML/SQL | Type | Défaut |
|
||||
|---|---|---|---|
|
||||
| `FRIENDLY_FIRE` | `friendly_fire` | bool | `false` |
|
||||
| `PVP_PROTECTION_SECONDS` | `pvp_protection_seconds` | int | `0` |
|
||||
| `MAX_SIZE` | `max_size` | int | `0` (illimité) |
|
||||
| `MIN_SIZE` | `min_size` | int | `0` |
|
||||
| `RESPAWN_AT_TEAM_SPAWN` | `respawn_at_team_spawn` | bool | `true` |
|
||||
| `TEAM_CHAT_ENABLED` | `team_chat_enabled` | bool | `true` |
|
||||
| `SHOW_TAG_ABOVE_HEAD` | `show_tag_above_head` | bool | `true` |
|
||||
| `TEAM_COLOR_IN_NAME` | `team_color_in_name` | bool | `true` |
|
||||
|
||||
CR-Core fournit les défauts ; **c'est au plugin de jeu d'appliquer ces
|
||||
settings** dans sa logique (ex. écouter `EntityDamageByEntityEvent` et
|
||||
checker `config.get(team, FRIENDLY_FIRE)` pour décider si le coup passe).
|
||||
|
||||
### API typée
|
||||
|
||||
```java
|
||||
boolean ff = core.teamConfig().get(team, TeamSettings.FRIENDLY_FIRE);
|
||||
int max = core.teamConfig().getGlobal(TeamSettings.MAX_SIZE);
|
||||
core.teamConfig().setPerTeam(team, TeamSettings.FRIENDLY_FIRE, true);
|
||||
core.teamConfig().resetPerTeam(team, TeamSettings.FRIENDLY_FIRE);
|
||||
core.teamConfig().setGlobal(TeamSettings.MAX_SIZE, 8); // persiste le YAML
|
||||
core.teamConfig().reload();
|
||||
```
|
||||
|
||||
### Settings custom (game plugin)
|
||||
|
||||
Un game plugin peut enregistrer ses propres settings :
|
||||
|
||||
```java
|
||||
public static final TeamSetting<Boolean> CITES_PVP_ROUND_END =
|
||||
TeamSetting.ofBoolean("cites_pvp_round_end", false);
|
||||
|
||||
@Override public void onEnable() {
|
||||
core = new CRCore(this).enable();
|
||||
TeamSettings.register(CITES_PVP_ROUND_END);
|
||||
}
|
||||
```
|
||||
|
||||
→ La clé apparaîtra automatiquement dans les GUI globaux et per-team, et
|
||||
sera persistée en SQLite + YAML comme les standards.
|
||||
|
||||
### Commande GUI
|
||||
|
||||
`/core team settings [team]` — player-only, ouvre l'interface graphique.
|
||||
|
||||
- Sans argument → **GUI globaux** (perm `crcore.team.settings.global`).
|
||||
Modif → écrit dans `<plugin>-team-config.yml`.
|
||||
- Avec argument → **GUI per-team** (perm `crcore.team.settings`). Modif →
|
||||
écrit en SQLite (overrides). Bouton "Reset tous les overrides" pour
|
||||
remettre toutes les valeurs au global.
|
||||
|
||||
### Mécaniques GUI
|
||||
|
||||
- **Inventaire 27 slots**, settings sur la ligne du milieu (slots 10..16).
|
||||
- **Booléens** : lampe verte (ON) / grise (OFF), clic = toggle.
|
||||
- **Entiers** : item livre.
|
||||
- Clic gauche = +1, shift = +10
|
||||
- Clic droit = -1, shift = -10
|
||||
- Clamp à 0 minimum
|
||||
- **Strings/Enums** : affichage seul (édition via YAML — pas dans la V1
|
||||
du GUI).
|
||||
- **Per-team** : indication visuelle "Override per-team actif" dans la
|
||||
lore quand une valeur est différente du global.
|
||||
- **Slot 22** : bouton Fermer.
|
||||
- **Slot 18** (per-team uniquement) : bouton "Reset tous les overrides".
|
||||
|
||||
### Framework GUI réutilisable
|
||||
|
||||
Le module `fr.luc.crcore.gui` est **générique** — réutilisable pour tout
|
||||
futur GUI CR-Core ou game plugin :
|
||||
|
||||
- `AbstractInventoryGui implements InventoryHolder` — base abstraite,
|
||||
`rebuild()`, `setButton(slot, item, handler)`, `setDecoration(...)`,
|
||||
`clearSlot(...)`, hook `onClose(...)`.
|
||||
- `GuiClickHandler` (FunctionalInterface) — handler de clic par slot.
|
||||
- `GuiListener` — un seul Listener Bukkit qui route les clics et les
|
||||
fermetures vers le bon GUI via `inventory.getHolder()`.
|
||||
- `GuiItems` — builder fluide `named(material, "&aTitre").lore(...).build()`,
|
||||
filler décoratif gris.
|
||||
|
||||
Pour faire un GUI custom : `extends AbstractInventoryGui`, créer
|
||||
l'inventaire dans le constructeur, override `rebuild()`. Le `GuiListener`
|
||||
est déjà enregistré par `CRCore.enable()`.
|
||||
|
||||
### Diagrammes
|
||||
|
||||
- [team-config-class-diagram.puml](diagrams/team-config-class-diagram.puml)
|
||||
- [gui-class-diagram.puml](diagrams/gui-class-diagram.puml)
|
||||
|
||||
---
|
||||
|
||||
## 11. Bootstrap `CRCore`
|
||||
|
||||
**Statut** : implémenté. Point d'entrée unique pour les plugins de jeu.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user