ApocalyInterfaceLib — Spécification Technique (v2)
Document de référence du projet. Doit être chargé en contexte à chaque session Claude Code. Cette v2 intègre la revue complète + le design system réel extrait du mod Clan existant.
1. Vision et scope
1.1 Mission
ApocalyInterfaceLib est une librairie d'interface pour Minecraft Forge 1.20.1 qui permet à tous les mods du serveur Apocaly de créer des écrans visuellement uniformes et entièrement personnalisables, sans avoir à coder le rendering Minecraft.
1.2 Mods consommateurs
- Court terme : Clan, Shop/Marchand, Banque, Jobs
- Long terme : tout futur mod Apocaly avec une UI
- Diffusion : interne au serveur Apocaly uniquement (pas de distribution publique)
1.3 Cas d'usage prioritaires
- Hub vertical à boutons (type Clan Hub) — famille données
- Écran marchand à slots (type Shop) — famille container
- Écran de stats/profil (cards + valeurs accentuées)
- Liste scrollable (membres, transactions, items)
- Écran avec barres de progression (jobs : XP/niveau)
Ces 5 cas sont les premiers livrables, mais l'architecture vise un catalogue extensible où l'on peut composer une infinité d'écrans, et où un dev peut créer son propre widget sans modifier la lib.
1.4 Ce que la lib FAIT
- Rendering uniforme via design system imposé
- Système de layout (rows, columns, cards, padding automatique)
- Catalogue de widgets réutilisables
- Résolution i18n automatique selon la langue du client
- Gestion des clics + exécution de callbacks définis par le mod consommateur
- Deux familles de Screen de base :
ApocalyScreen(données) etApocalyContainerScreen(container)
1.5 Ce que la lib NE FAIT PAS
- Réseau / packets (chaque mod gère sa propre synchro serveur->client)
- Logique métier
- Stockage / persistance de données
- Modifier les Screens vanilla existants
- Permissions / authentification
- Animations / transitions (reporté post-v1)
- Sons d'UI custom (on utilise les sons vanilla MC)
1.6 Modèle de programmation
Java pur + Builder pattern. Le dev étend ApocalyScreen et implémente une seule méthode buildLayout(Column root). La lib gère automatiquement : centrage, dessin du panneau et des bordures, scroll, hover, i18n, troncature de texte. Le dev ne calcule jamais de coordonnées de panneau.
Extensibilité future : le
core/reste modulaire pour permettre l'ajout d'une couche JSON déclarative par-dessus, sans refactor.
1.7 Distribution
Maven local pour démarrer (./gradlew publishToMavenLocal), puis GitHub Packages privé quand d'autres devs rejoignent. Les mods consommateurs ajoutent la dépendance dans build.gradle + déclarent dans mods.toml avec side="CLIENT".
1.8 Cycle de vie d'un Screen (joueur)
- Ouverture : trigger serveur -> le mod rassemble les données -> packet S2C -> client :
setScreen(new MonScreen(data)) - Refresh : nouveau packet ->
screen.update(newData)-> relayout automatique - Fermeture : Échap ou bouton Fermer ->
onClose().isPauseScreen() -> falsepar défaut
1.9 Cycle de vie (dev consommateur)
- Ajouter la dépendance lib
- Créer
class MonScreen extends ApocalyScreen - Implémenter
buildLayout(root)avec des builders - Brancher les callbacks (souvent : envoyer une commande/packet)
- Côté serveur : envoyer le packet d'ouverture avec les données
1.10 Compatibilité vanilla
La lib ne touche pas aux écrans vanilla. ApocalyContainerScreen permet d'intégrer de vrais slots vanilla (drag & drop validé serveur) dans un écran au style Apocaly. Mélange autorisé : slots vanilla + widgets Apocaly.
1.11 Métriques de succès
- Un écran type hub se code en < 50 lignes (vs ~120 aujourd'hui)
- Zéro calcul de layout manuel côté dev
- Zéro code de scroll réécrit
- Rendu pixel-identique au style Clan actuel
- Un dev peut créer son propre widget sans modifier la lib
- Les 4 mods cibles l'utilisent sans dupliquer de code UI
2. Identité et conventions
| Champ | Valeur |
|---|---|
| Nom d'affichage | ApocalyInterfaceLib |
| Mod ID | apocalyinterfacelib |
| Package racine | ca.tawess123.apocalyinterface |
| Auteur | eloileger |
| Licence | All Rights Reserved (interne Apocaly) |
| Versioning | SemVer : MAJOR.MINOR.PATCH |
| Repo Git | GitHub privé (à créer) |
Conventions de nommage
- Builders publics :
XxxBuilder - Implémentations cachées :
XxxImpl - Code (classes, méthodes, variables) : anglais (convention Java/Minecraft)
- Javadoc et documentation : français
- Une interface publique = une implémentation
Implcorrespondante danscore/
Conventions Java
- Java 17 : records, sealed classes, switch expressions autorisés
- Pas de Lombok (complique le build Forge)
- Javadoc obligatoire sur tout
api/public
3. Design system
Toutes les valeurs ci-dessous sont extraites du code Clan réel (
ClanGuiStyle.java), pas estimées. Elles sont la source de vérité pour reproduire le style à l'identique.
3.1 Principe fondamental : translucidité
Le style Apocaly repose sur des panneaux noirs translucides par-dessus l'assombrissement vanilla (renderBackground()), pas sur des couleurs de fond opaques. La teinte perçue (bleutée sur les screenshots) vient du monde visible derrière la transparence. Toujours activer le blend (RenderSystem.enableBlend()) avant de dessiner.
3.2 Palette (format ARGB, alpha en premier)
// Panneaux
PANEL_BG = 0x80050505; // fond panneau principal (noir, 50% alpha)
HEADER_BG = 0x66020202; // fond barre de titre (noir, 40% alpha)
SUB_BG = 0x80030303; // fond sous-panneau / card (noir, 50% alpha)
// Bordures & séparateurs
BORDER = 0xFFFFAA00; // bordure jaune signature (opaque)
SUB_BORDER = 0x33FFFFFF; // bordure sous-panneau (blanc, 20% alpha)
DIVIDER = 0x55FFFFFF; // ligne de séparation (blanc, 33% alpha)
// Texte
TITLE = 0xFFFFAA00; // titre (jaune, = BORDER)
TEXT = 0xFFE8E8E8; // texte standard (blanc cassé)
MUTED_TEXT = 0xFFA8A8A8; // texte secondaire (gris)
ACCENT = 0xFF9AD9FF; // valeurs mises en avant (cyan clair)
3.3 Couleurs sémantiques (extraites des écrans Clan)
SUCCESS = 0xFF55FF55; // vert (peut acheter, action OK)
SUCCESS_SOFT = 0xFF7CFF7C; // vert clair (membre en ligne)
DANGER = 0xFFFF5555; // rouge (refus, pas assez)
DANGER_SOFT = 0xFFFF8E8E; // rouge clair (membre hors ligne)
HOVER_TEXT = 0xFFFFFFCC; // jaune pâle (texte bouton survolé)
HOVER_FILL = 0x33FFFFFF; // surbrillance de ligne au survol
// Podium (leaderboard)
GOLD = 0xFFFFD700;
SILVER = 0xFFC0C0C0;
BRONZE = 0xFFCD7F32;
// Difficulté (gradient facile -> difficile)
DIFF_VERY_EASY = 0xFFB8FF8C;
DIFF_EASY = 0xFFD6FF72;
DIFF_NORMAL = 0xFFFFE26A;
DIFF_HARD = 0xFFFFB14F;
DIFF_VERY_HARD = 0xFFFF6363;
3.4 Dimensions
BORDER_THICKNESS = 2; // bordure panneau principal
SUB_BORDER_THICKNESS = 1; // bordure sous-panneau
HEADER_HEIGHT = 22; // barre de titre (top+2 -> top+24)
DIVIDER_OFFSET = 26; // y du divider sous le titre
TITLE_Y_OFFSET = 8; // y du texte de titre
BUTTON_HEIGHT = 20; // hauteur standard de bouton
BUTTON_SPACING = 24; // espacement vertical entre boutons
PANEL_MARGIN = 10; // marge interne du panneau
SCREEN_EDGE_MARGIN = 8; // marge minimale au bord de l'écran
3.5 Structure du panneau principal (ordre de dessin)
- Fond
PANEL_BGsur tout le rectangle - 4 bordures
BORDERde 2px (haut, bas, gauche, droite) - Barre de titre
HEADER_BGdetop+2àtop+24 - Divider
DIVIDER1px àtop+26 - Titre centré
TITLEàtop+8
3.6 Les 4 états de bouton
| État | Fond | Bordure | Texte |
|---|---|---|---|
| Actif, repos | 0x80050505 | 0xFF555555 | 0xFFFFFFFF |
| Actif, survol | 0x99101010 | 0xFFFFAA00 | 0xFFFFFFCC |
| Inactif, repos | 0x70030303 | 0xFF333333 | 0xFF666666 |
| Inactif, survol | 0x88101010 | 0xFFAA3333 | 0xFFFF6666 |
Un bouton peut aussi recevoir une couleur de texte fixe (utilisée par exemple pour afficher un coût en vert/rouge selon solvabilité).
3.7 Typographie
- Police :
Minecraft.getInstance().font(vanilla, jamais changée globalement) - Titres et libellés de boutons : centrés, avec ombre
- Texte de contenu : aligné à gauche
- Troncature automatique via
font.plainSubstrByWidth(...)+ suffixe...
3.8 Thématisation
Un seul thème (« Apocaly ») pour la v1, mais toutes les couleurs/dimensions passent par un ThemeManager centralisé pour permettre l'ajout de thèmes alternatifs plus tard sans toucher aux widgets.
4. API publique et catalogue de widgets
4.1 Classes de base
// Famille « données » (panneau unique — Clan Hub, stats, listes)
public abstract class ApocalyScreen extends Screen {
protected abstract void buildLayout(Column root);
// La lib gère init(), render(), mouseScrolled(), centrage, etc.
}
// Famille « données » (plusieurs panneaux côte à côte — Marchand, comparateurs)
public abstract class ApocalyMultiScreen extends Screen {
protected abstract List<PanelConfig> buildPanels();
// Chaque PanelConfig déclare : titre, largeur (px), Consumer<Column>
// La lib gère le centrage du groupe, la hauteur partagée et le scroll par panneau.
}
// Famille « container » (slots vanilla — Marchand avec vrais slots, Banque)
public abstract class ApocalyContainerScreen<T extends AbstractContainerMenu>
extends AbstractContainerScreen<T> {
protected abstract void buildLayout(Row root);
}
4.2 Système de layout
| Conteneur | Rôle |
|---|---|
Column | Empile ses enfants verticalement (cas Clan Hub) |
Row | Aligne ses enfants horizontalement (cas Marchand : 3 colonnes) |
Card | Sous-panneau encadré contenant d'autres widgets |
Grid | Grille N colonnes (slots, items) |
Spacer | Espace vide (gap manuel) |
Divider | Ligne de séparation horizontale |
Le layout calcule automatiquement les positions. Padding et gaps gérés par le thème.
4.3 Catalogue de widgets (v1)
Dérivé directement des patterns répétés dans le mod Clan :
| Widget | Origine (pattern Clan) | Description |
|---|---|---|
Label | drawString partout | Texte avec couleur, accent, troncature auto |
KeyValueRow | « Membres: 0 / 0 » partout | Ligne « clé: valeur » alignée |
Button | styledButton | Bouton 4 états + callback |
Card | drawSubPanel | Sous-panneau encadré |
ScrollableList<T> | scroll réimplémenté 5x | Liste défilante générique avec renderer |
ClickableList<T> | Leaderboard | Liste avec lignes cliquables + hover |
ProgressBar | (jobs, à venir) | Barre de progression |
SearchField | (shop) | Champ de recherche avec callback onChange |
ItemIcon | ZoneAnalysis | Item MC + count + tooltip |
SlotGrid | (shop) | Grille de slots vanilla (container) |
TabBar | Leaderboard | Onglets de sélection |
4.4 Builder pattern (exemple)
Button.builder()
.text("apocalyclan.button.my_clan") // clé i18n, jamais texte brut
.onClick(this::openMyClan) // OU .command("clan info")
.fullWidth()
.build();
Label.builder()
.text("apocalyclan.label.points")
.value(DataSource.of(clanPoints)) // valeur statique OU dynamique
.accent() // couleur cyan
.build();
4.5 Raccourci « commande » pour les boutons
Le mod Clan déclenche ses actions via player.connection.sendUnsignedCommand("clan info"). La lib fournit un raccourci :
Button.builder().text("...").command("clan info").build();
// équivaut à .onClick(() -> sendCommand("clan info"))
4.6 Widget custom (extensibilité)
Un dev crée son propre widget en implémentant l'interface Widget :
public interface Widget {
void render(GuiGraphics gfx, int mouseX, int mouseY, float partialTick);
int getPreferredHeight(int availableWidth);
default boolean mouseClicked(double mx, double my, int btn) { return false; }
default boolean mouseScrolled(double mx, double my, double delta) { return false; }
}
Il l'ajoute ensuite dans n'importe quel Column/Row/Card comme un widget natif.
4.7 i18n
- Système : Minecraft natif (
Component.translatable) - Fichiers lib :
assets/apocalyinterface/lang/{en_us,fr_ca,fr_fr}.json(clés internes uniquement) - Chaque mod consommateur fournit ses propres lang files
LocaleResolver: wrap avec fallback gracieux (clé manquante -> affiche la clé au lieu de crash)- Détection auto via
Minecraft.getInstance().getLanguageManager()
5. Architecture interne
5.1 Structure des packages
ca.tawess123.apocalyinterface/
│
├── ApocalyInterfaceLib.java # @Mod, setup client-only
├── Constants.java # MOD_ID, etc.
│
├── api/ # PUBLIC — stable, importé par les autres mods
│ ├── screen/
│ │ ├── ApocalyScreen.java
│ │ └── ApocalyContainerScreen.java
│ ├── layout/
│ │ ├── Column.java
│ │ ├── Row.java
│ │ ├── Card.java
│ │ ├── Grid.java
│ │ ├── Spacer.java
│ │ └── Divider.java
│ ├── widget/
│ │ ├── Widget.java # interface racine
│ │ ├── Label.java
│ │ ├── KeyValueRow.java
│ │ ├── Button.java
│ │ ├── ScrollableList.java
│ │ ├── ClickableList.java
│ │ ├── ProgressBar.java
│ │ ├── SearchField.java
│ │ ├── ItemIcon.java
│ │ ├── SlotGrid.java
│ │ └── TabBar.java
│ ├── data/
│ │ └── DataSource.java # hook statique/dynamique
│ └── theme/
│ ├── ApocalyColors.java
│ ├── ApocalyDimensions.java
│ └── ApocalyTheme.java
│
├── core/ # INTERNE — peut changer sans préavis
│ ├── render/
│ │ ├── PanelRenderer.java # drawPanel équivalent
│ │ ├── RenderUtils.java
│ │ └── TextUtils.java # troncature, ellipsize
│ ├── layout/
│ │ └── LayoutEngine.java # calcul des positions
│ ├── widget/impl/ # implémentations des widgets
│ ├── scroll/
│ │ └── ScrollController.java # logique de scroll centralisée
│ ├── i18n/
│ │ └── LocaleResolver.java
│ └── theme/
│ └── ThemeManager.java
│
└── (PAS de package network/ — la lib est client-side only)
5.2 Pipeline de rendu
ApocalyScreen.render() (final, géré par la lib) :
renderBackground()(assombrissement vanilla)- Calcul du rectangle de panneau (centrage + clamp écran)
PanelRenderer.drawPanel()(fond, bordures, titre)- Délégation : chaque widget du layout dessine sa portion
ScrollControllergère le défilement si le contenu dépasse
Le dev n'override jamais render() — il remplit juste buildLayout().
5.3 Layout engine
LayoutEngine parcourt l'arbre de widgets (Column -> enfants -> ...), demande à chacun sa hauteur préférée (getPreferredHeight), puis assigne les positions absolues. Les widgets reçoivent leurs coordonnées finales au moment du rendu.
5.4 Scroll centralisé
Toute la logique de scroll (scrollOffset, visibleRows, mouseScrolled, indicateur) vit dans ScrollController, utilisé par ScrollableList et ApocalyScreen. Plus jamais réécrite par les devs.
5.5 Source set
src/main/java # code de la lib
src/main/resources # mods.toml, lang files
src/test/java # (structure prévue, tests non prioritaires v1)
6. Statique vs dynamique
6.1 Le pattern existe déjà
Le mod Clan reçoit ses données par constructeur (packet S2C) et les rafraîchit via applyUpdate(). La lib formalise ça proprement via DataSource.
6.2 DataSource
public interface DataSource<T> {
T get();
static <T> DataSource<T> of(T value) { return () -> value; } // statique
}
- Statique (v1) :
DataSource.of(clanPoints)— valeur figée à la construction - Dynamique (préparé, activé v2) : un
DataSourcequi notifie un changement et déclenche un relayout automatique
6.3 Refresh côté mod consommateur
Quand un nouveau packet arrive, le mod appelle :
if (Minecraft.getInstance().screen instanceof MonScreen s) {
s.update(nouvellesDonnees); // la lib relayoute automatiquement
}
6.4 Scope du dynamique en v1
Les widgets acceptent déjà des DataSource (API prête), mais le push temps réel auto-rafraîchissant est reporté en v2. En v1, on rafraîchit explicitement via update(). Aucun refactor nécessaire pour passer au dynamique plus tard.
7. Distribution, documentation, qualité
7.1 Distribution
- Phase 7 initiale :
./gradlew publishToMavenLocal - Ensuite : GitHub Packages privé
- La lib produit un seul JAR (api + core ensemble pour la v1)
7.2 mods.toml de la lib (client-side only)
displayTest="IGNORE_ALL_VERSION" # serveur n'exige pas la lib, et vice-versa
[[dependencies.apocalyinterfacelib]]
modId="forge"
mandatory=true
versionRange="[47,)"
ordering="NONE"
side="CLIENT"
[[dependencies.apocalyinterfacelib]]
modId="minecraft"
mandatory=true
versionRange="[1.20.1]"
ordering="NONE"
side="CLIENT"
Code de la lib enregistré via FMLClientSetupEvent uniquement.
7.3 mods.toml d'un mod consommateur
[[dependencies.monmod]]
modId="apocalyinterfacelib"
mandatory=true
versionRange="[1.0,)"
ordering="AFTER"
side="CLIENT"
7.4 Documentation
Vit dans le repo en Markdown, maintenue par Claude Code dans le même commit que le code (règle obligatoire — voir section 9).
docs/
├── SPEC.md # ce document
├── README.md # vue d'ensemble + démarrage rapide
├── guide/
│ ├── 01-installation.md
│ ├── 02-premier-ecran.md
│ ├── 03-widgets.md
│ ├── 04-layout.md
│ └── 05-i18n.md
├── reference/
│ └── widgets/ # un .md par widget
└── CHANGELOG.md # journal par phase
Portage sur BookStack (wiki.tawess123.ca) en Phase 7.
7.5 Qualité
- Pas de tests unitaires prioritaires en v1 (l'UI se teste mal automatiquement)
- À la place : un mod de démo (
ApocalyInterfaceLibDemo) dans le même repo, avec un écran de test par widget, ouvrable via keybind. Test visuel in-game après chaque widget. - CI/CD (GitHub Actions build auto) : optionnel, v2
8. Roadmap des phases
| Phase | Deliverable | Critère de « done » |
|---|---|---|
| 0 | Ce SPEC v2 | Validé par eloi |
| 1 | Setup projet Forge + mod démo séparé | Lib + mod démo chargent en jeu, keybind du mod démo ouvre un écran vide stylé |
| 2 | Theme + i18n | ApocalyColors/Dimensions accessibles, fr_ca/en_us chargés, PanelRenderer.drawPanel reproduit le style Clan |
| 3 | Layout + widgets de base | Column/Row/Card + Label/KeyValueRow/Button. Démo : reproduire le haut du Clan Hub |
| 4 | Widgets avancés | ScrollableList, ClickableList, ProgressBar, SearchField, ItemIcon, TabBar |
| 5 | Container + slots | ApocalyContainerScreen + SlotGrid. Démo : grille de slots stylée |
| 6 | DataSource + refresh | update() relayoute, statique fonctionne de bout en bout |
| 7 | Repro UIs + build + doc | Clan Hub & Marchand reproduits via API publique, JAR publié, doc en ligne |
9. Instructions pour Claude Code
À chaque session, lire ce document EN PREMIER.
Règles non-négociables
- Ne jamais dévier de l'architecture des packages (section 5.1)
- Ne jamais hardcoder de couleurs/dimensions — utiliser
ApocalyColors/ApocalyDimensions - Ne jamais utiliser de texte brut dans les widgets — toujours une clé i18n
- Toujours utiliser
GuiGraphics(jamaisPoseStackdirect) - Toujours activer le blend pour la translucidité (
RenderSystem.enableBlend()) - Toujours fournir un Builder pour les widgets publics
- Toujours ajouter une Javadoc française sur les API publiques
- Un widget = une session = un commit
- Mettre à jour la doc
docs/dans le même commit que le code (toute classe publique ajoutée/modifiée -> doc correspondante à jour)
Pièges Forge 1.20.1
- Rendering via
GuiGraphicsuniquement (les tutos 1.18/1.19 utilisentPoseStack— obsolète) Screen: overriderender(GuiGraphics, int, int, float)AbstractContainerScreen: overriderenderBg(...)ETrenderLabels(...)DeferredRegisterpour tout enregistrement- Setup client via
FMLClientSetupEvent - Java 17 strict (pas de features Java 21)
En cas d'ambiguïté
Arrêter et demander à eloi. Ne pas inventer. Proposer 2-3 options avec trade-offs.
Format de travail par tâche
- Lire le SPEC
- Annoncer le plan et les fichiers touchés
- Écrire le code + la doc
- Récapituler ce qui est fait et ce qui reste
v2 — révision 2026-05-18 — intègre la revue complète + design system réel du mod Clan