Aller au contenu principal

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

  1. Hub vertical à boutons (type Clan Hub) — famille données
  2. Écran marchand à slots (type Shop) — famille container
  3. Écran de stats/profil (cards + valeurs accentuées)
  4. Liste scrollable (membres, transactions, items)
  5. É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) et ApocalyContainerScreen (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() -> false par défaut

1.9 Cycle de vie (dev consommateur)

  1. Ajouter la dépendance lib
  2. Créer class MonScreen extends ApocalyScreen
  3. Implémenter buildLayout(root) avec des builders
  4. Brancher les callbacks (souvent : envoyer une commande/packet)
  5. 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

ChampValeur
Nom d'affichageApocalyInterfaceLib
Mod IDapocalyinterfacelib
Package racineca.tawess123.apocalyinterface
Auteureloileger
LicenceAll Rights Reserved (interne Apocaly)
VersioningSemVer : MAJOR.MINOR.PATCH
Repo GitGitHub 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 Impl correspondante dans core/

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)

  1. Fond PANEL_BG sur tout le rectangle
  2. 4 bordures BORDER de 2px (haut, bas, gauche, droite)
  3. Barre de titre HEADER_BG de top+2 à top+24
  4. Divider DIVIDER 1px à top+26
  5. Titre centré TITLE à top+8

3.6 Les 4 états de bouton

ÉtatFondBordureTexte
Actif, repos0x800505050xFF5555550xFFFFFFFF
Actif, survol0x991010100xFFFFAA000xFFFFFFCC
Inactif, repos0x700303030xFF3333330xFF666666
Inactif, survol0x881010100xFFAA33330xFFFF6666

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

ConteneurRôle
ColumnEmpile ses enfants verticalement (cas Clan Hub)
RowAligne ses enfants horizontalement (cas Marchand : 3 colonnes)
CardSous-panneau encadré contenant d'autres widgets
GridGrille N colonnes (slots, items)
SpacerEspace vide (gap manuel)
DividerLigne 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 :

WidgetOrigine (pattern Clan)Description
LabeldrawString partoutTexte avec couleur, accent, troncature auto
KeyValueRow« Membres: 0 / 0 » partoutLigne « clé: valeur » alignée
ButtonstyledButtonBouton 4 états + callback
CarddrawSubPanelSous-panneau encadré
ScrollableList<T>scroll réimplémenté 5xListe défilante générique avec renderer
ClickableList<T>LeaderboardListe avec lignes cliquables + hover
ProgressBar(jobs, à venir)Barre de progression
SearchField(shop)Champ de recherche avec callback onChange
ItemIconZoneAnalysisItem MC + count + tooltip
SlotGrid(shop)Grille de slots vanilla (container)
TabBarLeaderboardOnglets 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) :

  1. renderBackground() (assombrissement vanilla)
  2. Calcul du rectangle de panneau (centrage + clamp écran)
  3. PanelRenderer.drawPanel() (fond, bordures, titre)
  4. Délégation : chaque widget du layout dessine sa portion
  5. ScrollController gè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 DataSource qui 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

PhaseDeliverableCritère de « done »
0Ce SPEC v2Validé par eloi
1Setup projet Forge + mod démo séparéLib + mod démo chargent en jeu, keybind du mod démo ouvre un écran vide stylé
2Theme + i18nApocalyColors/Dimensions accessibles, fr_ca/en_us chargés, PanelRenderer.drawPanel reproduit le style Clan
3Layout + widgets de baseColumn/Row/Card + Label/KeyValueRow/Button. Démo : reproduire le haut du Clan Hub
4Widgets avancésScrollableList, ClickableList, ProgressBar, SearchField, ItemIcon, TabBar
5Container + slotsApocalyContainerScreen + SlotGrid. Démo : grille de slots stylée
6DataSource + refreshupdate() relayoute, statique fonctionne de bout en bout
7Repro UIs + build + docClan 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

  1. Ne jamais dévier de l'architecture des packages (section 5.1)
  2. Ne jamais hardcoder de couleurs/dimensions — utiliser ApocalyColors / ApocalyDimensions
  3. Ne jamais utiliser de texte brut dans les widgets — toujours une clé i18n
  4. Toujours utiliser GuiGraphics (jamais PoseStack direct)
  5. Toujours activer le blend pour la translucidité (RenderSystem.enableBlend())
  6. Toujours fournir un Builder pour les widgets publics
  7. Toujours ajouter une Javadoc française sur les API publiques
  8. Un widget = une session = un commit
  9. 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

  1. Rendering via GuiGraphics uniquement (les tutos 1.18/1.19 utilisent PoseStack — obsolète)
  2. Screen : override render(GuiGraphics, int, int, float)
  3. AbstractContainerScreen : override renderBg(...) ET renderLabels(...)
  4. DeferredRegister pour tout enregistrement
  5. Setup client via FMLClientSetupEvent
  6. 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

  1. Lire le SPEC
  2. Annoncer le plan et les fichiers touchés
  3. Écrire le code + la doc
  4. 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