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>
New fr.luc.crcore.broadcast module:
- BroadcastAudience enum (NONE, LEADER, TEAM, ADMIN, ALL).
- BroadcastContext (fluent: team + involvedPlayerId + placeholders).
- BroadcastService interface + YamlBroadcastService impl.
- CRCoreBroadcastListener (Bukkit listener) wires the 12 native events
(9 team + 3 player) to broadcasts.broadcast(eventKey, ctx).
Same single-per-plugin file pattern as messages:
<plugin-dataFolder>/<plugin-name-lowercase>-broadcasts.yml. Defaults
bundled at resources/crcore-broadcasts.yml, copied on first boot (game
plugin's own resource of the same name takes priority as the template).
In-memory fallback so new CR-Core keys work without admin edit.
Routes (who) vs templates (what) are separated: broadcasts.yml lists
audiences per eventKey, messages.yml contains the templates under keys
<eventKey>.broadcast. Admin can change either independently.
12 new *.broadcast keys added to crcore-messages.yml with sensible
French defaults and color codes. Listener injects standard placeholders
(name, team_name, tag, color, visibility, player, new_leader,
old/new_value, etc.).
ADMIN audience resolved via crcore.broadcast.admin permission. Multi-
audiences via YAML list (e.g., [TEAM, ADMIN]); union of resolved
players, no duplicate.
New /core reload subcommand (permission crcore.reload) hot-reloads
both messages and broadcasts from disk without restart.
CRCore: protected buildBroadcastService() override point, getter
broadcasts(), wire of CRCoreBroadcastListener at enable(). CoreCommand
constructor extended to take BroadcastService, registers the reload
subcommand.
Docs/features.md: new section 9 "Service de broadcasts". docs/setup.md:
updated to mention both YAML files. decisions.md logs the routes-vs-
templates split, the audience model, and the reload semantics.
New diagram broadcasts-class-diagram.puml. README updated.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Each domain now has:
- Top-level package: contracts (interfaces, entities, enums, values, events).
- exception/ subpackage: exception hierarchy.
- impl/ subpackage: implementations CR-Core ships by default, swappable by
a game plugin if needed.
Moved (FQN changes for consumers):
- fr.luc.crcore.team.TeamException (+ AlreadyExists, NotFound, Access)
→ fr.luc.crcore.team.exception.*
- fr.luc.crcore.team.TeamServiceImpl, BukkitEventFiringTeamServiceImpl,
InMemoryTeamRepository, SqliteTeamRepository
→ fr.luc.crcore.team.impl.*
- fr.luc.crcore.player.PlayerException, PlayerProfileNotFoundException
→ fr.luc.crcore.player.exception.*
- fr.luc.crcore.player.PlayerProfileServiceImpl,
BukkitEventFiringPlayerProfileServiceImpl,
InMemoryPlayerProfileRepository, SqlitePlayerProfileRepository
→ fr.luc.crcore.player.impl.*
- fr.luc.crcore.message.YamlMessagesService
→ fr.luc.crcore.message.impl.YamlMessagesService
Unchanged top-level packages: database/, command/, common/ (already small
and well-organized). Events stay where they were (already in event/
subpackages).
setup.md tree updated to reflect the new layout. decisions.md logs the
rationale (consumer-facing FQNs, separation between contract and impl).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
features.md: new section 8 "Service de messages" with the single-file model,
two-layer in-memory (jar defaults + user file), API examples,
template-override mechanism, and override-the-impl extension point.
Bootstrap section renumbered to 9.
decisions.md: new decision logging the choice of MessagesService design
(YAML, single per-plugin file, in-jar fallback for new keys, template
priority from the game plugin's own resource, programmatic set() for
dynamic cases).
README.md: feature list mentions externalized messages; diagrams index
adds messages-class-diagram.puml.
setup.md: new "Fichier messages" section explaining the file location,
how to seed it from a game plugin's bundled template, and the API for
hot reload / extras / programmatic set().
New diagram: docs/diagrams/messages-class-diagram.puml — MessagesService
interface, YamlMessagesService impl, link to CRCore, with explanatory
note on the two-source merge and template fallback at first boot.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New module fr.luc.crcore.message exposing:
- MessagesService (interface) with get(key, placeholderPairs...), raw, has,
set, reload, loadAdditional, color-code toggle.
- YamlMessagesService (impl) that auto-orchestrates everything at construction:
1. Loads CR-Core defaults from the bundled crcore-messages.yml resource
(always in memory as fallback for missing keys).
2. Creates <dataFolder>/<plugin-name-lowercase>-messages.yml at first run
by copying the plugin's own resource of the same name if it ships one,
else CR-Core's defaults.
3. Loads the user file as an override layer on top of the defaults.
Single per-plugin file: the admin only edits one YAML, named after the host
plugin (e.g. mygame-messages.yml). Missing keys transparently fall back to
the bundled CR-Core defaults, so future CR-Core releases adding new keys
work without forcing the admin to edit anything. Plugins can ship their
own version of the file with extra keys + overrides to seed the template.
Placeholder substitution: {name} style via varargs key/value pairs. Color
codes (&a, &c, ...) translated automatically via
ChatColor.translateAlternateColorCodes.
Migration: all 14 default /core team subcommands (create, delete, add,
remove, join, leave, info, list, transfer, setleader, visibility, score,
top, setspawn) now use messages.get(...) instead of hardcoded French
strings. CoreCommand overrides handleResult to render
SUCCESS/FAILURE/INVALID_USAGE/NO_PERMISSION/PLAYER_ONLY through the
service.
CRCore exposes the service via core.messages() / getMessages(); built via
buildMessagesService() (overridable for custom impl).
Bundled defaults: src/main/resources/crcore-messages.yml with the full set
of French defaults, documented inline (placeholders listed per message).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
/core team create now takes [visibility] [leader] (both optional,
positional in this order). Variants:
- /core team create N T C → PRIVATE, leaderless
- /core team create N T C PUBLIC → PUBLIC, leaderless
- /core team create N T C PRIVATE Alice → PRIVATE, Alice as leader
- /core team create N T C PUBLIC Alice → PUBLIC, Alice as leader
Auto-migration for pre-existing SQLite databases:
SqliteTeamRepository.ensureSchema() now checks pragma_table_info for the
leader_id column. If it still has the NOT NULL constraint from an older
schema, it recreates the table (in a transaction) with leader_id nullable
and copies the rows over. Required because CREATE TABLE IF NOT EXISTS
skips the alteration on existing tables, so production databases were
crashing with SQLITE_CONSTRAINT_NOTNULL when an admin tried to create a
leaderless team.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The "you're chef, transfer first" message still pointed to the old
/core team transfer <player> signature (which used to target the chef's
own team). Since transfer is now admin-only (/core team transfer <team>
<player>), a player cannot execute it themselves. Redirects them to
ask an admin for /core team setleader or /core team delete instead.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Commands:
- Drop all short aliases (c/i/t/j/vis/disband/...) — long names only.
- Every /core team <action> now has a crcore.team.<action> permission.
- Three-tier model:
* Admin (perm only): create, delete, setleader, score
* Chef (perm + chef-check in execute): add, remove, transfer, visibility, setspawn
* Player (perm): join, leave, info, list, top
- delete now takes <team> as argument (admin); no more chef-disband shortcut.
New /core team setleader <team> <player>:
- Admin assigns or replaces a team's leader.
- More permissive than transfer: target may not yet be a member (auto-add),
and works on leaderless teams.
Leaderless teams:
- Team.leaderId is now nullable.
- getLeaderId() and getLeader() return Optional<...>.
- hasLeader() and isLeader(UUID) helpers added.
- New constructors Team(id, name, tag, color [, visibility]) for leaderless.
- TeamService.createTeam overloads without leaderId.
- TeamService.setLeader(teamId, playerId): assigns/replaces leader (auto-adds
as member if needed). Fires TeamLeadershipTransferEvent with optional old.
- TeamLeadershipTransferEvent.getOldLeaderId() returns Optional<UUID>.
- SqliteTeamRepository: leader_id column no longer NOT NULL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Uses <release>11</release> in maven-compiler-plugin (recommended over
source/target to guarantee bytecode and API surface match Java 11).
Code changes to drop Java 12-16 features:
- records (TeamRanking, PlayerRanking, internal tuples in
SqliteTeamRepository) become hand-written immutable classes; same
accessor names (rank()/team()/score()/...) so call sites are unchanged.
- instanceof X x pattern matching becomes classic instanceof + cast in
CommandContext.requirePlayer and Database.normalize.
- switch expressions with -> arrows become classic switch + break, or
if/else chains, in BaseCommand.handleResult, ArgumentTypes.BOOLEAN and
TeamScoreSubCommand.execute.
docs/setup.md, features.md and decisions.md updated to reflect Java 11.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CRCore.registerCommand() now falls back to dynamic registration via the
server's internal CommandMap (reflection on CraftServer.commandMap) when
the command is absent from the host plugin's plugin.yml. Game plugins can
now use CR-Core with zero plugin.yml changes — just instantiate CRCore.
If the command IS declared in plugin.yml, CR-Core detects it and uses
setExecutor/setTabCompleter as before.
pom.xml: distributionManagement targeting Gitea Packages
(https://gitea.luc-rival.fr/api/packages/admin/maven), plus
maven-source-plugin (3.3.0) and maven-javadoc-plugin (3.6.3) so each
mvn deploy publishes the main jar, a -sources.jar and a -javadoc.jar.
doclint=none on javadoc to tolerate partial doc.
docs/setup.md: clarifies that the commands: entry in plugin.yml is now
optional. docs/decisions.md: new decision logged for the dynamic
registration approach.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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>
Pure Maven library for CR Minecraft game plugins, targeting Paper 1.16.5.
Common abstractions (fr.luc.crcore.common): Identifiable, Named, ScoreHolder,
AbstractEntity, Repository<T>.
Team domain (fr.luc.crcore.team): Team entity with name/tag/color/leader/
visibility (PUBLIC|PRIVATE)/members/scores/spawn point, TeamMember,
TeamRole/TeamColor/TeamVisibility enums, TeamRanking record, TeamService with
overridable hooks (factories, validations, lifecycle events), in-memory
repository, dedicated exception hierarchy.
Player domain (fr.luc.crcore.player): PlayerProfile with named scores per
player, PlayerProfileService with auto-creation, individual rankings,
exception hierarchy. Both Team and PlayerProfile implement ScoreHolder.
Command framework (fr.luc.crcore.command): Command interface,
AbstractCommand base, BaseCommand (CommandExecutor + TabCompleter), SubCommand,
CommandContext, CommandResult, ArgumentType<T> + ArgumentTypes catalogue
(STRING, INTEGER, DOUBLE, BOOLEAN, ONLINE_PLAYER, enumOf, choice).
Docs (docs/) is the single source of truth: README, setup, features,
decisions log, and 6 PlantUML diagrams (team class/sequence/activity/join,
player class, command class).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>