Files
Cites_Plugins/docs/setup.md
T
Antone Barbaud c1b414f400 feat: SQLite persistence, default /core commands, Bukkit events, bootstrap
CRCore bootstrap class: one-line setup for game plugins (new CRCore(this).enable()).
Wires SQLite, services with event firing, and the /core command tree.

SQLite layer (fr.luc.crcore.database): Database wrapper exposing execute/update/
queryOne/query plus a fluent TableBuilder. ColumnType enum, RowMapper interface,
DatabaseException. Game plugins create their own tables in 2 lines via
db.table("foo").ifNotExists().column(...).create().

Repositories: SqliteTeamRepository and SqlitePlayerProfileRepository extend their
InMemory counterparts (write-through cache). 5 internal tables prefixed crcore_.

Command framework refactored for nested sub-commands: subcommand storage moved
from BaseCommand to AbstractCommand, recursive dispatch() and tabComplete(),
replaceSubCommand() for plugin overrides.

Default /core team commands (13 leaf sub-commands): create, delete, add, remove,
join, leave, info, list, transfer, visibility, score, top, setspawn. Each in its
own class under fr.luc.crcore.command.builtin.team, fully substitutable.

Bukkit events: 9 team events (Create/Dissolve/MemberAdd/MemberRemove/PlayerJoin/
LeadershipTransfer/VisibilityChange/ScoreChange/SpawnPointChange) + 3 player
events (ProfileCreate/Delete/ScoreChange). All post-only, non-cancellable.
BukkitEventFiringTeamServiceImpl and BukkitEventFiringPlayerProfileServiceImpl
override the on* hooks to call Bukkit.getPluginManager().callEvent.

JavaDoc on all new public classes and key existing ones. docs/, GEMINI.md and
PUML diagrams synced: new sections (built-in commands, events, database,
bootstrap), 4 new diagrams (builtin-commands, events, database, bootstrap-
sequence), and 7 new architecture decisions logged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 10:54:00 +02:00

9.2 KiB

Setup technique

Stack

  • Type : librairie Java (jar) — pas un plugin Bukkit
  • artifactId Maven : CR-Core
  • Build : Maven, Java 16
  • 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

Build & install local

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

<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

name: MyGame
main: fr.exemple.mygame.MyGamePlugin
version: 1.0
api-version: 1.16
commands:
  core:
    description: Commandes CR-Core
    # aliases optionnels — équivalents au point de vue Bukkit
    aliases: [cr, crcore]

Code minimal

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

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

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

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.