c1b414f400
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>
9.2 KiB
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
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
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.