From 8de569bbf188f7b76ede9235d3a2e5449907bcd5 Mon Sep 17 00:00:00 2001 From: ChrisM-Pek Date: Tue, 22 Sep 2026 03:03:48 +0200 Subject: [PATCH] Add access-code gate to Hello World creation (beta.144). Require a site-issued SANC code when the server has app URL and API token configured, with replay-safe ledger binding and FR/EN UI including galactic code display. Co-authored-by: Cursor --- CHANGELOG.md | 7 ++ docs/access-code-beta144.md | 57 +++++++++ gradle.properties | 4 +- mods/sanctuary/build.gradle | 8 ++ .../sanctuary/client/HelloWorldScreen.java | 57 +++++++-- .../sanctuary/client/ProgressionClient.java | 14 ++- .../sanctuary/progression/AccessCodes.java | 77 ++++++++++++ .../sanctuary/progression/AccessConfig.java | 107 ++++++++++++++++ .../sanctuary/progression/AccessLedger.java | 83 +++++++++++++ .../sanctuary/progression/AccessPayloads.java | 38 ++++++ .../progression/ProgressionService.java | 114 +++++++++++++++++- .../assets/sanctuary/lang/en_us.json | 15 +++ .../assets/sanctuary/lang/fr_fr.json | 15 +++ .../progression/AccessCodeSmoke.java | 48 ++++++++ packwiz/pack.toml | 2 +- 15 files changed, 628 insertions(+), 18 deletions(-) create mode 100644 docs/access-code-beta144.md create mode 100644 mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/AccessCodes.java create mode 100644 mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/AccessConfig.java create mode 100644 mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/AccessLedger.java create mode 100644 mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/AccessPayloads.java create mode 100644 mods/sanctuary/src/test/java/fr/koka/sanctuary/progression/AccessCodeSmoke.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d5b67d..7227703 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## beta.144 — Code d’accès à la création + +- Hello World demande le code `SANC-XXXX-XXXX` quand le serveur a l’URL et le jeton du site. +- Le client ne contacte pas le site. Le serveur vérifie, puis consomme le code au moment de créer l’habitant. +- Le lien Discord est enregistré à part. Une création interrompue après `redeemed` peut se terminer sans nouveau code si le compte est encore connu. +- [Contrat](docs/access-code-beta144.md). + ## beta.110 — Atelier d’argile inclus - Une seule livraison réunit l’atelier d’argile, les statuaires et l’import GLB de beta.106 avec les œufs et la neige saisonnière de beta.107 à beta.109. diff --git a/docs/access-code-beta144.md b/docs/access-code-beta144.md new file mode 100644 index 0000000..b181f7e --- /dev/null +++ b/docs/access-code-beta144.md @@ -0,0 +1,57 @@ +# beta.144 — Code d’accès à la création + +Le site accepte la candidature, whitelist le pseudo et envoie un code `SANC-XXXX-XXXX`. +Hello World demande ce code avant de créer le personnage. Le client ne parle pas +au site. Seul le serveur appelle l’API, avec le jeton `SANCTUARY_API_TOKEN`. + +Sans ces réglages, Hello World reste celui de beta.143 : biographie, couleur et +familier, sans code. Dès que l’URL et le jeton sont présents, un nouveau +personnage exige un code reconnu. Un habitant déjà enregistré entre sans écran. + +## Réglage serveur + +Variables d’environnement, ou fichier `config/sanctuary/access.json` si une +variable manque : + +```json +{"appUrl":"https://exemple.sanctuary","token":"..."} +``` + +`appUrl` est l’origine du site, sans barre finale. Le serveur appelle +`POST {appUrl}/api/v1/access-codes/verify` puis `redeem`. Le jeton n’est pas +écrit dans les logs, le client ou le pack. Une URL ou un jeton illisible ferme +la création : aucun personnage n’est inventé hors ligne. + +## Parcours + +1. La whitelist laisse passer le pseudo. L’écran s’ouvre tant que l’UUID n’a pas + d’habitant. +2. Le joueur saisit le code. L’affichage du champ et de l’exemple utilise + l’alphabet galactique standard (`minecraft:alt`, la police de la table + d’enchantement). La valeur envoyée reste le texte tapé. Un format complet + déclenche `verify`, pas chaque frappe. « Code reconnu » n’écrit rien. +3. Confirmer envoie le code de la session, la biographie, la couleur et le + familier. Le pseudo envoyé au site est celui de la session. +4. `redeem` répond `redeemed` avec `discord_id` : le lien est enregistré, puis + l’habitant est créé. Les connexions suivantes sautent l’écran. +5. Erreur, site injoignable ou jeton refusé : le joueur reste sur Hello World. + +Une déconnexion avant `redeemed` ne consomme pas le code. Si le site a répondu +`redeemed` et que l’écriture de l’habitant est interrompue, le lien Discord +reste dans `data/sanctuary-access.json` (`pending`, schéma 1, graine du monde). +La confirmation suivante termine le personnage sans rappeler `redeem`. + +Si le code est déjà consommé et que le site ne renvoie plus `discord_id`, le +joueur reste bloqué avec un message de reprise. Le site doit alors renvoyer +`minecraft_username` et `discord_id` pour le même pseudo. Le mod accepte cette +réponse, que `valid` soit vrai ou faux, et finit la création. + +Le registre des habitants ne change pas. Le Discord est un fichier à part. +Un fichier illisible est conservé et refuse l’accueil. + +## Limites + +Le gel dans le monde n’est pas ajouté : Hello World reste avant l’entrée, comme +aujourd’hui. Inventaire, commandes et dimensions ne sont pas accessibles tant +que l’habitant n’existe pas. La whitelist RCON reste celle du site. Un pseudo +ajouté à la main, sans code, voit l’écran sans pouvoir le valider. diff --git a/gradle.properties b/gradle.properties index f5825be..57c86fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,8 @@ loom_version=1.17.20 fabric_api_version=0.160.5+26.3 # Release counter: beta.001, .002, .003, ... (see docs/versioning.md). -mod_version=beta.143 -pack_version=beta.143 +mod_version=beta.144 +pack_version=beta.144 resource_pack_version=beta.121 maven_group=fr.koka.sanctuary jei_version=30.32.0-sanctuary.3 diff --git a/mods/sanctuary/build.gradle b/mods/sanctuary/build.gradle index 577a118..3eb2d11 100644 --- a/mods/sanctuary/build.gradle +++ b/mods/sanctuary/build.gradle @@ -1137,6 +1137,14 @@ tasks.register('intro031Smoke', JavaExec) { } tasks.named('check') { dependsOn('intro031Smoke') } +tasks.register('access144Smoke', JavaExec) { + group = 'verification' + dependsOn('testClasses') + classpath = sourceSets.test.runtimeClasspath + mainClass = 'fr.koka.sanctuary.progression.AccessCodeSmoke' +} +tasks.named('check') { dependsOn('access144Smoke') } + if (providers.gradleProperty('sanctuaryQuickTests').map { it.toBoolean() }.getOrElse(false)) { dependencies { gametestRuntimeOnly project(':sanctuary-test') } } diff --git a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/HelloWorldScreen.java b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/HelloWorldScreen.java index 809f73e..a3f2b2f 100644 --- a/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/HelloWorldScreen.java +++ b/mods/sanctuary/src/main/java/fr/koka/sanctuary/client/HelloWorldScreen.java @@ -12,13 +12,20 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.resources.Identifier; import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.FontDescription; +import net.minecraft.network.chat.Style; +import net.minecraft.util.FormattedCharSequence; import net.minecraft.ChatFormatting; /** Configuration-stage identity and starter slots. Closing cancels connection, never the saved offer. */ public final class HelloWorldScreen extends Screen { + private static final FontDescription GALACTIC = new FontDescription.Resource(Identifier.withDefaultNamespace("alt")); + private static final Style GALACTIC_STYLE = Style.EMPTY.withFont(GALACTIC); private ProgressionPayloads.Hello offer;private final Runnable disconnect; private EditBox bio,familiarName;private int color=-1;private Button submit;private boolean waiting,identityLocked; - private String draft="",starter="",nameDraft="";private List starterChoices=List.of(); + private String draft="",starter="",nameDraft="",codeDraft="",codeStatus="",lastChecked=""; + private boolean accessRequired,accessPending,admitting;private EditBox codeBox; + private List starterChoices=List.of(); private final List