Files
Cites_Plugins/docs/diagrams/team-class-diagram.puml
T
Antone Barbaud 84735221f1 refactor: split util/ vs features/ + modular opt-in setupX() config
Package restructure to prepare future modularization. Three layers now:

1. fr.luc.crcore.util.* (always active)
   - common, command framework, database, message, broadcast, gui,
     placeholder

2. fr.luc.crcore.features.* (opt-in)
   - team (entities, services, repos, events, exceptions, config + GUI,
     command/Team*SubCommand)
   - player (profile, ranking, services, repos, events, exceptions)

3. fr.luc.crcore.builtin.*
   - CoreCommand + CoreReloadSubCommand (top-level routing — not a
     feature, not an util)

FQN moves (game plugins importing these need their imports updated):
- fr.luc.crcore.common.*          → fr.luc.crcore.util.common.*
- fr.luc.crcore.command.*         → fr.luc.crcore.util.command.*
- fr.luc.crcore.command.builtin.team.*
                                  → fr.luc.crcore.features.team.command.*
- fr.luc.crcore.command.builtin.CoreCommand / CoreReloadSubCommand
                                  → fr.luc.crcore.builtin.*
- fr.luc.crcore.database.*        → fr.luc.crcore.util.database.*
- fr.luc.crcore.message.*         → fr.luc.crcore.util.message.*
- fr.luc.crcore.broadcast.*       → fr.luc.crcore.util.broadcast.*
- fr.luc.crcore.gui.*             → fr.luc.crcore.util.gui.*
- fr.luc.crcore.placeholder.*     → fr.luc.crcore.util.placeholder.*
- fr.luc.crcore.team.*            → fr.luc.crcore.features.team.*
- fr.luc.crcore.player.*          → fr.luc.crcore.features.player.*

CRCoreConfig — features opt-in via fluent setup:
- setupTeams()        enables team feature
- setupPlayers()      enables player feature
- setupPlaceholders() enables PAPI integration
- setupAll()          shortcut
By default no feature is active. Game plugin must opt-in.

CRCore.enable() now conditional:
- Util services always built (messages, broadcasts, gui listener).
- Team services + repos + config built only if setupTeams().
- Player services + repos built only if setupPlayers().
- Placeholder hook registered only if setupPlaceholders() (and PAPI
  installed at runtime).
- Getters of disabled features throw IllegalStateException with a clear
  message pointing to the missing setupX() call.
- CoreCommand only registers TeamGroupSubCommand if teamService and
  teamConfig are non-null. CoreReloadSubCommand handles teamConfig=null
  (just skips that reload). /core reload always available.

Docs:
- setup.md tree fully rewritten to reflect util/+features/+builtin
  layout (~70 lines of arborescence).
- setup.md code snippet shows the three opt-in patterns (setupAll /
  granular / with options).
- decisions.md logs both decisions (restructure + modular setup).
- All .puml diagrams auto-updated to new FQNs.

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

252 lines
6.4 KiB
Plaintext

@startuml team-class-diagram
title CR-Core — Team domain (class diagram)
skinparam classAttributeIconSize 0
hide empty members
' === Common abstractions ===
package "fr.luc.crcore.util.common" {
interface Identifiable {
+ getId(): UUID
}
interface Named {
+ getName(): String
}
interface ScoreHolder {
+ getScore(name): int
+ hasScore(name): boolean
+ getScores(): Map<String, Integer>
+ getTotalScore(): int
+ addScore(name, delta): int
+ setScore(name, value): int
+ resetScore(name): boolean
+ resetAllScores(): void
}
abstract class AbstractEntity {
- id: UUID
+ AbstractEntity(id: UUID)
+ getId(): UUID
+ equals(o: Object): boolean
+ hashCode(): int
}
interface "Repository<T extends Identifiable>" as Repository {
+ save(entity: T): T
+ findById(id: UUID): Optional<T>
+ findAll(): Collection<T>
+ delete(id: UUID): boolean
}
AbstractEntity ..|> Identifiable
}
' === Team domain ===
package "fr.luc.crcore.features.team" {
enum TeamRole {
LEADER
MEMBER
--
+ isLeader(): boolean
}
enum TeamVisibility {
PUBLIC
PRIVATE
--
+ isPublic(): boolean
+ isPrivate(): boolean
}
enum TeamColor {
RED
BLUE
GREEN
YELLOW
AQUA
LIGHT_PURPLE
GOLD
WHITE
BLACK
DARK_BLUE
DARK_GREEN
DARK_AQUA
DARK_RED
DARK_PURPLE
DARK_GRAY
GRAY
--
+ getChatColor(): ChatColor
+ getDyeColor(): DyeColor
+ getDisplayName(): String
}
class TeamMember {
- role: TeamRole
- joinedAt: Instant
+ getPlayerId(): UUID
+ getRole(): TeamRole
+ getJoinedAt(): Instant
+ isLeader(): boolean
+ withRole(role: TeamRole): TeamMember
}
class Team {
- name: String
- tag: String
- color: TeamColor
- leaderId: UUID *(nullable)*
- visibility: TeamVisibility
- members: Set<TeamMember>
- scores: Map<String, Integer>
- spawnPoint: Location
--
+ Team(id, name, tag, color) ' leaderless, PRIVATE
+ Team(id, name, tag, color, visibility) ' leaderless
+ Team(id, name, tag, color, leaderId) ' with leader, PRIVATE
+ Team(id, name, tag, color, leaderId, visibility)
--
+ getName(): String
+ getTag(): String
+ getColor(): TeamColor
+ getLeaderId(): Optional<UUID>
+ getLeader(): Optional<TeamMember>
+ hasLeader(): boolean
+ isLeader(playerId): boolean
+ getVisibility(): TeamVisibility
+ setVisibility(v): void
+ isPublic(): boolean
+ getMembers(): Set<TeamMember>
+ getMember(playerId): Optional<TeamMember>
+ hasMember(playerId): boolean
+ size(): int
+ addMember(playerId): TeamMember
+ removeMember(playerId): boolean
+ transferLeadership(newLeaderId): void ' strict: chef→chef
+ setLeader(newLeaderId): void ' permissive: assign
--
+ getScore(name): int
+ hasScore(name): boolean
+ getScores(): Map<String, Integer>
+ getTotalScore(): int
+ addScore(name, delta): int
+ setScore(name, value): int
+ resetScore(name): boolean
+ resetAllScores(): void
--
+ getSpawnPoint(): Optional<Location>
+ hasSpawnPoint(): boolean
+ setSpawnPoint(loc): void
+ clearSpawnPoint(): void
--
# newMember(playerId, role): TeamMember
}
class TeamRanking <<record>> {
+ rank: int
+ team: Team
+ score: int
}
interface TeamRepository {
+ findByName(name: String): Optional<Team>
+ findByTag(tag: String): Optional<Team>
+ findByMember(playerId: UUID): Optional<Team>
}
class InMemoryTeamRepository {
- teams: Map<UUID, Team>
}
interface TeamService {
+ createTeam(name, tag, color): Team ' leaderless, PRIVATE
+ createTeam(name, tag, color, visibility): Team ' leaderless
+ createTeam(name, tag, color, leaderId): Team
+ createTeam(name, tag, color, leaderId, visibility): Team
+ dissolveTeam(teamId): boolean
+ addMember(teamId, playerId): boolean
+ removeMember(teamId, playerId): boolean
+ joinTeam(teamId, playerId): boolean
+ transferLeadership(teamId, newLeaderId): boolean ' strict: chef→chef
+ setLeader(teamId, newLeaderId): boolean ' permissive (admin)
+ setVisibility(teamId, visibility): void
--
+ addScore(teamId, name, delta): int
+ setScore(teamId, name, value): int
+ getScore(teamId, name): int
+ resetScore(teamId, name): boolean
+ resetAllScores(teamId): void
--
+ getRankingByScore(name): List<TeamRanking>
+ getGlobalRanking(): List<TeamRanking>
+ getTopRankingByScore(name, limit): List<TeamRanking>
+ getTopGlobalRanking(limit): List<TeamRanking>
--
+ setSpawnPoint(teamId, loc): void
+ clearSpawnPoint(teamId): void
+ getSpawnPoint(teamId): Optional<Location>
--
+ getTeam / getTeamByName / getTeamByTag / getTeamOfPlayer
+ getAllTeams(): Collection<Team>
}
class TeamServiceImpl {
- repository: TeamRepository
--
# newTeam(...): Team
# newRanking(rank, team, score): TeamRanking
# rank(scoreFn): List<TeamRanking>
--
# validateName(name): void
# validateTag(tag): void
# validateLeader(leaderId): void
# validateJoinable(team, playerId): void
--
# onBeforeSave(team): void
# onAfterCreate(team): void
# onBeforeDissolve(team): void
# onAfterDissolve(team): void
# onMemberAdded(team, member): void
# onMemberRemoved(team, playerId): void
# onPlayerJoined(team, member): void
# onLeadershipTransferred(team, oldId, newId): void
# onVisibilityChanged(team, oldV, newV): void
# onScoreChanged(team, name, oldV, newV): void
# onSpawnPointChanged(team, oldLoc, newLoc): void
}
class TeamException
class TeamAlreadyExistsException
class TeamNotFoundException
class TeamAccessException
TeamMember --|> AbstractEntity
Team --|> AbstractEntity
Team ..|> Named
Team ..|> ScoreHolder
TeamRepository --|> Repository
InMemoryTeamRepository ..|> TeamRepository
TeamServiceImpl ..|> TeamService
Team "1" o-- "1..*" TeamMember : members
Team --> TeamColor : color
Team --> TeamVisibility : visibility
TeamMember --> TeamRole : role
TeamRanking --> Team
TeamServiceImpl o--> TeamRepository
TeamService ..> TeamRanking : produces
TeamException --|> RuntimeException
TeamAlreadyExistsException --|> TeamException
TeamNotFoundException --|> TeamException
TeamAccessException --|> TeamException
}
@enduml