8b7cad3fce
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>
273 lines
9.7 KiB
Markdown
273 lines
9.7 KiB
Markdown
# Setup technique
|
|
|
|
## Stack
|
|
|
|
- **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`
|
|
|
|
## Dépôts Maven
|
|
|
|
- `papermc` — https://repo.papermc.io/repository/maven-public/
|
|
- `spigot-repo` — https://hub.spigotmc.org/nexus/content/repositories/snapshots/
|
|
- `sonatype` — https://oss.sonatype.org/content/groups/public/
|
|
|
|
## Build & install local
|
|
|
|
```bash
|
|
mvn clean install
|
|
```
|
|
|
|
Publie `fr.luc:CR-Core:1.0-SNAPSHOT` dans le repo Maven local `~/.m2/`,
|
|
prêt à être consommé par les plugins de jeu.
|
|
|
|
## Intégration dans un plugin de jeu
|
|
|
|
### `pom.xml`
|
|
|
|
```xml
|
|
<dependency>
|
|
<groupId>fr.luc</groupId>
|
|
<artifactId>CR-Core</artifactId>
|
|
<version>1.0-SNAPSHOT</version>
|
|
<scope>compile</scope>
|
|
</dependency>
|
|
```
|
|
|
|
Le plugin de jeu doit **shader** CR-Core dans son jar final (avec
|
|
`maven-shade-plugin`) pour que sqlite-jdbc + le code du noyau soient bien
|
|
embarqués sur le serveur.
|
|
|
|
### `plugin.yml` du plugin de jeu
|
|
|
|
```yaml
|
|
name: MyGame
|
|
main: fr.exemple.mygame.MyGamePlugin
|
|
version: 1.0
|
|
api-version: 1.16
|
|
```
|
|
|
|
> **Pas besoin de déclarer la commande `core`** : CR-Core l'enregistre
|
|
> dynamiquement via le `CommandMap` du serveur quand elle est absente du
|
|
> plugin.yml. Si tu préfères la déclarer quand même (pour customiser la
|
|
> description ou les aliases côté Bukkit), tu peux ajouter :
|
|
>
|
|
> ```yaml
|
|
> commands:
|
|
> core:
|
|
> description: Commandes CR-Core
|
|
> aliases: [cr, crcore]
|
|
> ```
|
|
>
|
|
> Dans ce cas, CR-Core détecte la commande déclarée et s'y branche
|
|
> normalement via `setExecutor` (pas d'enregistrement dynamique).
|
|
|
|
### Code minimal
|
|
|
|
```java
|
|
public class MyGamePlugin extends JavaPlugin {
|
|
|
|
private CRCore core;
|
|
|
|
@Override
|
|
public void onEnable() {
|
|
// 1 ligne = SQLite + services + /core team ... opérationnels
|
|
this.core = new CRCore(this).enable();
|
|
|
|
// Listener custom sur les events team
|
|
getServer().getPluginManager().registerEvents(new MyTeamListener(), this);
|
|
|
|
// Table custom pour stocker des données spécifiques au jeu
|
|
core.getDatabase().table("my_kills")
|
|
.ifNotExists()
|
|
.column("player_id", ColumnType.UUID).primaryKey()
|
|
.column("kills", ColumnType.INTEGER).notNull().defaultValue("0")
|
|
.create();
|
|
}
|
|
|
|
@Override
|
|
public void onDisable() {
|
|
if (core != null) core.disable();
|
|
}
|
|
}
|
|
```
|
|
|
|
### Écouter les évènements
|
|
|
|
```java
|
|
public class MyTeamListener implements Listener {
|
|
|
|
@EventHandler
|
|
public void onTeamCreate(TeamCreateEvent event) {
|
|
Team team = event.getTeam();
|
|
Bukkit.broadcastMessage("Nouvelle équipe : " + team.getName());
|
|
}
|
|
|
|
@EventHandler
|
|
public void onPlayerJoinTeam(PlayerJoinTeamEvent event) {
|
|
// Auto-join uniquement (chef qui ajoute = TeamMemberAddEvent)
|
|
}
|
|
|
|
@EventHandler
|
|
public void onScoreChange(TeamScoreChangeEvent event) {
|
|
// event.getScoreName(), event.getOldValue(), event.getNewValue()
|
|
}
|
|
}
|
|
```
|
|
|
|
### Overrider une commande par défaut
|
|
|
|
```java
|
|
public class MyCreateCommand extends TeamCreateSubCommand {
|
|
public MyCreateCommand(TeamService service) {
|
|
super(service);
|
|
permission("mygame.team.create"); // permission custom
|
|
}
|
|
|
|
@Override
|
|
public CommandResult execute(CommandContext ctx) {
|
|
// logique custom
|
|
return super.execute(ctx);
|
|
}
|
|
}
|
|
|
|
// Dans onEnable() :
|
|
core.getCoreCommand().findSubCommand("team")
|
|
.ifPresent(team -> team.replaceSubCommand("create", new MyCreateCommand(core.getTeamService())));
|
|
```
|
|
|
|
### Stocker / récupérer des données custom via SQLite
|
|
|
|
```java
|
|
Database db = core.getDatabase();
|
|
|
|
db.update("INSERT OR REPLACE INTO my_kills (player_id, kills) VALUES (?, ?)",
|
|
playerUuid, 42);
|
|
|
|
int kills = db.queryOne(
|
|
"SELECT kills FROM my_kills WHERE player_id = ?",
|
|
rs -> rs.getInt("kills"),
|
|
playerUuid
|
|
).orElse(0);
|
|
```
|
|
|
|
## Arborescence du projet
|
|
|
|
```
|
|
CitesPlugin/ # dossier IntelliJ (renommer plus tard si voulu)
|
|
├── pom.xml
|
|
├── GEMINI.md
|
|
├── docs/
|
|
│ ├── README.md
|
|
│ ├── setup.md
|
|
│ ├── features.md
|
|
│ ├── decisions.md
|
|
│ └── diagrams/*.puml
|
|
└── src/main/java/fr/luc/crcore/
|
|
├── CRCore.java # bootstrap orchestrator
|
|
├── CRCoreConfig.java # config (sqlite, command name, …)
|
|
├── common/
|
|
│ ├── Identifiable.java
|
|
│ ├── Named.java
|
|
│ ├── ScoreHolder.java # contrat partagé Team + PlayerProfile
|
|
│ ├── AbstractEntity.java
|
|
│ └── Repository.java
|
|
├── database/ # wrapper SQLite
|
|
│ ├── Database.java
|
|
│ ├── TableBuilder.java
|
|
│ ├── ColumnType.java
|
|
│ ├── RowMapper.java
|
|
│ └── DatabaseException.java
|
|
├── command/ # framework
|
|
│ ├── Command.java (interface)
|
|
│ ├── AbstractCommand.java # base partagée, nested sub-commands
|
|
│ ├── BaseCommand.java # top-level Bukkit-aware
|
|
│ ├── SubCommand.java
|
|
│ ├── CommandContext.java
|
|
│ ├── CommandResult.java
|
|
│ ├── CommandException.java
|
|
│ ├── ArgumentType.java
|
|
│ ├── ArgumentTypes.java
|
|
│ ├── ArgumentDef.java # package-private
|
|
│ └── builtin/ # commandes prêtes à l'emploi
|
|
│ ├── CoreCommand.java # /core
|
|
│ └── team/
|
|
│ ├── TeamGroupSubCommand.java # /core team (container)
|
|
│ ├── TeamArgumentTypes.java # ArgumentType<Team> avec tab-complete
|
|
│ ├── TeamCreateSubCommand.java # /core team create
|
|
│ ├── TeamDeleteSubCommand.java # /core team delete
|
|
│ ├── TeamAddSubCommand.java # /core team add
|
|
│ ├── TeamRemoveSubCommand.java # /core team remove
|
|
│ ├── TeamJoinSubCommand.java # /core team join
|
|
│ ├── TeamLeaveSubCommand.java # /core team leave
|
|
│ ├── TeamInfoSubCommand.java # /core team info
|
|
│ ├── TeamListSubCommand.java # /core team list
|
|
│ ├── TeamTransferSubCommand.java # /core team transfer
|
|
│ ├── TeamVisibilitySubCommand.java # /core team visibility
|
|
│ ├── TeamScoreSubCommand.java # /core team score (admin)
|
|
│ ├── TeamTopSubCommand.java # /core team top
|
|
│ └── TeamSetSpawnSubCommand.java # /core team setspawn
|
|
├── team/
|
|
│ ├── Team.java
|
|
│ ├── TeamMember.java
|
|
│ ├── TeamRole.java
|
|
│ ├── TeamColor.java
|
|
│ ├── TeamVisibility.java
|
|
│ ├── TeamRanking.java # record
|
|
│ ├── TeamRepository.java
|
|
│ ├── InMemoryTeamRepository.java
|
|
│ ├── SqliteTeamRepository.java
|
|
│ ├── TeamService.java
|
|
│ ├── TeamServiceImpl.java
|
|
│ ├── BukkitEventFiringTeamServiceImpl.java # impl par défaut
|
|
│ ├── TeamException.java
|
|
│ ├── TeamAlreadyExistsException.java
|
|
│ ├── TeamNotFoundException.java
|
|
│ ├── TeamAccessException.java
|
|
│ └── event/ # Bukkit events team
|
|
│ ├── TeamEvent.java # base
|
|
│ ├── TeamCreateEvent.java
|
|
│ ├── TeamDissolveEvent.java
|
|
│ ├── TeamMemberAddEvent.java
|
|
│ ├── TeamMemberRemoveEvent.java
|
|
│ ├── PlayerJoinTeamEvent.java
|
|
│ ├── TeamLeadershipTransferEvent.java
|
|
│ ├── TeamVisibilityChangeEvent.java
|
|
│ ├── TeamScoreChangeEvent.java
|
|
│ └── TeamSpawnPointChangeEvent.java
|
|
└── player/
|
|
├── PlayerProfile.java
|
|
├── PlayerRanking.java
|
|
├── PlayerProfileRepository.java
|
|
├── InMemoryPlayerProfileRepository.java
|
|
├── SqlitePlayerProfileRepository.java
|
|
├── PlayerProfileService.java
|
|
├── PlayerProfileServiceImpl.java
|
|
├── BukkitEventFiringPlayerProfileServiceImpl.java
|
|
├── PlayerException.java
|
|
├── PlayerProfileNotFoundException.java
|
|
└── event/ # Bukkit events player
|
|
├── PlayerProfileEvent.java
|
|
├── PlayerProfileCreateEvent.java
|
|
├── PlayerProfileDeleteEvent.java
|
|
└── PlayerScoreChangeEvent.java
|
|
```
|
|
|
|
## Tables SQLite créées par CR-Core
|
|
|
|
Au premier `enable()`, les tables suivantes sont créées (en `IF NOT EXISTS`) :
|
|
|
|
- `crcore_teams` — métadonnées par équipe (id, name, tag, color, leader_id,
|
|
visibility, spawn_world/x/y/z/yaw/pitch)
|
|
- `crcore_team_members` — un membre = (team_id, player_id, role, joined_at)
|
|
- `crcore_team_scores` — (team_id, score_name, value)
|
|
- `crcore_player_profiles` — un profil = (id)
|
|
- `crcore_player_scores` — (profile_id, score_name, value)
|
|
|
|
Le préfixe `crcore_` évite les collisions avec les tables custom du plugin
|
|
de jeu.
|