# 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 fr.luc CR-Core 1.0-SNAPSHOT compile ``` 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 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.