Compare commits
45
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c85d49d47a | ||
|
|
db6194c3c0 | ||
|
|
9613322c6b | ||
|
|
96433d3306 | ||
|
|
ba4cf19233 | ||
|
|
968c8d7a43 | ||
|
|
b9727dfa41 | ||
|
|
63e4dab140 | ||
|
|
9f993989b0 | ||
|
|
24c3315f56 | ||
|
|
55c745a46d | ||
|
|
43ac3c7c5f | ||
|
|
6e2336db9d | ||
|
|
3faea2c9af | ||
|
|
8ab5a45bd3 | ||
|
|
fff288f27e | ||
|
|
0315fe2b05 | ||
|
|
c2dfa9e171 | ||
|
|
b49b3e2549 | ||
|
|
e382a65aae | ||
|
|
7979f33028 | ||
|
|
460c4706d3 | ||
|
|
ad960ef9bc | ||
|
|
835e6116e6 | ||
|
|
aadff6fd5e | ||
|
|
705fe9c1a0 | ||
|
|
dbb9e3428d | ||
|
|
9a429c40a1 | ||
|
|
905b3bcfa1 | ||
|
|
0c55a37562 | ||
|
|
8deba7cfb4 | ||
|
|
c1d2278674 | ||
|
|
bdaad0c940 | ||
|
|
19aa8054a5 | ||
|
|
9791d178ab | ||
|
|
32158e2e05 | ||
|
|
3bfb3cb251 | ||
|
|
0eb1a381d4 | ||
|
|
8de569bbf1 | ||
|
|
b97ec6c9e8 | ||
|
|
448e2febff | ||
|
|
1c23d0c17e | ||
|
|
6f871446bb | ||
|
|
96a36d99be | ||
|
|
cf9a670f44 |
@@ -11,6 +11,7 @@ La vision est dans `docs/vision.md` ; elle décrit aussi des fonctionnalités fu
|
||||
- Ne pas modifier un monde existant, régénérer des chunks, changer un format de sauvegarde ou activer une expansion sans contrat de migration explicite.
|
||||
- Garder les identifiants `sanctuary:*` stables et documenter toute évolution de la génération avec graine et version.
|
||||
- Ajouter les libellés FR/EN des nouvelles interfaces. Vérifier les dépendances pour la version Minecraft exacte ; aucune compatibilité supposée à partir du nom d'un mod.
|
||||
- Les tests graphiques Sanctuary ciblent uniquement Vulkan. OpenGL est abandonné comme cible de validation depuis le 18 septembre 2026 : ne plus lancer de suite OpenGL ni revendiquer sa prise en charge à partir des essais historiques.
|
||||
- Lancer `./gradlew check build` pour livrer du code, et `./gradlew assemblePack` si la distribution change. Ajouter seulement les tests utiles au comportement touché.
|
||||
- Pour chaque nouvelle livraison du mod, incrémenter le compteur `beta.xxx` (départ `beta.001`) dans `mod_version` et `pack_version` de `gradle.properties`, synchroniser `packwiz/pack.toml` et documenter le changement. Le tag reprend cette version exacte, sans préfixe `v` ; voir `docs/versioning.md`. Une simple modification documentaire n'incrémente pas les binaires.
|
||||
- Le pack Beta suit un canal packwiz stable et une seule instance Prism. Pour une mise à jour demandée, suivre `docs/packwiz.md` : publier un artefact vérifié et immuable, avancer le canal, puis synchroniser l'instance existante en conservant ses sauvegardes et réglages.
|
||||
|
||||
@@ -1,5 +1,74 @@
|
||||
# beta.172 — cartes de horde utilisables en jeu
|
||||
|
||||
- Suppression de la restriction au cercle laboratoire : une carte peut ouvrir
|
||||
une invasion à la position du joueur dans tout monde chargé.
|
||||
- Activation autorisée en Survie et en Créatif, avec consommation serveur et
|
||||
synchronisation immédiate de l'inventaire dans les deux modes.
|
||||
- Les joueurs créatifs restent participants aux invasions de carte ; le socle
|
||||
historique conserve ses anciennes règles d'inscription.
|
||||
- Les apparitions cherchent un sol sûr jusqu'à six blocs au-dessus ou en dessous
|
||||
du point prévu, sans écrire de bloc ni charger de chunk supplémentaire.
|
||||
- [Contrat et vérifications](docs/horde-runtime-beta172.md).
|
||||
|
||||
# beta.171 — familles de hordes dimensionnelles
|
||||
|
||||
- Chaque carte possède une famille majoritaire stable : zombies, squelettes,
|
||||
creepers, arthropodes, pillards, cauchemars, infernaux, piglins, flétris ou End.
|
||||
- La dimension de découverte favorise ses familles et espèces ; des créatures
|
||||
locales aléatoires et de rares intrus interdimensionnels cassent la régularité.
|
||||
- Catalogue porté à 34 monstres terrestres ou volants, du zombie au blaze, au
|
||||
ghast et au shulker ; les boss et créatures strictement aquatiques sont exclus.
|
||||
- Illustration divisée : file exacte des apparitions en haut, toutes les piles
|
||||
de butin garanties en bas, avec chevauchement lorsque nécessaire.
|
||||
- Le vieux socle rejoint la carte native dans le langage de particules et
|
||||
n'émet plus de messages Horde dans le chat ou la barre d'action.
|
||||
- [Contrat et vérifications](docs/horde-families-beta171.md).
|
||||
|
||||
# beta.170 — carte de horde native et langage visuel
|
||||
|
||||
- Nouvel objet `sanctuary:horde_trial_card` fondé sur la carte native 26.3,
|
||||
visible dans l'onglet créatif Outils et utilitaires.
|
||||
- Chaque exemplaire vierge tenu en main découvre une identité unique et stable ;
|
||||
les copies d'une carte découverte gardent son identité.
|
||||
- Révélation, activation, progression, refus et fin d'invasion passent par des
|
||||
connexions et glyphes de particules, sans message Sanctuary dans le chat.
|
||||
- Compatibilité conservée avec les cartes remplies beta.168/beta.169.
|
||||
- [Contrat et vérifications](docs/horde-map-native-beta170.md).
|
||||
|
||||
# beta.169 — invasion continue des cartes de horde
|
||||
|
||||
- Les cartes sont inconnues avant leur prise en main et se révèlent durablement.
|
||||
- Remplace les trois vagues par 12, 18 ou 27 arrivées individuelles qui accélèrent.
|
||||
- Mélange jusqu’à dix espèces selon la difficulté ; dessin et butins suivent le roster réel.
|
||||
- Butins généreux propres aux monstres, rubis et saphirs inclus ; anciennes cartes préservées.
|
||||
- Runes SGA natives, combat spontané élargi et interruptions diagnostiquées.
|
||||
- [Contrat et vérifications](docs/horde-invasion-beta169.md).
|
||||
|
||||
# beta.168 — cartes de horde (prototype local)
|
||||
|
||||
- Trois cartes natives illustrées et consommables ; invocation immédiate sans socle.
|
||||
- Participation spontanée et butins physiques sur les zombies, ramassage libre.
|
||||
- Nouveau labo isolé beta.168 ; cercle beta.167 conservé pour les futurs donjons.
|
||||
- [Contrat et vérifications](docs/horde-cartes-beta168.md).
|
||||
|
||||
# Changelog
|
||||
|
||||
## beta.167 — Carte de horde et socle d'épreuve, prototype local
|
||||
|
||||
- Arène circulaire dans un nouveau laboratoire, scène préservée aux redémarrages.
|
||||
- Carte réutilisable, inscriptions de 1 à 4 joueurs, préparation et lancement
|
||||
explicites, trois vagues de zombies et résultat commun.
|
||||
- Nettoyage des monstres à l'interruption et reprise du labo au repos ; aucune
|
||||
récompense économique, aucun changement de monde Sanctuary existant.
|
||||
- [Contrat et vérifications](docs/horde-lab-beta167.md).
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -1,5 +1,260 @@
|
||||
# Sanctuary
|
||||
|
||||
## beta.178 — vallées bornées et grottes rocheuses
|
||||
|
||||
Le [nouvel essai solo](docs/rocky-ecology-beta178.md) limite l’automne aux
|
||||
vallées Y=200–232 et expose la roche sur les flancs profonds. Les grottes
|
||||
gardent des mares et de petites plaques de mousse. Nouveau monde de labo,
|
||||
graine 42, vue à 32 chunks et commandes activées ; contrôle ciblé réussi,
|
||||
suite générale et assemblages réussis.
|
||||
|
||||
## beta.177 — les biomes suivent le relief
|
||||
|
||||
Le [nouvel essai de laboratoire](docs/relief-ecology-beta177.md) réunit
|
||||
forêts et plaines fleuries sur le plateau, automne dans les creux extérieurs,
|
||||
cerisiers sur un sommet local et végétation humide sous roche. La mangrove
|
||||
et la neige sont retirées ; des gisements de pierre interrompent les strates.
|
||||
Solo Vulkan à 32 chunks, relevés natifs et réouverture vérifiés ; 265/265
|
||||
GameTests, `check build` et assemblages réussis. Détails dans la fiche.
|
||||
|
||||
## beta.176 — strates, biomes et mares du labo
|
||||
|
||||
Le [nouveau laboratoire écologique](docs/island-ecology-beta176.md) conserve
|
||||
le relief beta.175 et ajoute des strates ondulées, de grandes régions
|
||||
automnales et de cerisiers, des récifs végétalisés et des mares locales.
|
||||
Ses biomes excluent les structures natives, notamment les mineshafts.
|
||||
Trois graines et une réouverture vérifiées ; visite solo Vulkan à 32 chunks.
|
||||
Une réserve sur la suite générale des familiers est détaillée dans la fiche.
|
||||
Aucune modification des mondes existants.
|
||||
|
||||
## beta.175 — récifs aériens et minerais du labo
|
||||
|
||||
Le [nouveau profil de laboratoire](docs/sky-fragments-beta175.md) remplace les
|
||||
anciennes masses flottantes par des récifs rares en bruit 3D, au-dessus de
|
||||
l’île principale conservée. Y=512–639 reste réservé à l’ISS. Cuivre et charbon
|
||||
abondants, peu de fer, lapis et améthyste, diamant enfoui dans la deepslate ;
|
||||
aucun or ni redstone dans cette répartition. Relevés natifs plutôt que vues
|
||||
en jeu ; contrat, mesures et limites dans la fiche du lot.
|
||||
|
||||
## beta.174 — laboratoire de relief Sanctuary
|
||||
|
||||
Le [labo worldgen](docs/worldgen-lab-beta174.md) ajoute un preset distinct
|
||||
pour travailler le relief de l'île sans les calculs de plans d'eau et de
|
||||
structures. La génération normale est conservée. Profil rapide et référence
|
||||
complète utilisent des mondes de développement séparés. Les résultats de
|
||||
validation et les limites sont consignés dans la fiche du lot.
|
||||
|
||||
## Refonte de Sanctuary Island — direction du 29 septembre
|
||||
|
||||
Le palais souterrain, ses accès et les raccordements au palais sont abandonnés
|
||||
dans la conception. Le [fil rouge de la refonte](docs/storyquest-fil-rouge.md)
|
||||
situe le socle, propose des plans géographiques indépendants et organise la
|
||||
suite en parcours jouables qui font avancer plusieurs systèmes ensemble.
|
||||
La priorité suivante est le labo worldgen rapide ci-dessus, puis un parcours
|
||||
arrivée, halte extérieure, ancre et conséquence visible. L'esthétique reste
|
||||
à comparer sur ce parcours.
|
||||
|
||||
## beta.173 — prototype du palais, conservé comme essai technique
|
||||
|
||||
Un palais octogonal par seed, huit ancres avec des matériaux distincts et le bloc
|
||||
originel en bedrock : éteint, allumé en noir et blanc, puis une gemme colorée par
|
||||
ancre activée. [Prototype, laboratoire et vérifications](docs/palais-prototype-beta173.md).
|
||||
Le prototype reste disponible dans son laboratoire ; son intégration sous
|
||||
Sanctuary Island sort du plan depuis le changement de direction du 29 septembre.
|
||||
|
||||
Le [cadrage Storyquest](docs/storyquest-beta173.md) et l'[inventaire du socle
|
||||
beta.172](docs/storyquest-socle-beta172.md) conservent les autres chantiers de
|
||||
refonte. Les priorités courantes sont dans le fil rouge ci-dessus.
|
||||
Branche `codex/storyquest-beta173`.
|
||||
|
||||
## beta.172 — cartes utilisables en jeu
|
||||
|
||||
Une carte révélée ouvre désormais sa brèche à la position du joueur dans
|
||||
n'importe quel monde, en Survie comme en Créatif. L'exemplaire est consommé
|
||||
côté serveur dans les deux modes ; le terrain n'est pas modifié et les arrivées
|
||||
cherchent un sol valide autour du point d'invocation.
|
||||
[Contrat et vérifications](docs/horde-runtime-beta172.md).
|
||||
|
||||
## beta.171 — hordes dominantes et chaos dimensionnel
|
||||
|
||||
Chaque carte choisit une famille dominante — zombies, squelettes, creepers,
|
||||
arthropodes, pillards, cauchemars, infernaux, piglins, flétris ou créatures de
|
||||
l'End — puis y glisse des surprises. La dimension de découverte favorise très
|
||||
fortement ses propres monstres, sans interdire de rares intrus. La carte est
|
||||
séparée horizontalement : ordre exact des arrivées en haut, totalité du butin
|
||||
généreux en bas. [Contrat et vérifications](docs/horde-families-beta171.md).
|
||||
|
||||
## beta.170 — carte de horde native et langage de particules
|
||||
|
||||
La carte de horde est désormais un véritable objet-carte Sanctuary, disponible
|
||||
dans Outils et utilitaires. Chaque exemplaire vierge découvre en main une
|
||||
identité stable et différente ; difficulté, dessin, monstres et butins restent
|
||||
liés à cette carte. Révélation, refus, brèche, progression et résultat sont
|
||||
communiqués uniquement par des connexions de particules, sans message Sanctuary
|
||||
dans le chat. [Contrat et vérifications](docs/horde-map-native-beta170.md).
|
||||
|
||||
## beta.169 — cartes de horde à invasion continue
|
||||
|
||||
Une carte reste inconnue jusqu’à sa prise en main, puis révèle sa difficulté, ses
|
||||
12, 18 ou 27 monstres variés et leurs butins. Le clic droit ouvre une invasion
|
||||
sans vagues : les ennemis jaillissent un par un, de plus en plus vite. Récompenses
|
||||
généreuses propres aux espèces, rubis/saphirs et runes SGA natives. Les cartes
|
||||
beta.168 existantes restent compatibles.
|
||||
[Contrat et vérifications](docs/horde-invasion-beta169.md).
|
||||
|
||||
## beta.168 — trois cartes de horde consommables
|
||||
|
||||
Labo sans socle, apparition immédiate, combat spontané et butins sur les monstres.
|
||||
Trois cartes illustrées tenues comme des cartes Minecraft, trois difficultés.
|
||||
[Essayer le laboratoire et connaître ses limites](docs/horde-cartes-beta168.md).
|
||||
|
||||
## beta.167 — prototype local de horde coopérative
|
||||
|
||||
Une arène ronde de laboratoire, une carte réutilisable et un socle interactif
|
||||
pour réunir un groupe et lancer trois vagues de zombies. Inscription et départ
|
||||
explicites, résultat commun, sans récompense économique dans ce prototype.
|
||||
[Contrat, lancement et vérifications](docs/horde-lab-beta167.md).
|
||||
Le laboratoire utilise un nouveau monde ; aucune mise à jour de Prism ni du
|
||||
canal public n'est effectuée par ce chantier.
|
||||
|
||||
[Dernière release : beta.166](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.166) ·
|
||||
[Historique des publications beta.144–166](docs/publications-beta144-166.md).
|
||||
Les releases sont distinctes du canal packwiz, qui reste inchangé lors de ce rattrapage.
|
||||
|
||||
La [direction de travail du 25 septembre](docs/ecosysteme-communaute-economie.md)
|
||||
pose les liens entre tableau, Gazette, statistiques en BDD, État, navets,
|
||||
machines de loterie, bounties et expansions. C'est un cadrage de conception,
|
||||
avec décisions et propositions séparées, sans nouvelle livraison de jeu.
|
||||
|
||||
## beta.166 — inscription depuis le site
|
||||
|
||||
Accueil avec code récupéré sur le web et reprise après coupure.
|
||||
[Contrat, migration et vérifications](docs/inscription-web-beta166.md).
|
||||
|
||||
## Vérification du socle beta.165
|
||||
|
||||
Les 23 échecs historiques ont été corrigés dans les scénarios de test :
|
||||
252/252 GameTests passent, ainsi que `check build`. Code de jeu inchangé.
|
||||
[Actualisation et preuves](docs/actualisation-tests-beta165.md).
|
||||
|
||||
## beta.165 — article Gazette et conversation
|
||||
|
||||
Grande photo et texte dans un même conteneur à gauche, auteur dessous, réponses
|
||||
à droite ou sous l’article sur petit écran.
|
||||
[Résultat et vérifications](docs/gazette-scene-beta165.md) ·
|
||||
[Audit des 23 échecs connus](docs/audit-echecs-beta165.md).
|
||||
|
||||
## beta.164 — calendrier et intendance
|
||||
|
||||
Date et heure réunies à gauche ; titre serveur aligné sur la carte et les panneaux.
|
||||
[Résultat et vérifications](docs/pause-calendar-beta164.md).
|
||||
|
||||
## beta.163 — menu pause encadré
|
||||
|
||||
Calendrier, intendance et horloge en en-tête ; deux groupes de navigation,
|
||||
séparateurs natifs et reprise centrée dans le footer.
|
||||
[Résultat et vérifications](docs/pause-frame-beta163.md).
|
||||
|
||||
## beta.162 — tableau en scène et conversation
|
||||
|
||||
Portrait de l’auteur tourné vers la souris, bulle aux couleurs de l’annonce,
|
||||
demande à gauche et réponses à droite. Retour/Actualiser restent en haut ;
|
||||
la conversation passe dessous sur les GUI étroits.
|
||||
[Résultat et vérifications](docs/notice-scene-beta162.md).
|
||||
|
||||
## beta.161 — correctifs de séance et site vivant
|
||||
|
||||
Liste des joueurs sur U, suivi des demandes expliqué, intendance compacte et
|
||||
compteurs réels partagés avec le site. Site public en lecture seule, Gazette
|
||||
en accordéon avec aperçus compacts.
|
||||
[Contrat et vérifications](docs/session-fixes-beta161.md).
|
||||
|
||||
## beta.160 — laboratoire de test en duo
|
||||
|
||||
Shader désactivé sur une configuration neuve, serveur local léger, personnages
|
||||
de test optionnels et séances de captures horodatées.
|
||||
[Mode d’emploi et vérifications](docs/duo-lab-beta160.md) ·
|
||||
[Récap Discord 130–160](docs/discord-beta130-160.md).
|
||||
|
||||
## beta.159 — recherche communautaire et galerie adaptable
|
||||
|
||||
Recherche par joueur, titre ou texte, historique Serveur en lecture seule,
|
||||
année dans les dates et galerie adaptée au GUI avec grand aperçu.
|
||||
Repères « ! » et infobulles sur la carte pour les demandes suivies géolocalisées.
|
||||
[Résultat et vérifications](docs/community-search-beta159.md).
|
||||
|
||||
## beta.158 — cartes communautaires et demandes suivies
|
||||
|
||||
Grilles à deux colonnes, articles photo/texte, annonces colorées avec visages,
|
||||
coffres descriptifs et suivi persistant fichier/MariaDB. Sélecteur photo compact.
|
||||
[Contrat et vérifications](docs/community-cards-beta158.md).
|
||||
|
||||
## beta.157 — photo obligatoire dans la Gazette
|
||||
|
||||
Galerie de captures défilante, aperçu et assignation à un article. Publication
|
||||
avec photo obligatoire, stockage fichier ou MariaDB partagé avec le site.
|
||||
[Contrat de migration et vérifications](docs/gazette-photos-beta157.md).
|
||||
|
||||
## beta.156 — reprendre la partie sous la colonne communautaire
|
||||
|
||||
Le bouton Reprendre la partie est aligné sous la Gazette et le tableau
|
||||
d’affichage, en bas à droite. [Vérifications](docs/pause-resume-beta156.md).
|
||||
|
||||
## beta.155 — nouvelle structure du menu pause
|
||||
|
||||
Navigation Sanctuary à gauche, carte centrale, Gazette et demandes défilantes
|
||||
à droite. Date, intendance et heure en en-tête ; options et sortie en bas de la colonne
|
||||
gauche, reprise de la partie en bas au centre.
|
||||
[Contrat et vérifications](docs/pause-redesign-beta155.md).
|
||||
|
||||
## beta.154 — Gazette et tableau communautaire
|
||||
|
||||
Panneaux gauche/droite du menu pause, intendance, publication, réponses,
|
||||
modification et modération. Stockage fichier par défaut ; MariaDB optionnelle
|
||||
commune avec le site. Shaders beta.151 conservés.
|
||||
[Contrat et configuration](docs/community-contract-v1.md) ·
|
||||
[Vérifications et limites](docs/community-beta154.md).
|
||||
|
||||
## beta.151 — Nether, reflets des blocs et SSR
|
||||
|
||||
Synchronisation de l’inventaire au changement de dimension, retour de l’intensité
|
||||
spéculaire des blocs solides à son niveau antérieur et bois mat avec relief.
|
||||
SSR activé par défaut à 20 %, choix sauvegardés conservés.
|
||||
[Contrat et vérifications](docs/nether-pbr-beta151.md).
|
||||
|
||||
## beta.150 — Terre mate et reflets du coucher de soleil
|
||||
|
||||
Terre mate avec relief conservé. Le soleil et son reflet se colorent ensemble
|
||||
en suivant la couleur et la transition du coucher de soleil natif de Minecraft.
|
||||
Les accents lumineux suivent les normales animées des vaguelettes et leur
|
||||
orientation vers le soleil et la caméra. [Contrat et vérifications](docs/matte-dirt-beta150.md).
|
||||
|
||||
## beta.149 — Distances PBR / SSR et feuillage
|
||||
|
||||
Deux distances séparées, 64 blocs par défaut chacune, jusqu’à 256 blocs.
|
||||
Option renommée « SSR » et suppression du voile spéculaire sur le feuillage.
|
||||
[Contrat et vérifications](docs/independent-distances-beta149.md).
|
||||
|
||||
## beta.148 — PBR 50 % et soleil diffus
|
||||
|
||||
PBR à 50 %, reflet solaire plus doux avec fusion par texel préservant
|
||||
la texture de l’eau. SSR à 20 %, portée commune jusqu’à 256 blocs.
|
||||
[Contrat et vérifications](docs/pbr-soft-sun-beta148.md).
|
||||
|
||||
## beta.147 — PBR proche, lune et portée 256
|
||||
|
||||
Correction du scintillement à très courte distance, reflet lunaire adouci
|
||||
et distance commune PBR/SSR jusqu’à 256 blocs.
|
||||
[Contrat et vérifications](docs/pbr-close-range-beta147.md).
|
||||
|
||||
## beta.146 — Reflets et transitions de l’eau
|
||||
|
||||
Correction du masquage des personnages en F5 et sélection stricte des surfaces PBR.
|
||||
PBR à 80 % et SSR à 20 % par défaut, distance commune PBR/SSR
|
||||
(16/32/64/128 blocs), reflet solaire large et blanc avec léger bloom des matières.
|
||||
Eau naturelle selon le type de monde et mélange 1/3/5/9/17 blocs.
|
||||
[Contrat et vérifications](docs/reflections-water-beta146.md).
|
||||
|
||||
## beta.145 — PBR de l’eau et reflets pixélisés (livraison locale)
|
||||
|
||||
L’eau et le verre rejoignent le PBR natif. L’option expérimentale de reflets
|
||||
@@ -2328,7 +2583,7 @@ Depuis beta.090, la cible est **Minecraft 26.3 finale**, publiée le 15 septembr
|
||||
| Fabric API | 0.160.5+26.3 |
|
||||
| Fabric Loom | 1.17.20 |
|
||||
| Gradle Wrapper | 9.5.1, distribution vérifiée par SHA-256 |
|
||||
| Sanctuary / pack | beta.122 |
|
||||
| Sanctuary / pack | beta.166 |
|
||||
|
||||
Java 25 et Python 3.11 ou plus récent sont nécessaires. Le script pack utilise
|
||||
uniquement la bibliothèque standard et repère aussi une installation Python
|
||||
@@ -2357,7 +2612,7 @@ décrits dans [Validation](docs/testing.md).
|
||||
|
||||
Résultats :
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.122.jar` : mod à installer avec
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.166.jar` : mod à installer avec
|
||||
Fabric API sur la version Minecraft indiquée.
|
||||
- `build/packwiz/` : pack de développement complet, avec le mod construit et
|
||||
l'index vérifié. Voir [Installation packwiz](packwiz/README.md).
|
||||
|
||||
@@ -136,3 +136,10 @@ intégré et le template personnel (SHA-256
|
||||
`a97570908db75caf8e6fc2f5ecabf12c54fd99bfea7be634df66c4310dca22af`).
|
||||
Le template regroupe les ressources client déjà distribuées par Sanctuary ;
|
||||
leurs crédits et conditions respectives restent applicables.
|
||||
|
||||
MariaDB Connector/J 3.5.10 (`org.mariadb.jdbc:mariadb-java-client`),
|
||||
MariaDB Corporation et contributeurs : LGPL-2.1-or-later. Le JAR officiel est
|
||||
embarqué comme dépendance imbriquée, sans modification, pour le stockage
|
||||
communautaire optionnel. Sa licence est fournie dans `licenses/mariadb-connector-j-LGPL-2.1.txt` du JAR Sanctuary.
|
||||
Sources correspondantes : https://github.com/mariadb-corporation/mariadb-connector-j/tree/3.5.10
|
||||
(distribution Maven Central 3.5.10).
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# beta.144 — Code d’accès à la création
|
||||
|
||||
Document historique de la PR de Chris. Le contrat de reprise ci-dessous est
|
||||
remplacé par [beta.166](inscription-web-beta166.md), notamment pour les codes
|
||||
consommés et les déconnexions.
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Actualisation des tests — socle beta.165
|
||||
|
||||
Suite à l’audit des 23 échecs. Changements limités aux GameTests et à leur
|
||||
préparation ; aucun changement des règles, des données de production ou de
|
||||
format de monde. Pas de nouvelle version binaire : le laboratoire reste beta.165.
|
||||
|
||||
## Scénarios corrigés
|
||||
|
||||
- Placement : joueur à portée réelle, sans occuper la cellule visée. Les compteurs,
|
||||
événements, empreintes et les huit diamants de la tombe restent vérifiés.
|
||||
- Inventaires : données d’ouverture explicites pour l’atelier d’argile et le
|
||||
multibloc, avec maintien de la boucle sur toutes les entrées du registre.
|
||||
- Familiers : achat de l’accès puis commande serveur de mode travail ; le helper
|
||||
vérifie aussi que le mode combat n’accorde pas les anciens passifs. Les bonus,
|
||||
consommation de ressources et annulations restent vérifiés.
|
||||
- Ancienne invulnérabilité : test des coups amicaux sans dégâts, du K.-O. sur
|
||||
dégât létal et de la conservation de l’œuf. Le poisson terrestre utilise son
|
||||
profil actuel et garde sa vérification de transition aquatique.
|
||||
- Permissions : accès public à l’introduction, restrictions opérateur sur names
|
||||
et community admin. Catalogue recompte les 67 documents, 2042 recettes,
|
||||
1780 items, 1374 blocs et 1937 identifiants distincts. Hauteur actuelle 640.
|
||||
- Dragon : cycle natif tickNonPassenger (commonTick + tick), compteur d’entité
|
||||
contrôlé, déplacement/altitude/collision conservés.
|
||||
- Dalle/coffre : visée à portée du dessus réel, sans collision du joueur avec la
|
||||
surface à bâtir. Les assertions de fusion, collisions et contenu restent.
|
||||
- Hydrologie : les ouvertures OUTLET doivent être des coupes sans source ni
|
||||
sédiment, appartenant à un déversement terminal déclaré. Les autres cellules
|
||||
conservent les contrôles de support naturel.
|
||||
|
||||
## Diagnostics indépendants
|
||||
|
||||
- [Construction](audit-construction-beta165.md)
|
||||
- [Dragon](audit-dragon-beta165.md)
|
||||
- [Berges](audit-berges-beta165.md)
|
||||
|
||||
## Validation
|
||||
|
||||
Premier rejeu ciblé : **81/81 tests obligatoires passent** en 1 min 45 s.
|
||||
Journal : `build/test-refresh-focused.log`.
|
||||
Groupes : inventory, inventoryflow, companions, refonte, accessories, graves,
|
||||
collections, progression, demeure. Ce rejeu valide notamment les nouvelles
|
||||
assertions atteintes et le déplacement du dragon. Construction et hydrologie
|
||||
sont réservées au rejeu complet suivant.
|
||||
|
||||
`./gradlew check build --continue -PsanctuaryQuickTests=true` : **BUILD SUCCESSFUL**
|
||||
en 7 min 11 s. **252/252 GameTests obligatoires réussis**, zéro échec, puis
|
||||
les autres contrôles de `check` terminent avec succès.
|
||||
Journal : `build/test-refresh-full.log`.
|
||||
|
||||
Les quatre cas analysés par les agents passent dans ce rejeu, sans modification
|
||||
production. Le patch supplémentaire de parois OUTLET proposé à titre conditionnel
|
||||
n’a pas été appliqué : aucune assertion correspondante n’a échoué.
|
||||
Ce résultat porte sur la suite configurée ci-dessus ; aucune stabilité statistique
|
||||
sur plusieurs graines, plateformes ou répétitions n’est revendiquée.
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
# Audit du support naturel des berges — beta.165
|
||||
|
||||
## Verdict
|
||||
|
||||
L’échec initial est une **attente de test antérieure aux ouvertures de berges
|
||||
alpha.30**, avec une confiance très forte. La cellule signalée n’est pas une
|
||||
terrasse sèche avec sédiments : c’est la deuxième ouverture `Kind.OUTLET` du
|
||||
plan régional. Son absence de fondation est intentionnelle. Aucune modification
|
||||
de génération n’est recommandée pour faire passer cette assertion.
|
||||
|
||||
Ce diagnostic ne démontre pas que le reste du test passe : celui-ci échoue dès
|
||||
la préparation, avant sa vérification des blocs après décoration et ticks.
|
||||
Aucun monde, chunk, fichier de production ou test partagé n’a été modifié pour
|
||||
cet audit. Aucun serveur ni JVM supplémentaire n’a été lancé.
|
||||
|
||||
## Preuves et chaîne de traitement
|
||||
|
||||
1. `build/beta165-check-build.log:962` signale, au tick 0 :
|
||||
`Cell[x=-141, z=-86, waterY=-1, bedY=238, carveTop=241, material=STONE,
|
||||
featureId=15307446929, sedimentDepth=0]`.
|
||||
2. `PopulationHydrologyRuntime.regionPlan` construit un plan classique admis,
|
||||
puis applique `BankOutlets30.openBanks` pour les réglages `unified_5/10/20`.
|
||||
La densité vient du même `finalDensity` et du même `RandomState` que le
|
||||
diagnostic, via un mémo de signe. Les coordonnées locales sont translatées
|
||||
avec l’origine régionale. Dans la région `(0,0)`, cette translation est nulle.
|
||||
3. `BankOutlets30.java:30–45` cherche le vide à 1–5 blocs d’un bassin et crée
|
||||
des cellules sèches de profondeur de sédiments zéro, classées `OUTLET`.
|
||||
Ces cellules n’ajoutent ni roche ni eau : elles décrivent la coupe de la berge.
|
||||
4. Recalcul indépendant des opérations entières 64 bits en Python, sans moteur
|
||||
Minecraft : pour graine 0 et région `(0,0)`, la graine régionale non signée vaut
|
||||
`12661893618221475390`; la base des identifiants de sorties vaut
|
||||
`15307446928`. L’identifiant en échec vaut exactement base + 1, donc la
|
||||
deuxième sortie (index 1). Ce calcul renforce l’identification par
|
||||
`sedimentDepth=0`, au lieu de la supposer depuis le nom du test.
|
||||
5. `PopulationHydrologyRuntime.apply` exclut explicitement `plan.isOutletCell`
|
||||
de la validation des deux couches de support. Sa boucle de sédiments ne fait
|
||||
aucune écriture avec profondeur zéro ; la boucle de coupe supprime les blocs
|
||||
entre `bedY+1` et `carveTop`. L’eau se propage ensuite par les ticks vanilla.
|
||||
6. `PopulationDiagnostics.checkOwnership:333–344` applique au contraire la
|
||||
densité positive à **toutes** les cellules, donc impose ici une fondation à
|
||||
Y237 et Y238. C’est précisément la contrainte absente du contrat des sorties.
|
||||
7. Le contrat publié dans `docs/generation-alpha30.md`, section Hydrologie,
|
||||
autorise une chute dans le vide et précise l’absence de fondation ou de
|
||||
colonne d’eau artificielle. `River30Smoke.java:23–24` vérifie déjà profondeur
|
||||
zéro et absence de nouvelle source. Ce smoke a passé dans le journal beta.165
|
||||
(lignes 453–454).
|
||||
|
||||
La documentation Java de `PopulationHydrology.Cell:53` décrit encore seulement
|
||||
les cellules sédimentaires : elle mérite une clarification future pour le cas
|
||||
OUTLET, mais ne prévaut pas sur le contrat alpha.30 ni son implémentation dédiée.
|
||||
|
||||
## Adaptation ciblée proposée
|
||||
|
||||
Le patch préparé dans `build/audit-berges.patch` ne change que
|
||||
`PopulationDiagnostics.checkOwnership`. Il conserve les assertions de région
|
||||
et d’appartenance de toutes les cellules. Pour une cellule **explicitement
|
||||
classée OUTLET**, il exige :
|
||||
|
||||
- aucune source d’eau, aucun sédiment, un intervalle de coupe positif ;
|
||||
- un déversement terminal déclaré avec le même identifiant.
|
||||
|
||||
Les autres cellules conservent intégralement l’assertion de densité naturelle des
|
||||
sédiments et des deux couches de support. Ne pas remplacer ce contrôle par une
|
||||
exception générale pour toutes les cellules sèches ou de profondeur zéro.
|
||||
|
||||
Le patch reste isolé et non appliqué pour intégration par l’agent principal.
|
||||
|
||||
## Reproduction et validation minimales
|
||||
|
||||
Le défaut de contrat se reproduit sans monde avec le témoin existant
|
||||
`River30Smoke` : la côte synthétique est solide à x≤4 ; la brèche atteint x=5,
|
||||
qui est du vide. Exiger un support positif sous cette cellule ferait échouer
|
||||
une ouverture intentionnelle que le smoke valide.
|
||||
|
||||
Pour confirmer le témoin réel, rejouer le GameTest
|
||||
`UnifiedWorldGameTests.retainedHydrologySurvivesNativeDecoration` après application
|
||||
ciblée, dans le **monde jetable de tests**, graine 0, population 10, diamètre 724.
|
||||
Conserver les vérifications de blocs FULL, de sédiments, de coquille et de ticks.
|
||||
La commande et l’ordonnancement seront assurés par l’agent principal pour ne pas
|
||||
saturer les 8 Go du Mac. Aucun passage de ce test n’est revendiqué ici.
|
||||
|
||||
## Points à surveiller après la première correction
|
||||
|
||||
- `verifyFinishedShell:460` impose sédiments 3–5 à ses cellules sélectionnées.
|
||||
La sélection normale porte sur lac/étang/terrasse et exclut les OUTLET ; son
|
||||
fallback prend la première feature. Si une ouverture est sélectionnée à
|
||||
l’avenir, elle nécessitera son propre contrôle de coupe, sans support imposé.
|
||||
- Le contrôle de paroi humide reconnaît `isSpillOpening`, qui ne contient que
|
||||
la position terminale. Une ouverture de trois blocs de large peut également
|
||||
créer une paroi ouverte avant ce point. Si cette assertion échoue ensuite,
|
||||
vérifier le voxel contre une cellule OUTLET déclarée et son intervalle exact
|
||||
de coupe ; ne pas désactiver le contrôle de toutes les parois.
|
||||
- Le test complet n’a pas encore atteint ses contrôles finaux avec ce patch.
|
||||
Les éventuels nouveaux échecs doivent être diagnostiqués séparément.
|
||||
- Les plafonds historiques à Y384 restent présents dans certaines inspections
|
||||
naturelles alors que la dimension atteint Y640. Ils ne causent pas l’échec
|
||||
ici à Y237/238, et ne sont pas modifiés dans ce patch ciblé.
|
||||
|
||||
## Fichiers examinés
|
||||
|
||||
- `mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/BankOutlets30.java`
|
||||
- `mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/PopulationHydrology.java`
|
||||
- `mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/PopulationHydrologyRuntime.java`
|
||||
- `mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/PopulationIslandDensity.java`
|
||||
- `mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/IslandCapacity.java`
|
||||
- `mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/PopulationDiagnostics.java`
|
||||
- `mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/River30WorldGameTests.java`
|
||||
- `mods/sanctuary/src/test/java/fr/koka/sanctuary/worldgen/River30Smoke.java`
|
||||
|
||||
## Complément : contrôle précis de la paroi ouverte
|
||||
|
||||
Un second patch non appliqué, `build/audit-berges-shell.patch`, est disponible
|
||||
**uniquement si l’exécution atteint un échec de paroi correspondant à une
|
||||
ouverture réelle**. Il ajoute un cas au contrôle des parois de
|
||||
`verifyFinishedShell`, après ses exceptions existantes pour l’eau planifiée et
|
||||
le voxel terminal du spill.
|
||||
|
||||
Le voxel doit appartenir au plan de sa propre position X/Z, à une cellule
|
||||
explicitement `OUTLET`, sans eau planifiée ni sédiments, associée par son
|
||||
identifiant à un spill terminal. Son Y doit être **strictement supérieur à
|
||||
bedY et inférieur ou égal à carveTop**. Le bloc final doit alors être de l’air
|
||||
ou de l’eau vanilla ; un bloc solide ou de la lave fait toujours échouer le test.
|
||||
Les couches sous la coupe, les voisins latéraux hors emprise, les autres types
|
||||
de cellules et les voxels au-dessus de la coupe conservent leur contrôle normal.
|
||||
|
||||
Le patch parcourt `cellsAt` plutôt que de se fier seulement à `cellAt(x,y,z)` :
|
||||
ce dernier inclut aussi les couches de support dans sa sélection, ce qui aurait
|
||||
créé une exception trop large. Les assertions sur les sédiments sélectionnés
|
||||
restent inchangées. La suite en cours doit décider si ce patch est nécessaire ;
|
||||
aucun résultat d’exécution n’est anticipé.
|
||||
|
||||
## Résultat de l’intégration
|
||||
|
||||
Le patch proposé a ensuite été intégré aux tests par l’agent principal.
|
||||
Le rejeu complet `build/test-refresh-full.log` termine avec 252/252 GameTests
|
||||
réussis et `check build` réussi. Aucun code de production ni monde existant modifié.
|
||||
Voir [la validation consolidée](actualisation-tests-beta165.md).
|
||||
@@ -0,0 +1,98 @@
|
||||
# Audit ciblé — construction groupée sur dalle et coffre
|
||||
|
||||
Audit du 24 septembre 2026, sources beta.165. Aucun changement de production,
|
||||
aucun monde lancé ou modifié, aucune nouvelle exécution de GameTest dans cet audit.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Les deux refus initiaux ont une explication commune de **fixture hors portée**, avec
|
||||
une confiance forte : le rayon de visée s’arrête à 3,5 blocs avant la surface réelle.
|
||||
Ils ne démontrent ni une fusion de dalle cassée, ni une altération du contenu du coffre.
|
||||
Les assertions suivantes restent à rejouer ; elles ne sont pas déclarées réussies.
|
||||
|
||||
Le contrat beta.009 utilisait la portée native, tandis que le contrat actuel
|
||||
[beta.081](build-reach-beta081.md) fixe le rang 1 à 3,5 blocs. Le même point de vue
|
||||
atteint encore un cube plein, mais pas les surfaces plus basses.
|
||||
|
||||
## Chaîne de refus
|
||||
|
||||
- `Building009GameTests.java:107–113` : plan 3×3 de dalles basses ; joueur au rang 1 ;
|
||||
`aim` place ses pieds en `(0,5 ; 1 ; −2,5)` par rapport à l’origine, puis vise
|
||||
`(0,5 ; 0,5 ; 0,5)`.
|
||||
- `Building009GameTests.java:165–170` : coffre central et pierre autour ; même position,
|
||||
mais `aim` conserve la cible d’un cube plein `(0,5 ; 1 ; 0,5)`.
|
||||
- `BuildReach.java:13–21` et `BuildingLimits.java:6` : portée 3,5 ; le contrôle préalable
|
||||
de proximité utilise l’AABB du **bloc entier**, pas la forme de la dalle/coffre.
|
||||
- `BuildSelection.java:40–44` : `player.pick(3.5, 1, false)` doit réellement toucher
|
||||
le bloc d’origine. Un MISS ou un autre bloc donne `null`.
|
||||
- `BuildingSession.java:26–30` : ce `null` empêche de créer la session, avant tout
|
||||
placement, fusion ou usage du coffre.
|
||||
- Le journal existant `build/beta165-check-build.log:1105–1138` confirme les refus
|
||||
à `start`, lignes 113 et 170 des tests, au tick 0.
|
||||
|
||||
## Géométrie vérifiée
|
||||
|
||||
Les constantes natives ont été inspectées par `javap -c -p` dans le JAR local exact
|
||||
Minecraft 26.3 : `Avatar` définit les yeux debout à 1,62 ; `SlabBlock` utilise
|
||||
`column(16,0,8)` pour la dalle basse ; `ChestBlock` utilise `column(14,0,14)` pour
|
||||
le coffre simple. Traces dans `build/audit-construction-avatar-bytecode.txt` et
|
||||
`build/audit-construction-native-bytecode.txt`. Aucun serveur/JVM Minecraft lancé.
|
||||
|
||||
Les yeux sont donc en **E=(0,5 ; 2,62 ; −2,5)**. Calculs Python indépendants du jeu :
|
||||
|
||||
| Surface et visée actuelle | Premier impact géométrique prévu | Distance yeux-impact |
|
||||
|---|---|---:|
|
||||
| Cube plein témoin | `(0,5 ; 1 ; 0,5)` | 3,409457 |
|
||||
| Dalle basse, visée corrigée sur son dessus | `(0,5 ; 0,5 ; 0,5)` | **3,673472** |
|
||||
| Coffre, visée restée au dessus d’un cube plein | `(0,5 ; 0,875 ; 0,731481)` | **3,672533** |
|
||||
|
||||
Le coffre occupe horizontalement `[1/16 ; 15/16]`, donc ce point est bien dans son
|
||||
dessus. Les cubes de pierre voisins ne coupent pas ce rayon avant lui : à `y=1`,
|
||||
le rayon est déjà au centre de la cellule d’origine. Les autres dalles basses ne
|
||||
coupent pas davantage le rayon avant `y=0,5`. Le précontrôle AABB entier passe,
|
||||
avec une distance minimale de 2,978993 : il ne garantit pas que le rayon atteigne
|
||||
la forme réelle.
|
||||
|
||||
L’écart d’environ 0,173 bloc dépasse largement les arrondis float de la direction
|
||||
et de la hauteur des yeux. Ces calculs expliquent le refus dans les deux fixtures ;
|
||||
une trace native reste utile pour confirmer explicitement le MISS en exécution.
|
||||
|
||||
## Correction recommandée, limitée aux tests
|
||||
|
||||
Patch proposé, **non appliqué** : `build/audit-construction.patch`.
|
||||
|
||||
Le helper dédié `aimInsetTop` rapproche les pieds à `z=−1,5`, conserve `y=1` et
|
||||
vise le centre du dessus réel (`y=0,5` pour la dalle, `14/16` pour le coffre).
|
||||
Les distances deviennent respectivement **2,914515** et **2,654247** blocs.
|
||||
Le joueur reste hors du plan 3×3 (son bord proche est vers `z=−1,2`, le plan
|
||||
commence à `z=−1`) afin de ne pas exclure une pose par sa propre collision.
|
||||
Une assertion explicite vérifie que le rayon atteint la face UP avant le démarrage.
|
||||
|
||||
Toutes les assertions métier existantes restent : neuf consommations/fusion,
|
||||
dalles de plafond, exclusion de la vache, trois diamants conservés, menu fermé,
|
||||
refus des objets à placement particulier. Aucun changement de portée de production,
|
||||
aucune suppression de test, aucun passage en créatif pour masquer le problème.
|
||||
|
||||
## Protocole ciblé restant
|
||||
|
||||
1. Relire/appliquer le patch de tests sur une branche de correction coordonnée.
|
||||
2. Rejouer la famille `building` dans un monde GameTest jetable, avec le filtre
|
||||
du dépôt `-PsanctuaryFocusedTests=building`, après libération de la mémoire du
|
||||
laboratoire. Ne pas exécuter en parallèle plusieurs serveurs sur le Mac 8 Go.
|
||||
3. Si un refus subsiste, tracer yeux, portée, `player.pick(...)`, bloc/face/distance,
|
||||
`BuildSelection.aimed`, puis nombre de cibles : ne pas augmenter arbitrairement
|
||||
la portée ou neutraliser une validation.
|
||||
4. Confirmer les assertions suivantes : une fois la première barrière levée, des
|
||||
défauts secondaires peuvent devenir visibles. Garder un scénario séparé de refus
|
||||
hors portée à la limite, sans le confondre avec fusion ou conservation d’objets.
|
||||
|
||||
Risque du patch faible et limité à la géométrie de test. Risque de modifier la
|
||||
production maintenant inutilement élevé : cela modifierait le contrat de portée
|
||||
pour contourner un scénario préparé avec l’ancien contrat.
|
||||
|
||||
## Résultat de l’intégration
|
||||
|
||||
Le patch proposé a ensuite été intégré aux tests par l’agent principal.
|
||||
Le rejeu complet `build/test-refresh-full.log` termine avec 252/252 GameTests
|
||||
réussis et `check build` réussi. Aucun code de production ni monde existant modifié.
|
||||
Voir [la validation consolidée](actualisation-tests-beta165.md).
|
||||
@@ -0,0 +1,79 @@
|
||||
# Audit dragon — beta.165
|
||||
|
||||
## Verdict
|
||||
|
||||
L’échec de `Companion019GameTests.creatureProfilesActuallyMove` est expliqué par
|
||||
une simulation incomplète du cycle natif de l’entité. Il ne démontre pas un
|
||||
blocage du dragon en jeu. Aucune correction de locomotion ne se justifie avant
|
||||
le rejeu du scénario avec son horloge d’entité effective.
|
||||
|
||||
## Chaîne causale confirmée
|
||||
|
||||
1. [Le test](../mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java)
|
||||
appelle `pet.tick()` 120 fois immédiatement, au tick GameTest 0. Le journal
|
||||
`build/beta165-check-build.log` confirme l’assertion de déplacement échouée au tick 0.
|
||||
2. Dans Minecraft **26.3**, `ServerLevel.tickNonPassenger(Entity)` appelle
|
||||
**`Entity.commonTick()` puis `Entity.tick()`**. L’incrément `tickCount++` se trouve
|
||||
dans `commonTick`, avec la mise à jour des anciennes positions et du délai
|
||||
d’invulnérabilité ; il ne se trouve pas dans `Entity.tick()` ou `baseTick()`.
|
||||
Vérification directe du bytecode du JAR natif local par `javap -c -p` :
|
||||
`build/audit-dragon-serverlevel.txt` (méthode `tickNonPassenger`),
|
||||
`build/audit-dragon-entity.txt` (méthode `commonTick`).
|
||||
3. Au premier tick, [FamiliarEntity.configureMotion](../mods/sanctuary/src/main/java/fr/koka/sanctuary/cosmetics/FamiliarEntity.java)
|
||||
choisit `FlyingPathNavigation`, `FlyingMoveControl`, désactive la gravité et
|
||||
appelle `stopRoaming()`.
|
||||
4. [FamiliarRoam.reset](../mods/sanctuary/src/main/java/fr/koka/sanctuary/familiar/FamiliarRoam.java)
|
||||
fixe `nextChoice = pet.tickCount + 20`. L’errance lit **pet.tickCount**, pas
|
||||
`level.getGameTime()`. Les 120 appels directs laissent donc `now = 0` et
|
||||
`nextChoice = 20`. La condition `if(now < nextChoice) return` interdit toute
|
||||
première destination.
|
||||
5. Le familier apparaît déjà près du joueur, à environ +2 blocs en Y grâce à
|
||||
`teleportNearOwner`. Il n’atteint pas le seuil de retour vers le propriétaire
|
||||
qui pourrait contourner cette attente. Aucun déplacement vers une destination
|
||||
n’est donc engagé dans ce scénario.
|
||||
|
||||
L’hypothèse initiale d’une simple dépendance au temps du monde était trop vague :
|
||||
la cause directe est l’absence de **commonTick**, et donc le compteur d’entité figé.
|
||||
|
||||
## Correction de test recommandée
|
||||
|
||||
Pour ce test synchrone de locomotion, remplacer les appels de simulation par
|
||||
`h.getLevel().tickNonPassenger(pet)` ; cela reproduit le préambule natif complet,
|
||||
contrairement à un simple `pet.tickCount++`. Conserver les assertions sur le profil,
|
||||
le déplacement supérieur à un bloc, l’altitude et l’absence de collision.
|
||||
Ajouter une assertion du nombre de ticks réellement avancés afin d’éviter une
|
||||
régression de la fixture. Rejouer les vérifications Ghast et Wither, masquées
|
||||
jusqu’ici par l’échec dragon.
|
||||
|
||||
Le correctif proposé est disponible dans `build/audit-dragon.patch`, sans avoir
|
||||
été appliqué aux sources par cet audit. Cette boucle reste synchrone : le temps
|
||||
du monde, les hooks serveur et le propriétaire ne progressent pas. Elle vérifie
|
||||
le cycle de locomotion de l’entité, pas une séance complète de suivi en jeu.
|
||||
|
||||
Un scénario supplémentaire de suivi en ticks réels est utile si ce premier rejeu
|
||||
échoue : propriétaire déplacé sans dépasser le seuil de téléportation, personnage
|
||||
et graine d’errance fixés, trajet effectivement parcouru contrôlé. Il doit vivre
|
||||
dans un monde de test jetable, jamais dans la sauvegarde du laboratoire.
|
||||
|
||||
## Aléa et limites
|
||||
|
||||
Le tempérament provient du UUID du lien, créé aléatoirement. Les rayons d’errance
|
||||
sont 2,5 / 4 / 5 blocs et les pauses 100–139 / 25–64 / 50–89 ticks. Ces pauses
|
||||
sont intentionnelles : ne pas imposer un mouvement à chaque tick. Pour une
|
||||
reproduction déterministe, fixer la graine du générateur de l’entité et le
|
||||
tempérament dans les données du test, ou tester explicitement les trois.
|
||||
|
||||
Les destinations nécessitent des chunks chargés, une autorisation de mouvement,
|
||||
un volume libre et un chemin accessible. Aucun de ces refus n’a été démontré
|
||||
ici : l’ancien scénario s’arrêtait **avant** leur évaluation.
|
||||
|
||||
Audit statique et lecture du bytecode terminés. À ce stade, aucun serveur de test
|
||||
supplémentaire lancé, aucune source de jeu modifiée, aucune sauvegarde touchée.
|
||||
Le passage effectif du test corrigé reste à confirmer dans l’exécution coordonnée.
|
||||
|
||||
## Résultat de l’intégration
|
||||
|
||||
Le patch proposé a ensuite été intégré aux tests par l’agent principal.
|
||||
Le rejeu complet `build/test-refresh-full.log` termine avec 252/252 GameTests
|
||||
réussis et `check build` réussi. Aucun code de production ni monde existant modifié.
|
||||
Voir [la validation consolidée](actualisation-tests-beta165.md).
|
||||
@@ -0,0 +1,100 @@
|
||||
# Audit des 23 échecs GameTest — socle beta.165
|
||||
|
||||
Audit initial du 24 septembre 2026, code `dbb9e34` (tag beta.165).
|
||||
|
||||
Suite : [actualisation des tests et validation](actualisation-tests-beta165.md).
|
||||
|
||||
## Portée et méthode
|
||||
|
||||
Lecture des 23 messages du dernier journal `build/beta165-check-build.log`, des
|
||||
méthodes de test et des chemins de production concernés. La liste a été comparée
|
||||
à beta.164 : mêmes 23 identifiants. Recompte local des JSON de collections.
|
||||
Aucun test désactivé, aucune correction de code, aucune modification du monde,
|
||||
aucun nouveau lancement Minecraft pendant cet audit. Le laboratoire reste disponible.
|
||||
|
||||
Il s’agit d’un diagnostic statique étayé par l’exécution précédente, pas d’une
|
||||
preuve que les tests passeront après adaptation. Un échec à la première assertion
|
||||
masque potentiellement des problèmes dans la suite de la même méthode.
|
||||
|
||||
## Conclusion
|
||||
|
||||
**19 échecs ont une explication étayée dans le scénario de test ou un ancien
|
||||
contrat ; 4 nécessitent encore une reproduction ciblée.** Ce ne sont donc pas
|
||||
23 bugs joueurs démontrés. Inversement, la stabilité de la liste ne prouve pas
|
||||
l’absence de bugs. Les objectifs des tests restent majoritairement pertinents.
|
||||
|
||||
| Famille | Nombre | Lecture principale |
|
||||
| --- | ---: | --- |
|
||||
| Placement simple / empreinte / tombe | 5 | Positions de test hors portée |
|
||||
| Menus d’inventaire | 3 | Constructeur incompatible avec les menus étendus |
|
||||
| Bonus de familiers | 6 | Mode travail absent des fixtures |
|
||||
| Invulnérabilité et poisson familier | 2 | Anciennes règles remplacées par le système de combat |
|
||||
| Collections, permissions, hauteur du monde | 3 | Attentes historiques à remettre à jour |
|
||||
| Construction dalle/coffre | 2 | Raycast et portée à instrumenter |
|
||||
| Déplacement du dragon | 1 | Ticks réels et navigation à reproduire |
|
||||
| Hydrologie | 1 | Contrat de support naturel à examiner |
|
||||
|
||||
## Inventaire exhaustif
|
||||
|
||||
| Test | Verdict | Preuve et limite | Suite pertinente |
|
||||
| --- | --- | --- | --- |
|
||||
| [BlockKnowledgeGameTests.placementCountsConfirmedStatesAndRejectsFailure](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/BlockKnowledgeGameTests.java:44) | Test mal positionné — forte confiance | Joueur en (5,5 ; 3 ; 5,5), support en (1 ; 1 ; 1) : la distance horizontale seule dépasse 5,65 blocs. Le mixin de placement applique la portée réelle (vanilla ou progression), avant les assertions métier. | Rapprocher le joueur et viser la face réelle ; conserver les assertions de compteurs, événements et empreinte. Ajouter/conserver un cas hors portée refusé. |
|
||||
| [BlockKnowledgeGameTests.listenersReceiveUpdatedProgressionAndStockChanges](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/BlockKnowledgeGameTests.java:220) | Test mal positionné — forte confiance | Joueur en (5,5 ; 3 ; 5,5), support en (1 ; 1 ; 1) : la distance horizontale seule dépasse 5,65 blocs. Le mixin de placement applique la portée réelle (vanilla ou progression), avant les assertions métier. | Rapprocher le joueur et viser la face réelle ; conserver les assertions de compteurs, événements et empreinte. Ajouter/conserver un cas hors portée refusé. |
|
||||
| [MaterialActivityGameTests.nativeActionsProduceDatedDeltasWhileObservationsProduceNone](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/MaterialActivityGameTests.java:39) | Test mal positionné — forte confiance | Joueur en (5,5 ; 3 ; 5,5), support en (1 ; 1 ; 1) : la distance horizontale seule dépasse 5,65 blocs. Le mixin de placement applique la portée réelle (vanilla ou progression), avant les assertions métier. | Rapprocher le joueur et viser la face réelle ; conserver les assertions de compteurs, événements et empreinte. Ajouter/conserver un cas hors portée refusé. |
|
||||
| [Demeure006GameTests.confirmedGesturesAndPersonalAtlasBoundary](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Demeure006GameTests.java:20) | Test mal positionné — forte confiance | Joueur en (5,5 ; 3 ; 5,5), support en (1 ; 1 ; 1) : la distance horizontale seule dépasse 5,65 blocs. Le mixin de placement applique la portée réelle (vanilla ou progression), avant les assertions métier. | Rapprocher le joueur et viser la face réelle ; conserver les assertions de compteurs, événements et empreinte. Ajouter/conserver un cas hors portée refusé. |
|
||||
| [GravesFood038GameTests.ordinaryPlacementAndCreativeBreakPreserveGrave](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/GravesFood038GameTests.java:120) | Test mal positionné — forte confiance | Joueur novice à 3 blocs horizontalement du clic, yeux au-dessus : distance supérieure aux 3 blocs de portée initiale. Le test échoue avant la conservation des composants et la casse créative. | Remettre le clic à portée, puis vérifier impérativement les 8 diamants après pose et casse ; le résultat actuel ne prouve aucune perte d’objets. |
|
||||
| [Inventory010GameTests.machineQuickMovesAndMounts](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Inventory010GameTests.java:77) | Initialisation de test incompatible — certaine | Boucle sur tout BuiltInRegistries.MENU et appel create(id, inventory). Des menus Sanctuary sont ExtendedMenuType et exigent des données supplémentaires ; exception avant les comparaisons. | Séparer menus vanilla et menus étendus ; fournir les données requises aux menus Sanctuary, garder les tests de conservation et de shift-clic. |
|
||||
| [Inventory010GameTests.everyNativeContainerKeepsItsIndices](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Inventory010GameTests.java:58) | Initialisation de test incompatible — certaine | Boucle sur tout BuiltInRegistries.MENU et appel create(id, inventory). Des menus Sanctuary sont ExtendedMenuType et exigent des données supplémentaires ; exception avant les comparaisons. | Séparer menus vanilla et menus étendus ; fournir les données requises aux menus Sanctuary, garder les tests de conservation et de shift-clic. |
|
||||
| [Inventory012GameTests.nativeAndExtraRowsHaveIdenticalMachinePriorities](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Inventory012GameTests.java:38) | Initialisation de test incompatible — certaine | Même boucle sur tous les menus et même constructeur sans données réseau. | Même adaptation commune ; comparer les destinations et reliquats des lignes natives et supplémentaires. |
|
||||
| [Companion019GameTests.switchingEggRemovesPassiveAndActive](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:45) | Contrat du scénario périmé — forte confiance | L’attribut de chute du chat n’est pas actif dans le mode de combat par défaut. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [Companion019GameTests.poisonDurationAndMilkUseNativeHooks](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:53) | Contrat du scénario périmé — forte confiance | La réduction de poison est attendue sans sélectionner le mode travail. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [Companion019GameTests.cropCyclesStopAfterOwnerLeaves](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:152) | Contrat du scénario périmé — forte confiance | La préparation BEE n’active pas le mode travail nécessaire au bonus de culture. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [Companion019GameTests.furnaceBonusConsumesFuelAndKeepsOneOutput](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:163) | Contrat du scénario périmé — forte confiance | La préparation BLAZE n’active pas le mode travail nécessaire au bonus de cuisson. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [Companion032GameTests.axolotlFoodDurationAndGolemKnockbackUseNativeHooks](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion032GameTests.java:77) | Contrat du scénario périmé — forte confiance | Le bonus de durée de consommation est attendu hors du mode autorisant les anciens passifs. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [GravesFood038GameTests.familiarSaturationPreviewUsesActualServerPassive](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/GravesFood038GameTests.java:130) | Contrat du scénario périmé — forte confiance | L’assertion passive(p)==species échoue avant de comparer l’aperçu et la consommation. Le helper equip règle l’attunement, mais FamiliarBattle.prepare initialise workMode=false ; CompanionService.passive renvoie alors null. | Préparer explicitement le mode travail par le chemin prévu, puis tester aussi l’absence de bonus en mode combat. Rejouer le reste du scénario : ses assertions suivantes restent non validées. |
|
||||
| [Accessory018GameTests.familiarIsHarmlessAndEscapesWalls](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Accessory018GameTests.java:82) | Ancien contrat invulnérable — forte confiance | Le test exige que genericKill ne cause aucun dégât. hurtServer délègue désormais à FamiliarBattle.hurt ; le système possède santé et K.-O. depuis beta.054. | Remplacer l’invulnérabilité universelle par les règles actuelles : dégâts autorisés, coups amicaux, K.-O., œuf préservé. Conserver les vérifications de collisions, sortie de mur et non-duplication. |
|
||||
| [Companion019GameTests.aquaticPetFlopsAndThenSwims](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:113) | Ancien contrat de déplacement — forte confiance | Le test exige une vitesse ≤ 0,06 sur terre. configureMotion réserve le mode poisson ralenti aux familiers aquatiques sans profil de combat actif ; sinon la vitesse vient du profil. | Tester séparément le profil actuel sur terre et la transition dans l’eau. Ne pas rétablir automatiquement l’ancien poisson ralenti. |
|
||||
| [Companion019GameTests.creatureProfilesActuallyMove](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Companion019GameTests.java:104) | À isoler — navigation | Le profil aérien est reconnu ; c’est le déplacement > 1 et l’altitude > propriétaire + 0,5 qui échouent. Le test appelle pet.tick 120 fois sans faire avancer normalement le monde ; l’errance actuelle dépend de destinations sûres, du pathfinding et comporte des pauses. | Reproduire dans un monde de test avec ticks réels, graine/personnalité fixées, propriétaire déplacé, cible et chemin enregistrés. Si l’immobilité persiste, corriger la navigation. |
|
||||
| [Collections035GameTests.exhaustiveCatalogueMatchesLoadedVanilla](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Collections035GameTests.java:17) | Constantes périmées — certaine | Le dernier comptage attend 1658 items / 1286 blocs / 1815 identifiants distincts. Recompte des 67 JSON : 1780 / 1374 / 1937. Les 2042 recettes uniques sont toujours présentes ; le test a déjà passé les contrôles précédents des identifiants et recettes. | Comparer la couverture aux registres et au contrat de collections ; documenter les nombres actuels, éviter qu’un total historique soit l’unique preuve d’exhaustivité. |
|
||||
| [Progression003GameTests.serverCustomNameConfiguration](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Progression003GameTests.java:120) | Permission testée au mauvais niveau — certaine | Le test exige que toute la racine /sanctuary soit inaccessible aux joueurs. intro et community link sont maintenant publics ; names et community admin portent leur propre condition opérateur. | Tester un joueur ordinaire et un opérateur sur chaque sous-commande sensible. Vérifier names reload en particulier ; ne pas rebloquer toute la racine. |
|
||||
| [UnifiedWorldGameTests.populationTerrainAndNaturalSpawnRemainPresent](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/UnifiedWorldGameTests.java:56) | Hauteur historique périmée — certaine | Attend minY=0 et hauteur=384. La dimension Sanctuary actuelle déclare hauteur et logical_height=640, minY=0 ; le test client de création attend lui aussi 640. | Actualiser le contrat de hauteur, puis rejouer les assertions suivantes sur le spawn naturel. Ne pas modifier la dimension ni les mondes existants. |
|
||||
| [Building009GameTests.slabMergingCeilingAndEntityCollisions](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Building009GameTests.java:107) | À isoler — construction groupée | Échec au premier start (ligne 113), avant fusion des dalles. Le fixture vise le centre d’une dalle basse depuis une position conçue pour un cube plein ; la portée au rang 1 est 3,5 et le point visé sur la dalle est plus éloigné. | Journaliser hit réel, bloc touché, distance, portée et raison de refus ; reproduire à portée courte puis à la limite. Conserver fusion, plafond et exclusion des entités. |
|
||||
| [Building009GameTests.containerSupportsAndUnsupportedItems](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/Building009GameTests.java:165) | À isoler — construction groupée | Échec au premier start sur coffre (ligne 170), avant toute vérification du contenu. Le coffre n’a pas la collision d’un cube plein ; le raycast peut toucher le support voisin ou dépasser la portée. | Même diagnostic que la dalle ; vérifier que le coffre ne s’ouvre pas et garde ses 3 diamants. Ne pas assimiler l’échec à une corruption d’inventaire. |
|
||||
| [UnifiedWorldGameTests.retainedHydrologySurvivesNativeDecoration](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/UnifiedWorldGameTests.java:39) | À isoler — génération, priorité haute | Échec dans PopulationDiagnostics.checkOwnership sur la densité naturelle du support à x=-141, z=-86, bedY=238, sedimentDepth=0, waterY=-1 (terrasse sèche). Ce contrôle survient avant la vérification finale des blocs décorés ; son nom ne prouve donc pas une fuite d’eau en jeu. | Comparer l’admission de cette cellule et la densité réellement utilisée, puis les blocs finaux dans un monde jetable, graine 0 / 10 joueurs / diamètre 724. Le contrat Cell annonce un support naturel : ne pas supprimer cette assertion sans explication. |
|
||||
|
||||
## Priorités proposées
|
||||
|
||||
1. **Remettre les tests en mesure de vérifier leur sujet** : placements à portée,
|
||||
construction correcte des menus, sous-commandes protégées, inventaire de
|
||||
collections et hauteur 640. Changements de tests ciblés, sans affaiblir leurs
|
||||
vérifications métier. Les tombes et transferts d’inventaire sont prioritaires
|
||||
parce qu’ils protègent les objets des joueurs.
|
||||
2. **Isoler dalle/coffre et hydrologie** : les deux premiers concernent une action
|
||||
courante ; le dernier touche la génération et exige un monde jetable, jamais
|
||||
une régénération de la sauvegarde de test actuelle. Aucun changement de
|
||||
génération n’est autorisé implicitement par cet audit.
|
||||
3. **Actualiser les scénarios familiers** selon le mode travail/combat, puis
|
||||
reproduire le vol du dragon avec une horloge de monde réelle. Garder les
|
||||
contrôles d’anti-duplication, de ressources consommées et d’annulation.
|
||||
4. Rejouer les familles corrigées, puis la suite complète. Toute nouvelle
|
||||
assertion atteinte doit être réévaluée ; ne pas annoncer « 19 réglés » avant cela.
|
||||
|
||||
La correction des fixtures et constantes paraît contenue. Les quatre cas à
|
||||
reproduire ne permettent pas encore une estimation fiable. Aucune suppression de
|
||||
fonctionnalité ni suppression de test n’est recommandée à ce stade.
|
||||
|
||||
## Points d’entrée dans le code
|
||||
|
||||
- [BlockPlacementReachMixin](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/mixin/BlockPlacementReachMixin.java:14)
|
||||
- [BuildingLimits](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/building/BuildingLimits.java:6)
|
||||
- [CompanionService](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/companion/CompanionService.java:71)
|
||||
- [FamiliarBattle](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/familiar/FamiliarBattle.java:125)
|
||||
- [FamiliarEntity](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/cosmetics/FamiliarEntity.java:262)
|
||||
- [FamiliarRoam](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/familiar/FamiliarRoam.java:16)
|
||||
- [Statuary](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/plans/Statuary.java:39)
|
||||
- [Multiblocks](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/multiblock/Multiblocks.java:36)
|
||||
- [ProgressionService](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/ProgressionService.java:178)
|
||||
- [PopulationDiagnostics](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/gametest/java/fr/koka/sanctuary/gametest/PopulationDiagnostics.java:333)
|
||||
- [PopulationHydrology](/Users/koka/Documents/sanctuary/sanctuary-community-beta154/mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/PopulationHydrology.java:56)
|
||||
|
||||
Contrat fonctionnel des familiers : [beta.054](familiar-combat-beta054.md).
|
||||
+135
@@ -1,5 +1,140 @@
|
||||
# Backlog Sanctuary
|
||||
|
||||
## WG-ECO-177 — écologie liée au relief
|
||||
|
||||
Branche `codex/relief-ecology-beta177`. Nouveau solo à 32 chunks demandé
|
||||
après la visite beta.176 : plateau forestier/fleuri, vallées automnales,
|
||||
hauteurs à cerisiers, cavités humides arborées, retrait de la neige et de
|
||||
la mangrove, pierres mêlant strates et gisements. Profil de labo distinct ;
|
||||
[contrat et vérifications](relief-ecology-beta177.md).
|
||||
|
||||
## WG-ECO-176 — strates, biomes et mares locales
|
||||
|
||||
Branche `codex/island-ecology-beta176`. Le créateur autorise la mise à
|
||||
l’essai des [retours de visite](worldgen-retours-beta175.md). Nouveau monde
|
||||
de labo, relief conservé ; géologie continue, larges régions automne et
|
||||
cerisiers, récifs humides et mares bornées. Aucun mineshaft ni village
|
||||
natif. Prototype jouable, mesures natives validées sur trois graines.
|
||||
[Contrat, résultats et réserve sur la suite générale](island-ecology-beta176.md).
|
||||
|
||||
## WG-SKY-175 — récifs rares et ressources de l’île
|
||||
|
||||
Suite du labo rapide : l’île principale est conservée, les anciennes masses
|
||||
flottantes sont remplacées et la bande 512–640 reste libre pour l’ISS. Le
|
||||
créateur demande des relevés scientifiques, puis précise la répartition
|
||||
des minerais et la présence de deepslate. Nouveau monde de labo uniquement.
|
||||
[Contrat et résultats](sky-fragments-beta175.md).
|
||||
|
||||
## WG-LAB-174 — worldgen rapide en laboratoire
|
||||
|
||||
**Priorité demandée pendant la séance du 29 septembre.** Profil de relief
|
||||
réservé aux nouveaux mondes de labo, sans plans d'hydrologie ni structures,
|
||||
avec une référence complète séparée et des mesures reproductibles. La
|
||||
génération normale reste inchangée. [Contrat et mesures](worldgen-lab-beta174.md).
|
||||
Ce lot précède les maquettes de halte du Storyquest : l'outil d'itération
|
||||
permet de reprendre d'abord les formes et la géographie de Sanctuary Island.
|
||||
|
||||
**Livré localement en beta.174 :** profil relief, lanceur protégé, comparaison
|
||||
à froid/réouverture, graines 0/42/173 et client Vulkan vérifiés. `check build`
|
||||
et les deux assemblages réussis ; génération normale conservée. L'optimisation
|
||||
des algorithmes complets reste le travail suivant.
|
||||
|
||||
## SQ-173 — Refonte Storyquest et géographies indépendantes
|
||||
|
||||
**Direction actualisée le 29 septembre 2026.** Branche `codex/storyquest-beta173`,
|
||||
issue de beta.172 (`55c745a`), avec prototype local beta.173 livré depuis.
|
||||
Le [fil rouge courant](storyquest-fil-rouge.md) fait autorité pour les priorités ;
|
||||
le [cadrage général](storyquest-beta173.md) conserve les autres intentions.
|
||||
Le créateur abandonne le palais souterrain, ses accès et ses raccordements.
|
||||
La refonte concerne Sanctuary Island, l'île de départ.
|
||||
|
||||
Ordre de travail proposé, à ajuster après chaque visite :
|
||||
|
||||
1. Un parcours extérieur arrivée → halte → ancre, comparé dans deux ambiances.
|
||||
2. Une première conséquence territoriale réelle, sous contrat de nouveaux mondes.
|
||||
3. Une préparation utile au parcours : familier, produit ou plan constructible.
|
||||
4. Un premier accès vertical, puis la déclinaison des lieux qui fonctionnent.
|
||||
|
||||
Chaque étape croise géographie, récit, retour d'information et ambiance. Les
|
||||
critères d'essai sont dans le fil rouge ; cet ordre n'est pas un verrouillage
|
||||
de progression joueur. Pas de nouveau code ni de carte de terrain livré par
|
||||
cette révision documentaire. Les autres sujets — menus, Friends, PNJ, cuisine,
|
||||
économie, ISS, créatures et communauté — restent dans la réserve de conception.
|
||||
|
||||
## PALAIS-173 — prototype livré ; architecture abandonnée le 29 septembre
|
||||
|
||||
Première implémentation dans un laboratoire neuf : un palais par seed, huit
|
||||
ancres extérieures, huit matériaux distincts et huit couleurs indépendantes
|
||||
sur le bloc originel. [Contrat, essai et validation](palais-prototype-beta173.md).
|
||||
Le code et le laboratoire sont conservés comme preuve technique. **Le placement
|
||||
du palais sous l'île n'est plus prévu.** Les mécaniques d'ancres et du bloc
|
||||
originel peuvent alimenter le nouveau parcours ; leur intégration indépendante
|
||||
et l'ouverture effective d'une expansion restent à réaliser.
|
||||
|
||||
## HORDE-170 — Objet-carte natif et langage de particules
|
||||
|
||||
[Contrat de la carte native](horde-map-native-beta170.md). L'objet créatif
|
||||
vierge découvre une identité stable et différente à sa première prise en main.
|
||||
Toutes les étapes des invasions par carte sont signalées visuellement, sans chat.
|
||||
La distribution en survie depuis les campements reste un ticket futur : aucune
|
||||
génération existante n'est modifiée dans cette livraison.
|
||||
|
||||
## HORDE-169 — Cartes inconnues et invasion accélérée
|
||||
|
||||
[Contrat de l'invasion](horde-invasion-beta169.md). La prise en main révèle la
|
||||
difficulté et le contenu. Les nouvelles cartes remplacent les vagues par un flux
|
||||
de 12, 18 ou 27 monstres de plus en plus rapide, avec jusqu'à dix espèces et des
|
||||
butins généreux correspondants. Les cartes beta.168 restent lisibles et jouables.
|
||||
|
||||
## FAMILIERS-COMBAT — Refonte à définir après essai de horde
|
||||
|
||||
Retour du créateur : les familiers ne lui semblent pas utiles au combat. Examiner
|
||||
leurs comportements réels et définir une contribution perceptible avant de modifier
|
||||
leurs statistiques. Tester avec joueurs sur une horde. Intention consignée, non implémentée.
|
||||
|
||||
## HORDE-168 — Cartes consommables et participation spontanée
|
||||
|
||||
Branche `codex/cartes-horde-beta168`. [Contrat du labo](horde-cartes-beta168.md).
|
||||
Trois cartes natives illustrées, invocation immédiate sans socle, vagues ouvertes
|
||||
et butins sur les monstres ramassables librement. L’ancien cercle est conservé
|
||||
pour les futurs donjons. Suite générale 254/254, puis validation ciblée serveur
|
||||
et client Vulkan des derniers ajustements réussies.
|
||||
|
||||
## HORDE-167 — Premier essai de carte d'épreuve en laboratoire
|
||||
|
||||
Branche `codex/communaute-economie-beta167`. [Contrat et vérifications](horde-lab-beta167.md).
|
||||
Prototype local vérifié sur son parcours serveur et client Vulkan : arène ronde, bloc central, carte réutilisable,
|
||||
inscriptions et trois vagues coopératives. Aucun gain d'XP ou de butin de récompense.
|
||||
La simplification du tableau, la BDD et les autres cartes restent à réaliser.
|
||||
La suite complète reste à 252/253 : un échec de déplacement des familiers,
|
||||
reproduit au rejeu ciblé, est signalé dans le contrat de livraison.
|
||||
|
||||
## Direction de travail — communauté, économie et aventures
|
||||
|
||||
[Cadrage du 25 septembre 2026](ecosysteme-communaute-economie.md) : décisions
|
||||
du créateur, état beta.166, propositions et questions ouvertes. Conception
|
||||
uniquement ; branche `codex/communaute-economie-beta167`, basée sur le dernier
|
||||
`main` beta.166. Le prototype beta.167 a été intégré sur `main` localement.
|
||||
|
||||
Dernière orientation de conception : cartes de découverte, cartes d'épreuve,
|
||||
clés/reliques, avec une piste de cartes collectionnables générées. Imaginer une
|
||||
arène ronde de laboratoire et son nouveau bloc central pour une carte de horde
|
||||
de zombies par vagues. Le [prototype beta.167](horde-lab-beta167.md) fait maintenant
|
||||
l'objet d'une réalisation séparée ; les autres familles restent en conception.
|
||||
|
||||
Ordre proposé : tableau à message général → statistiques existantes en BDD et
|
||||
premier rapport quotidien → première bounty physique jouable → navets et machine
|
||||
du dimanche avec suivi économique → huit structures et ancres d'expédition.
|
||||
Le contrat de ballast doit être livré avant les Backrooms ; site et Discord
|
||||
prolongent progressivement ces parcours. Aucun de ces nouveaux lots n'est livré.
|
||||
|
||||
## COMM-154 — Communauté dans le menu pause et sur le site
|
||||
|
||||
Branches `codex/community-beta154` (mod) et `codex/community-contract` (site).
|
||||
Résultat : Gazette, tableau et intendance avec lecture, publication, réponses,
|
||||
droits serveur et comptes liés ; fichier autonome ou MariaDB partagée.
|
||||
[Contrat](community-contract-v1.md), [livraison locale](community-beta154.md).
|
||||
|
||||
## STAT-03 — Atelier d’argile et hotbar — beta.112
|
||||
|
||||
Branche `codex/clay-workshop-hotbar-beta112`. [Contrat et vérifications](clay-workshop-hotbar-beta112.md).
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# COMM-154 — Communauté et conversations, beta.154
|
||||
|
||||
Livraison locale préparée sur `codex/community-beta154`, depuis beta.151
|
||||
`b97ec6c9e820bed7693e75f200a02b16af01cf89`. Le site est préparé sur
|
||||
`codex/community-contract`, depuis `4af93d1e42ba6084ecd654967ad6d8f317a5d3ef`.
|
||||
Contrat identique dans les deux dépôts : [communauté v1](community-contract-v1.md).
|
||||
|
||||
## Résultat
|
||||
|
||||
Gazette à gauche et tableau à droite du menu pause, intendance au-dessus ;
|
||||
accès compacts lorsque la largeur GUI ne permet pas trois colonnes.
|
||||
Lire, publier et répondre, modifier son texte, masquer et clôturer une annonce.
|
||||
Modération autorisée côté serveur et comptes Web liés par code confirmé en jeu.
|
||||
|
||||
Les discussions montrent les visages Minecraft, le pseudo, la date et chaque
|
||||
message dans un bloc de conversation. Les actions secondaires sont regroupées
|
||||
sous « ⋯ » ; la réponse se saisit directement sous le fil. Même présentation
|
||||
sur le site, avec rendu des visages par UUID via son prestataire existant.
|
||||
|
||||
Fichier JSON atomique par défaut ; MariaDB optionnelle partagée avec Laravel.
|
||||
Le serveur exécute la persistance hors du thread de jeu. Brouillons conservés
|
||||
en cas de refus ; aucun repli silencieux vers un fichier après une panne SQL.
|
||||
Aucun changement des shaders, de leurs réglages, de la génération ou des
|
||||
sauvegardes existantes. Le delta part exactement du shader beta.151.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Environnement : macOS Apple M1, Java Temurin 25, Minecraft 26.3,
|
||||
Fabric Loader 0.19.5, Fabric API 0.160.5+26.3, Vulkan/MoltenVK 1.4.2.
|
||||
Site : PHP 8.5.10, Laravel 13.32.0, PHPUnit 12.5.35. MariaDB locale 12.3.3,
|
||||
Connector/J 3.5.10 imbriqué dans le mod.
|
||||
|
||||
- Scénario de stockage commun réussi sur fichier et MariaDB : persistance,
|
||||
redémarrage, exact retry, conflits de révision, auteurs, opérateurs,
|
||||
fermeture/réouverture, masquage, pagination, texte Unicode et isolation.
|
||||
Codes expirés, consommés et tentative de réattribution d’identité refusés.
|
||||
`build/community-client-database-final.log`, marqueurs FILE_PASS et SQL_PASS.
|
||||
- Aller-retour réel Java → HTTP Laravel → Java réussi : article, création d’un
|
||||
code Web, liaison côté Java, modification Web et réponse relues par Java.
|
||||
`build/community-interop-{seed,claim,verify}.log` ; deux phases PHPUnit,
|
||||
3 puis 6 assertions. Migration Laravel sur les tables déjà créées par le SQL
|
||||
du mod réussie ; création depuis Laravel également testée par les tests HTTP.
|
||||
- Tests client Vulkan réussis en FR/EN, GUI 2/3 (panneaux et mode compact),
|
||||
publication, réponse intégrée, clôture, actions contextuelles, visages,
|
||||
brouillon refusé et réponse tardive après fermeture.
|
||||
Fichier : `build/community-conversation-client.log`.
|
||||
MariaDB : `build/community-conversation-database.log`, mode explicitement
|
||||
vérifié par le client. Captures dans `mods/sanctuary/build/run/clientGameTest/screenshots/`.
|
||||
- Panne SQL testée avec une adresse locale sans service : erreur visible,
|
||||
brouillon conservé, aucune création de fichier de repli.
|
||||
`build/community-client-outage.log`, marqueur COMMUNITY154_OUTAGE_PASS.
|
||||
- Web : **12 tests / 60 assertions** communautaires réussis, comprenant
|
||||
validation, identité non falsifiable, restrictions du staff, auteur,
|
||||
XSS/texte échappé, formulaires, pagination, compte lié et rejet CSRF réel.
|
||||
Pint réussi. Rendu des pages et visages vérifié dans le navigateur local,
|
||||
navigation accueil → discussion et FR/EN. Aucune erreur console applicative ;
|
||||
avertissement THREE.Clock hérité du panorama.
|
||||
- Suite Web complète : **55 réussites, 1 échec, 1 test optionnel ignoré** sur
|
||||
57. L’échec existant de PlayerModerationTest attend « Nommer admin », alors
|
||||
que ViewUser affiche déjà « Nommer staff » dans le commit de départ.
|
||||
- `./gradlew check build --continue` exécuté : **229/252 GameTests réussis**,
|
||||
**23 échecs strictement identiques à beta.151** (comparaison des identifiants).
|
||||
`build/community-check-build.log`, `build/community-server-failures.json`.
|
||||
Ce passage a aussi rencontré un `ClassNotFoundException` temporaire dans
|
||||
seasonal105Smoke pendant des compilations concurrentes de vérification.
|
||||
Le contrôle final est relancé seul, avec la tâche GameTests exclue (les 252 cas ont été exécutés lors du premier passage).
|
||||
|
||||
Le contrôle complet reste rouge : cette livraison ne prétend pas réparer les
|
||||
23 GameTests historiques ni l’ancienne assertion de libellé Web. Les parcours
|
||||
communautaires sont validés indépendamment.
|
||||
|
||||
## Rejouer les tests ciblés
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary:community154Smoke
|
||||
./gradlew :sanctuary:runClientGameTest \
|
||||
-PsanctuaryClientTests=true -PsanctuaryQuickTests=true \
|
||||
-PsanctuaryCommunity154ClientTests=true -PsanctuaryClientGraphicsBackend=vulkan
|
||||
```
|
||||
|
||||
Pour le même scénario en base, préparer une MariaDB de développement vide
|
||||
avec `src/main/resources/community/schema-v1.sql`, puis définir
|
||||
`SANCTUARY_COMMUNITY_TEST_JDBC` (URL JDBC),
|
||||
`SANCTUARY_COMMUNITY_CLIENT_DATABASE=true` et
|
||||
`SANCTUARY_COMMUNITY_DB_PASSWORD`. Ces fixtures de développement utilisent
|
||||
`root` sans mot de passe, uniquement dans la MariaDB locale isolée de test.
|
||||
Ne jamais les lancer sur une base personnelle ou de production.
|
||||
|
||||
L’aller-retour utilise une autre base isolée, également migrée par Laravel :
|
||||
`community154Interop -PcommunityInteropPhase=seed`, puis CommunityInteropTest
|
||||
avec `SANCTUARY_COMMUNITY_INTEROP_PHASE=link`, Java `claim`, PHP `write`, Java
|
||||
`verify`. Les deux programmes reçoivent le même chemin absolu
|
||||
`SANCTUARY_COMMUNITY_INTEROP_FILE` vers un JSON dans un dossier ignoré.
|
||||
Le test PHP reçoit les paramètres DB_CONNECTION/DB_HOST/DB_PORT/DB_DATABASE
|
||||
pointant exclusivement vers cette base. Sans cette variable, il est ignoré.
|
||||
|
||||
## Limites de livraison
|
||||
|
||||
Code et artefacts locaux ; aucun déploiement Web, serveur personnel, canal
|
||||
packwiz ou instance Prism. Validation Windows et connexion Discord réelle sur
|
||||
le domaine de production à faire lors du déploiement. Le compte Web utilisé
|
||||
dans les essais est fictif, la liaison passe cependant par le vrai stockage.
|
||||
Pas de pièces jointes, de notification poussée ou d’import fichier → base.
|
||||
Les anciens JAR encore présents sont conservés.
|
||||
|
||||
## Artefacts vérifiés
|
||||
|
||||
Export packwiz réussi. ZIP, versions Minecraft/Fabric, JAR unique, pilote JDBC
|
||||
imbriqué, licence, absence des tests et égalité octet pour octet des 29 fichiers
|
||||
shader avec beta.151 vérifiés. Reçu : `build/community-artifacts.json`.
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.154.jar` — SHA-256 `07e7419cb3e814d59a6b6f130a1b05b4f65c2407a0ffbd00b194ba97614923ad`.
|
||||
- `build/Sanctuary-beta.154.mrpack` — SHA-256 `fbc007fe59777b0c52b30b6e461b09fe0636fb0b86d254b9e98952856b7f6c8a`.
|
||||
|
||||
Site : commit local `32d804a` sur `codex/community-contract`.
|
||||
@@ -0,0 +1,98 @@
|
||||
# beta.158 — cartes communautaires et demandes suivies
|
||||
|
||||
## Contrat v3 et migration
|
||||
|
||||
Les règles de génération et les inventaires Minecraft ne changent pas. Les coffres
|
||||
sont des sélections descriptives : aucun objet n'est prélevé, réservé, livré ou
|
||||
créé. Les échanges et la répartition des récompenses sont organisés entre joueurs.
|
||||
Il n'y a ni gagnant automatique ni validation automatique d'une livraison.
|
||||
|
||||
Une annonce peut porter `task` : position facultative (dimension, x/y/z), liste
|
||||
`materials` de 27 identifiants d'objets et quantités (1 à 999999), participation
|
||||
`shared` ou `single`, récompense `none`, `items` (27 sélections) ou `custom` (240
|
||||
caractères). Les catégories info/work/need/event gardent leurs identifiants.
|
||||
Le serveur Minecraft valide aussi les objets et dimensions contre son registre.
|
||||
Les données du site sont descriptives et restent lisibles si un objet manque côté
|
||||
client ; les identifiants inconnus ne créent jamais d'objet.
|
||||
|
||||
Les abonnements sont authentifiés, idempotents, au plus 16 par joueur et 64 par
|
||||
annonce. Une annonce single accepte un seul abonné. L'auteur ne s'abonne pas à sa
|
||||
propre annonce. Fermeture et masquage retirent le suivi actif ; réouvrir autorise
|
||||
à nouveau l'abonnement. Les abonnements existants ne sont pas supprimés par la
|
||||
fermeture : un joueur peut se désabonner. Changer shared vers single est refusé
|
||||
si plusieurs abonnés sont présents. La liste côté HUD est issue du serveur ; les
|
||||
notifications affichent les trois suivis ouverts les plus récents, avec le lieu
|
||||
et un rappel des matériaux. La fermeture est annoncée, sans attribuer de gain.
|
||||
|
||||
Fichier : ajout du champ task et de la collection followers ; lecture v1/v2 sans
|
||||
écriture. À la première écriture, backup exact `.v1.bak` ou `.v2.bak` sans
|
||||
écrasement puis écriture atomique en v3. Le plafond 64 Mio reste appliqué. Aucun
|
||||
monde personnel n'est migré pendant le développement. Retour arrière : serveur
|
||||
arrêté, restauration du backup et de la version logicielle correspondante.
|
||||
|
||||
MariaDB : arrêter les écritures du mod et du site, sauvegarder, appliquer la
|
||||
migration v2→v3 (SQL livré ou Laravel), puis mettre à jour les deux applications.
|
||||
Colonne task nullable, tables followers et subscribers (verrous par joueur).
|
||||
Aucune migration automatique par le mod ; aucune suppression des anciens posts.
|
||||
Les anciens articles et annonces restent lisibles. Rollback uniquement par
|
||||
restauration explicite ; pas de suppression automatique des nouvelles données.
|
||||
|
||||
La Gazette et le tableau utilisent une grille à deux colonnes. Les aperçus de
|
||||
photo sont bornés à 6 Kio binaires par carte ; les listes ne diffusent pas les
|
||||
photos complètes. L'intendance reste affichée dans l'en-tête, sans accès public
|
||||
à son historique ni à son éditeur ; sa gestion est réservée au panneau admin.
|
||||
Le panneau Web est complété par `/sanctuary community admin` côté jeu, réservé
|
||||
aux opérateurs par le serveur, notamment pour les installations en mode fichier.
|
||||
Les métadonnées de l’article (visage, nom, date) sont sur la rangée du bouton
|
||||
« … », juste sous l’encadré photo/texte. La galerie est plafonnée à 420 unités
|
||||
de largeur et 180 de hauteur ; l’aperçu du formulaire est limité à 160 × 72.
|
||||
Le bouton de liaison du compte Web disparaît des écrans communautaires en jeu.
|
||||
Les sondages restent une piste, sans fonctionnalité livrée dans cette version.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Vérifications ciblées du 23 septembre 2026 :
|
||||
|
||||
- Stockage fichier et MariaDB : métadonnées, inscription exclusive concurrente,
|
||||
idempotence, limite de 16 suivis, fermeture/réouverture, désabonnement,
|
||||
refus shared→single occupé, masquage et redémarrage. Migration v1/v2 avec
|
||||
conservation exacte des backups.
|
||||
- Client natif Vulkan : français/anglais, échelles GUI 2/3/4, grilles de deux
|
||||
colonnes, défilement et chargement des cartes suivantes sans retour en haut,
|
||||
galerie compacte, photo obligatoire, publication, coffre de sélection,
|
||||
position courante, récompense personnalisée, abonnement et HUD.
|
||||
- Vérification du déplacement visage/nom/date sous l’article, à côté de « … ».
|
||||
- Accès opérateur au panneau d’intendance en jeu : refus au joueur ordinaire,
|
||||
ouverture et publication après activation des permissions natives.
|
||||
- Régression du menu pause : colonnes, boutons, carte, défilements indépendants,
|
||||
longs articles/annonces et réponse, sous Vulkan en FR/EN et GUI 2/3/4.
|
||||
- Site : 23 tests / 131 assertions, dont droits, migrations v3 sans perte ni
|
||||
rétrogradation, validation des matériaux et inscription exclusive.
|
||||
- Interopérabilité MariaDB réelle Java→PHP→Java : photo, édition, réponse,
|
||||
identité vérifiée, demande et abonnement Web relus par le mod (18 assertions PHP).
|
||||
- Ressources de shaders : 29 fichiers identiques au JAR beta.157, qui conservait
|
||||
le socle beta.151. Les anciens JAR et MRpack sont conservés.
|
||||
|
||||
Suite générale : `./gradlew check build assemblePack` exécuté ; 252 GameTests,
|
||||
229 réussites et les mêmes 23 échecs identifiés dans la beta.157, aucun nouvel ID.
|
||||
Comparaison conservée dans `build/cards158-server-failures.json`. Les autres
|
||||
contrôles, le build et l’assemblage passent avec cette suite déjà vérifiée exclue :
|
||||
`./gradlew check build assemblePack :sanctuary:runClientGameTest -x :sanctuary:runGameTest`
|
||||
avec les propriétés client Cards158/Vulkan et la base MariaDB de développement.
|
||||
Le dernier parcours confirme aussi le refus des quantités invalides et la
|
||||
conservation des 17 bûches et 3 diamants réels après publication descriptive.
|
||||
|
||||
Export packwiz réussi ; version interne beta.158 et JAR embarqué vérifiés.
|
||||
Captures finales : `build/cards158-database-screens/` ; captures fichier :
|
||||
`build/cards158-file-screens/`. Les grilles, la galerie et le pied d’article
|
||||
ont été inspectés visuellement. Journaux : `build/beta158-*.log`.
|
||||
|
||||
Site local : commit `0dcab21` sur
|
||||
`codex/community-quests`. Aucun déploiement dans une installation personnelle,
|
||||
aucune publication du canal packwiz ni modification d’un monde existant.
|
||||
|
||||
|
||||
## Artefacts locaux
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.158.jar` — SHA-256 `0590320dcece7f94543ab17e99fb23ac37cc3e2578499aa6e80ad801212675e8`.
|
||||
- `build/Sanctuary-beta.158.mrpack` — SHA-256 `9c1e556ddce3094febf92631c8a1c8cc0ec56971b332cd9c2d7448f2f9931b89`.
|
||||
@@ -0,0 +1,240 @@
|
||||
# Communauté Sanctuary — contrat v1
|
||||
|
||||
> Depuis beta.158, le schéma actif est la v3 : photos et demandes suivies.
|
||||
> Appliquer les migrations avant de connecter les deux applications.
|
||||
|
||||
Ticket beta.154, branche `codex/community-beta154`, socle beta.151 `b97ec6c`.
|
||||
Ce contrat décrit le comportement cible et les échanges implémentés par ce ticket.
|
||||
La note de livraison indique séparément les vérifications effectivement réussies.
|
||||
Les shaders de beta.151 sont conservés sans modification.
|
||||
|
||||
## Périmètre
|
||||
|
||||
Gazette (`article`), tableau (`notice`), intendance (`bulletin`) et réponses
|
||||
(`reply`). Contenus texte brut : titre 120 caractères Unicode, corps 4 000,
|
||||
réponse 1 000. Aucune interprétation HTML, Markdown ou commande Minecraft.
|
||||
Catégories stables : `info`, `work`, `need`, `event`. Photos et pièces jointes
|
||||
sont hors de cette première livraison.
|
||||
|
||||
La pause présente la Gazette à gauche, les actions natives au centre, le tableau
|
||||
à droite et l'intendance au-dessus. À faible largeur GUI, accès compacts vers
|
||||
les mêmes écrans. Publication et réponses disponibles dès cette version.
|
||||
|
||||
## Autorité et identité
|
||||
|
||||
Le client passe exclusivement par le serveur Minecraft. L'auteur vient de la
|
||||
session serveur, jamais d'un UUID fourni par le client. Chaque installation
|
||||
possède un `serverId` UUID stable, également configuré sur le site. Chaque
|
||||
requête SQL inclut ce serveur. Les sauvegardes du monde restent indépendantes.
|
||||
|
||||
Tous les joueurs connectés peuvent publier et répondre. L'auteur peut modifier
|
||||
ou masquer son contenu et clôturer/réouvrir son annonce. Un opérateur Minecraft
|
||||
ou membre du staff Web disposant de `users.moderate` peut masquer des contenus, épingler les publications et
|
||||
publier l'intendance ; il ne réécrit pas le texte d'un autre auteur.
|
||||
Le site exige un compte connecté lié à l'UUID Minecraft : code aléatoire à usage
|
||||
unique créé sur le site, valable 10 minutes, confirmé en jeu. Seul le hash SHA-256
|
||||
est conservé. La confirmation nécessite un serveur authentifié (`online-mode`).
|
||||
Une identité déjà liée ne peut être réattribuée automatiquement.
|
||||
|
||||
## Stockage
|
||||
|
||||
`config/sanctuary-community.json` sélectionne `file` (défaut) ou `database`.
|
||||
Le fichier du monde `data/sanctuary-community.json` est remplacé atomiquement
|
||||
après synchronisation disque. Une corruption ou modification externe est une
|
||||
erreur : aucune réinitialisation silencieuse.
|
||||
|
||||
En mode base, MariaDB utilise les tables `sanctuary_community_*`, utf8mb4,
|
||||
InnoDB. Les nouvelles tables sont additives. Le schéma SQL v1 livré peut être
|
||||
installé par l'administrateur ; Laravel possède la migration équivalente.
|
||||
Le mod vérifie la version et ne lance jamais de migration au démarrage.
|
||||
Une seule application du schéma suffit avant de démarrer les deux applications.
|
||||
Les évolutions futures auront une migration explicite commune.
|
||||
|
||||
Aucun basculement automatique base → fichier lors d'une panne. Les lectures en
|
||||
cache restent signalées comme anciennes ; une écriture n'est confirmée qu'après
|
||||
commit. Les brouillons restent côté client durant la session. Les requêtes SQL
|
||||
s'exécutent hors du thread de jeu, avec délais et file d'attente bornés.
|
||||
Le passage fichier ↔ base nécessite un export/import explicite ; changer le mode
|
||||
ne migre ni ne fusionne les données. La v1 ne fournit pas cet importateur.
|
||||
|
||||
## Enregistrements et concurrence
|
||||
|
||||
Les entrées portent : serveur, UUID, parent éventuel, type, catégorie, UUID et nom
|
||||
d'auteur, titre, corps, état (`open`, `closed`, `hidden`), épingle, révision,
|
||||
date de création et date de modification (millisecondes Unix UTC).
|
||||
Une réponse a un parent article/annonce du même serveur ; pas de réponses imbriquées.
|
||||
Masquer un parent masque également sa discussion dans toutes les lectures.
|
||||
|
||||
Chaque création utilise un UUID d'opération stable. La répétition exacte rend le
|
||||
même résultat ; une réutilisation avec d'autres données est un conflit. Chaque
|
||||
modification exige la révision courante. La transaction verrouille le parent
|
||||
avant de répondre, empêchant la course avec une clôture ou un masquage.
|
||||
Les listes sont paginées (10 entrées), épingles puis date/id décroissants ; les
|
||||
réponses sont ordonnées par date/id croissants. L'intendance montre le bulletin
|
||||
visible le plus récent. Les textes et le nombre de résultats sont bornés.
|
||||
|
||||
## Vérification attendue
|
||||
|
||||
Même scénario sur fichier et vraie MariaDB : publication, réponses, redémarrage,
|
||||
idempotence, conflits, droits, masquage, fermeture et isolation de deux serveurs.
|
||||
Aller-retour Java → PHP → Java sur les mêmes tables. Essais client Vulkan FR/EN,
|
||||
GUI 2/3, mode compact, publication/refus/brouillon et fermeture avant réponse.
|
||||
Tests HTTP du site : identité, CSRF (middleware), validation, permissions,
|
||||
échappement HTML, erreurs, pagination. `./gradlew check build` et `assemblePack`
|
||||
sont consignés avec leurs éventuels échecs hérités.
|
||||
|
||||
## Mise en service
|
||||
|
||||
### Serveur autonome (fichier, défaut)
|
||||
|
||||
Installer le JAR ou le pack beta.154. La première ouverture d’un panneau crée
|
||||
`config/sanctuary-community.json` et son `serverId`. La première publication
|
||||
crée uniquement le fichier communautaire du monde. Aucun service externe requis.
|
||||
Les joueurs utilisent Échap → Gazette/Tableau → Publier, puis Répondre dans
|
||||
une discussion. Le mode fichier n’est pas partagé avec le site Web.
|
||||
|
||||
Conserver le `serverId` et sauvegarder le fichier communautaire avec son monde.
|
||||
Ne pas éditer ce fichier pendant que le serveur tourne. La limite de la v1 est
|
||||
64 Mio par fichier communautaire ; une écriture qui la dépasse est refusée.
|
||||
|
||||
### Même MariaDB pour Minecraft et Laravel
|
||||
|
||||
1. Sauvegarder la base existante. Déployer le code Web puis appliquer sa migration
|
||||
`php artisan migrate --force`. Elle ajoute quatre tables sans modifier les
|
||||
données de jeu. Un schéma déjà installé par `schema-v1.sql` est reconnu.
|
||||
À l’inverse, ne pas rejouer le SQL brut sur des tables déjà créées par Laravel.
|
||||
2. Dans le `.env` du site, garder ses paramètres MariaDB et définir
|
||||
`SANCTUARY_COMMUNITY_SERVER_ID` avec le même UUID que le mod. Recharger
|
||||
la configuration Laravel (`php artisan config:cache` en production).
|
||||
3. Arrêter Minecraft et éditer sa configuration :
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "database",
|
||||
"serverId": "11111111-1111-4111-8111-111111111111",
|
||||
"jdbcUrl": "jdbc:mariadb://db.example.net:3306/sanctuary?sslMode=verify-full",
|
||||
"user": "sanctuary_game",
|
||||
"passwordEnv": "SANCTUARY_COMMUNITY_DB_PASSWORD"
|
||||
}
|
||||
```
|
||||
|
||||
L’UUID ci-dessus est un exemple : reprendre celui de votre installation.
|
||||
Définir `SANCTUARY_COMMUNITY_DB_PASSWORD` dans l’environnement du service Minecraft,
|
||||
puis le redémarrer. Utiliser un certificat TLS valide pour l’hôte de la base.
|
||||
Le compte de jeu a besoin de SELECT sur `sanctuary_community_schema`, SELECT,
|
||||
INSERT et UPDATE sur `sanctuary_community_entries`, SELECT et INSERT sur
|
||||
`sanctuary_community_identities`, SELECT et DELETE sur `sanctuary_community_links`.
|
||||
Il n’a besoin ni des tables de comptes Web, ni de droits de création de tables.
|
||||
Le compte Laravel conserve ses permissions habituelles et celles des migrations.
|
||||
|
||||
4. Sur le site : se connecter avec Discord, ouvrir Gazette, demander un code de
|
||||
liaison. En jeu : Gazette → Lier mon compte → saisir le code. La liaison
|
||||
exige `online-mode=true`. Recharger le site après confirmation ; les boutons
|
||||
Publier et Répondre deviennent accessibles.
|
||||
5. Modération : opérateur Minecraft ou staff Web autorisé à `users.moderate`,
|
||||
avec compte lié. Les fonctions de lecture seule du staff ne donnent aucun
|
||||
droit de masquage, épinglage ou publication officielle.
|
||||
|
||||
L’accueil du site conserve sa version de téléchargement beta.151 tant que le
|
||||
pack public n’est pas mis à jour. Ce ticket ne publie pas le site, le canal
|
||||
packwiz ou une installation Prism.
|
||||
|
||||
### Synchronisation et limites de la v1
|
||||
|
||||
Le menu pause recharge ses aperçus toutes les 5 secondes. Les pages de lecture
|
||||
ont un bouton Actualiser. Le site charge les publications à chaque navigation.
|
||||
Les brouillons Minecraft restent en mémoire jusqu’à déconnexion ; le site garde
|
||||
ses brouillons dans le stockage local du navigateur, par compte et serveur.
|
||||
Aucune notification poussée ni import automatique d’archives ou de fichier vers
|
||||
MariaDB. Les anciennes publications d’exemple du site étaient du texte statique,
|
||||
pas des données à migrer. Les pièces jointes et la réattribution d’un compte lié
|
||||
restent hors périmètre.
|
||||
|
||||
### Présentation des discussions et visages
|
||||
|
||||
Les réponses se lisent en conversation : visage Minecraft, pseudo et date,
|
||||
texte dans un bloc distinct, actions compactes derrière « ⋯ ». Le formulaire
|
||||
reste sous la discussion. Les listes et les aperçus du menu pause affichent
|
||||
également les visages. En jeu, les profils sont résolus par les widgets natifs
|
||||
Minecraft, avec leur texture de repli tant que le skin n’est pas disponible.
|
||||
Sur le site, le rendu par UUID utilise le prestataire mc-api.io déjà employé
|
||||
par la page de compte ; chargement différé, sans référent, initiale locale en
|
||||
cas d’erreur. Aucune image ni URL arbitraire n’entre dans le contrat des posts.
|
||||
Documentation du rendu : https://mc-api.io/docs (GET `/render/{uuid}?size=64`).
|
||||
|
||||
|
||||
## Évolution v2 (beta.157)
|
||||
|
||||
Le champ photo et sa migration explicite, avec conservation des anciens articles,
|
||||
sont décrits dans [Photos de Gazette](gazette-photos-beta157.md).
|
||||
Pour une nouvelle base partagée avec le site, utiliser les migrations Laravel.
|
||||
Pour une base autonome administrée par SQL, appliquer schema-v1.sql puis
|
||||
schema-v1-to-v2.sql avant de connecter le mod beta.157.
|
||||
|
||||
|
||||
## Évolution v3 — demandes suivies (beta.158)
|
||||
|
||||
Annonce : `task` contient `location` nullable (`dimension`, `x`, `y`, `z`),
|
||||
`materials` et `rewards` (27 objets maximum, `item` namespacé de 100 caractères
|
||||
maximum, `count` de 1 à 999999), `audience` shared/single, `reward` none/items/custom
|
||||
et `customReward` (240 caractères). Les coffres sont descriptifs : échanges
|
||||
et partage des récompenses entre joueurs, sans transaction d'inventaire.
|
||||
|
||||
Les tables `sanctuary_community_followers` et `sanctuary_community_subscribers`
|
||||
portent les abonnements et les verrous par joueur. Maximum 16 abonnements/joueur,
|
||||
64/annonce, un seul en mode single ; pas d'auto-abonnement de l'auteur.
|
||||
Le masquage supprime les abonnements ; la fermeture les conserve mais suspend
|
||||
le suivi actif. Un passage en single est refusé au-delà d'un abonné.
|
||||
|
||||
Arrêter les écritures et sauvegarder avant migration. Installation SQL neuve :
|
||||
schema-v1.sql, schema-v1-to-v2.sql, puis schema-v2-to-v3.sql du mod. Sur le site,
|
||||
`php artisan migrate` applique l'équivalent et reconnaît les tables déjà créées.
|
||||
Le compte JDBC nécessite SELECT/INSERT/DELETE sur followers et SELECT/INSERT
|
||||
sur subscribers, en plus des permissions v2. Aucun DDL automatique par le mod.
|
||||
|
||||
L'intendance se gère dans le panneau admin ; seul son dernier message reste
|
||||
public en en-tête. La liaison de compte se fait avec le code généré sur le site
|
||||
et `/sanctuary community link <code>` en jeu (serveur authentifié requis).
|
||||
Les anciens boutons de liaison et d'historique public de l'intendance disparaissent.
|
||||
|
||||
En mode fichier, le panneau d’intendance en jeu s’ouvre avec
|
||||
`/sanctuary community admin` ; le serveur exige les permissions opérateur.
|
||||
|
||||
## Évolution beta.159 — recherche et lecture de l’intendance
|
||||
|
||||
Le schéma reste **v3** : aucune migration de données. Cette évolution remplace
|
||||
la restriction de lecture publique de beta.158 : les onglets Gazette, Tableau
|
||||
et Serveur permettent de lire leurs historiques. La publication et la gestion
|
||||
de l’intendance restent dans l’administration, sans réponse ni abonnement.
|
||||
|
||||
`list` et `cards` acceptent une recherche facultative dans `Request.body` (120
|
||||
caractères maximum, vide par défaut). Le site utilise le paramètre GET `q`,
|
||||
conservé dans la pagination. Recherche littérale de sous-chaîne dans le nom de
|
||||
l’auteur, le titre **ou** le corps, sans distinction de casse, avant pagination.
|
||||
Les accents restent significatifs et `%` / `_` sont des caractères ordinaires.
|
||||
Le filtre reste limité au serveur et à la rubrique sélectionnés ; les contenus
|
||||
masqués restent exclus. Les épingles gardent leur priorité. Le client attend
|
||||
300 ms après la frappe et ignore les réponses correspondant à un ancien filtre.
|
||||
Les pages restent bornées à 10 publications ; aucune recherche dans les réponses.
|
||||
|
||||
Les dates des publications affichent l’année (UTC). Le menu pause affiche la date
|
||||
complète dans la langue et le fuseau local du joueur. La galerie utilise la
|
||||
surface disponible du GUI, adapte ses colonnes et conserve ses pages de 24
|
||||
captures défilantes. Le clic ouvre un aperçu dans cette surface, en conservant
|
||||
les proportions et les boutons Retour / Assigner. Le formulaire conserve son
|
||||
aperçu compact. Les PNG sources ne sont pas modifiés.
|
||||
|
||||
Les demandes suivies ouvertes qui ont une position affichent un `!` sur la carte
|
||||
de pause et le grand atlas, dans la dimension correspondante. Elles ne révèlent
|
||||
pas le terrain et ne chargent aucun chunk. L’infobulle montre titre, auteur,
|
||||
résumé, coordonnées, matériaux et récompense ; les listes longues sont abrégées
|
||||
pour rester dans le GUI. Le clic ouvre l’annonce. « Voir sur la carte » centre
|
||||
l’atlas sur son lieu dans la dimension courante, sans téléportation. Les repères
|
||||
suivent tous les abonnements, indépendamment des trois suivis visibles dans le
|
||||
HUD ; ils disparaissent au désabonnement, à la clôture ou au masquage. Un
|
||||
rafraîchissement des suivis reste actif lorsque la carte ou la pause est ouverte
|
||||
(période de cinq secondes, différée si une autre requête est en cours).
|
||||
|
||||
Le message réseau `following` ajoute `summary`, extrait littéral du corps limité
|
||||
à 240 caractères Unicode plus une ellipse. Le texte intégral et les photos ne
|
||||
sont pas diffusés dans ce message. Aucun champ persistant supplémentaire.
|
||||
@@ -0,0 +1,81 @@
|
||||
# beta.159 — recherche communautaire et galerie adaptable
|
||||
|
||||
Branche `codex/community-search-gallery-beta159`, socle beta.158.
|
||||
|
||||
## Résultat
|
||||
|
||||
Une barre de recherche filtre les publications par auteur, titre ou texte côté
|
||||
serveur, avant pagination. Le filtre accompagne les pages suivantes, conserve
|
||||
les épingles et se réinitialise en effaçant le champ. Les réponses aux anciennes
|
||||
recherches sont ignorées et le focus de saisie est conservé.
|
||||
|
||||
Les trois onglets Gazette / Tableau / Serveur donnent accès aux publications de
|
||||
chaque rubrique. L’en-tête d’intendance de la pause ouvre aussi son historique.
|
||||
La lecture est publique ; la publication et la gestion des messages du serveur
|
||||
restent réservées au panneau administrateur, même pour un opérateur visitant
|
||||
l’écran public. Les messages du serveur n’acceptent ni réponses ni abonnements.
|
||||
|
||||
L’année figure dans la date de pause et les métadonnées des publications. La
|
||||
galerie utilise l’espace disponible aux différentes échelles de GUI et tailles
|
||||
de fenêtre. Les captures restent paginées par 24 avec défilement. Cliquer une
|
||||
capture ouvre un grand aperçu avec les commandes de retour et d’assignation.
|
||||
La photo assignée reste compacte dans le formulaire et les brouillons sont
|
||||
conservés lors du retour. Aucune modification des PNG originaux.
|
||||
|
||||
Les annonces suivies géolocalisées portent un « ! » sur les deux cartes.
|
||||
L’infobulle fournit titre, auteur, résumé, coordonnées, matériaux et récompense
|
||||
sans ouvrir la discussion ; le clic reste disponible. Un bouton dans l’annonce
|
||||
centre le grand atlas sur sa position, dans la dimension courante. Aucune
|
||||
téléportation ni révélation du terrain : seules les coordonnées publiées sont
|
||||
projetées. Les annonces fermées, masquées, sans lieu ou non suivies ne portent
|
||||
pas de repère. Les longues infobulles sont abrégées pour tenir dans le GUI.
|
||||
|
||||
Le site suit le même contrat de recherche et de lecture. Schéma v3 inchangé,
|
||||
aucune migration de sauvegarde ni de base. Libellés français et anglais.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- Stockage fichier et MariaDB : recherche par auteur et corps, casse et accents,
|
||||
filtre avant pagination, épingles conservées, caractères `%` / `_` littéraux,
|
||||
rubrique et serveur isolés, contenus masqués exclus, borne de 120 caractères.
|
||||
- Client natif Minecraft 26.3 / Java 25 / Vulkan : galerie et aperçu en FR/EN,
|
||||
GUI 2/3/4 et fenêtres 1280×960 / 1920×1080 ; retour au brouillon, assignation,
|
||||
publication, listes défilantes et chargement des pages suivantes.
|
||||
- Recherche en jeu au-delà de la première page, filtre par joueur, effacement,
|
||||
conservation du focus et rejet d’une réponse correspondant à l’ancien filtre.
|
||||
- Lecture des messages serveur par un joueur ordinaire, aucun contrôle d’écriture
|
||||
dans les pages publiques, y compris pour l’opérateur ; administration inchangée.
|
||||
- Carte : projection dans la dimension correspondante, titre/auteur/résumé,
|
||||
matériaux et récompense au survol ; ouverture de la demande par clic sur les
|
||||
deux cartes ; retrait immédiat du repère après désabonnement.
|
||||
- Date avec année en FR/EN, GUI 2/3/4, sans chevauchement de l’intendance.
|
||||
- Site : 24 tests / 157 assertions sur SQLite puis MariaDB de développement,
|
||||
formatage Pint réussi. Commit local `e70e78d`, branche `codex/community-search`.
|
||||
|
||||
La commande complète `./gradlew :sanctuary:runClientGameTest check build assemblePack`
|
||||
avec les propriétés client Cards158, QuickTests et Vulkan a exécuté 252 GameTests :
|
||||
229 réussites et les mêmes 23 échecs que beta.158. Aucun test serveur supprimé
|
||||
ou modifié. La comparaison est conservée dans `build/search159-server-failures.json`
|
||||
et le journal dans `build/beta159-check-build.log`.
|
||||
|
||||
Les contrôles restants, la compilation et `assemblePack` passent avec la suite
|
||||
serveur déjà exécutée exclue (`-x :sanctuary:runGameTest`) : 133 tâches, journal
|
||||
`build/beta159-release.log`. Le parcours client final passe en mode MariaDB,
|
||||
puis en mode fichier (`build/beta159-file-client.log`). Les repères ont été
|
||||
vérifiés avec et sans terrain d’atlas débloqué ; les infobulles restent dans la
|
||||
fenêtre aux GUI 2/3/4. Captures représentatives inspectées :
|
||||
`build/search159-file-screens/` et `build/search159-database-screens/`.
|
||||
|
||||
L’export packwiz est vérifié : intégrité ZIP, version beta.159, JAR embarqué
|
||||
identique au JAR de livraison. Les 29 ressources de shaders sont identiques à
|
||||
beta.158, qui conserve le socle beta.151. Les anciens JAR et MRpack beta.154 à
|
||||
beta.158 restent présents. Aucun monde personnel, déploiement de serveur, canal
|
||||
packwiz ou instance Prism n’est modifié.
|
||||
|
||||
## Artefacts locaux
|
||||
|
||||
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.159.jar` — SHA-256 `c30d57a5f37e0a5c9dbb387ca831ce9e5c15a1dfe4cb55af0af20a376e862576`.
|
||||
|
||||
- `build/Sanctuary-beta.159.mrpack` — SHA-256 `868932d88bc241bfb526a7c5e4054009e247658bd0784b7141d82017edcc2823`.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Récap Discord — beta.130 à beta.160
|
||||
|
||||
Brouillon à relire avant publication. Les captures sont des scènes de test,
|
||||
pas des photos d'un serveur public. Aucune annonce envoyée automatiquement.
|
||||
|
||||
## Message 1 — l'image et la lumière
|
||||
|
||||
**Sanctuary — retour sur les beta.130 à 160**
|
||||
|
||||
On a d'abord beaucoup travaillé l'ambiance : bloom, minerais lumineux, rebonds
|
||||
de couleur, relief des matériaux, reflets, rayons du soleil et lumières portées.
|
||||
L'eau a reçu ses propres réglages, puis plusieurs passes de correction ont
|
||||
amélioré les transitions, les distances et les matières.
|
||||
|
||||
La version du shader conservée est celle du socle beta.151. Elle reste gourmande,
|
||||
mais on garde ce travail ! En beta.160, le shader devient **désactivé par défaut
|
||||
sur une nouvelle configuration**, et peut être réactivé dans les options.
|
||||
|
||||
Images : `01-minerais-bloom-beta130.png`, `02-eau-beta144.png`.
|
||||
|
||||
## Message 2 — la vie du serveur
|
||||
|
||||
Le chantier suivant rapproche Minecraft et le site Sanctuary : **Gazette,
|
||||
tableau d'affichage et intendance** partagent un contrat de publications.
|
||||
Le mod fonctionne avec des fichiers par défaut ; une base MariaDB permet de
|
||||
partager les données avec le site.
|
||||
|
||||
Le menu pause a été réorganisé autour des systèmes Sanctuary, de la carte et
|
||||
des publications. La Gazette accepte les articles avec une capture obligatoire,
|
||||
les réponses, les épingles et la recherche par joueur ou contenu.
|
||||
|
||||
Le tableau distingue les types d'annonces par couleur. Les demandes peuvent
|
||||
indiquer un lieu, des matériaux, une récompense et des participants. Ce sont
|
||||
des descriptions pour organiser les échanges entre joueurs : aucun coffre de
|
||||
dépôt ni paiement automatique. Une demande suivie apparaît avec un **! sur la
|
||||
carte**, et son résumé se lit au survol.
|
||||
|
||||
Images : `03-menu-et-quete-beta159.png`, `04-materiaux-beta158.png`.
|
||||
|
||||
## Message 3 — tester ensemble
|
||||
|
||||
Avec la beta.160, on prépare nos séances de test en binôme sur Mac : un petit
|
||||
serveur local, un seul Minecraft à l'écran et des personnages de laboratoire.
|
||||
Le but : vérifier les échanges côté technique pendant que les vrais essais en
|
||||
jeu nous montrent ce qu'il faut simplifier dans l'interface.
|
||||
|
||||
Des scènes courtes et des captures horodatées permettront de reprendre les
|
||||
moments où l'on hésite, cherche un bouton ou perd le fil. Le prochain chantier
|
||||
est l'ergonomie, dans le jeu comme sur le site.
|
||||
|
||||
## Sources et pièces jointes
|
||||
|
||||
Les images originales et leurs empreintes sont rassemblées dans
|
||||
`build/discord-beta130-160/` (ignoré par Git), avec `images.json` pour la provenance.
|
||||
Les captures anciennes illustrent leur version d'origine, sans revendiquer une
|
||||
nouvelle validation graphique. Les sondages restent une idée, pas une fonction livrée.
|
||||
|
||||
Références : `shader-beta130.md` à `nether-pbr-beta151.md`,
|
||||
`community-beta154.md`, `pause-redesign-beta155.md`, `gazette-photos-beta157.md`,
|
||||
`community-cards-beta158.md`, `community-search-beta159.md`, `duo-lab-beta160.md`.
|
||||
@@ -0,0 +1,155 @@
|
||||
# DUO-160 — laboratoire Mac et tests en binôme
|
||||
|
||||
Branche `codex/duo-lab-beta160`, base `beta.159`. Minecraft 26.3, Java 25,
|
||||
Fabric 0.19.5 ; mêmes dépendances. Validation graphique Vulkan uniquement.
|
||||
|
||||
## Contrat
|
||||
|
||||
Le shader natif est OFF quand sa préférence est absente. Un choix sauvegardé
|
||||
ON ou OFF reste respecté, ainsi que ses intensités. Aucun shader supprimé :
|
||||
les ressources beta.151 restent identiques. Le client du laboratoire reçoit
|
||||
explicitement `enabled:false`, indépendamment de l'installation personnelle.
|
||||
|
||||
Tout le laboratoire réside dans `build/duo/`, ignoré par Git. Le monde plat
|
||||
`duo-flat-160`, graine 160, sert aux interfaces et aux échanges ; il ne valide
|
||||
pas le terrain Sanctuary. Aucun monde personnel ouvert ou modifié.
|
||||
Le module `sanctuary-test` fournit les personnages uniquement avec
|
||||
`-Dsanctuary.duo=true`. Il ne fait pas partie du pack normal.
|
||||
|
||||
## Préparer et lancer
|
||||
|
||||
```sh
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
|
||||
./gradlew :sanctuary-test:exportDuoLaunch --max-workers=1 -Dorg.gradle.jvmargs=-Xmx1G
|
||||
python3 scripts/duo_lab.py prepare
|
||||
python3 scripts/duo_lab.py server
|
||||
# Dans un autre terminal :
|
||||
python3 scripts/duo_lab.py client
|
||||
```
|
||||
|
||||
L'export permet ensuite de lancer Java directement, sans conserver un processus
|
||||
Gradle pendant la séance. Préparer à nouveau ne remplace aucun fichier existant.
|
||||
Le client rejoint automatiquement `127.0.0.1:25575`. Au premier accès,
|
||||
choisir la couleur et le familier dans HELLO_WORLD puis entrer dans Sanctuary.
|
||||
Depuis la console serveur : `op KokaLab` donne les commandes au client de test.
|
||||
|
||||
Le serveur écoute seulement sur loopback et utilise des identités hors ligne
|
||||
pour ce laboratoire. Ce profil ne doit pas être exposé sur le réseau. La liaison
|
||||
authentifiée d'un compte web exige toujours un vrai serveur en mode en ligne ;
|
||||
elle n'est pas contournée par les outils du laboratoire.
|
||||
|
||||
Réglages de départ : serveur 256–768 Mio de heap, vue 4 chunks, simulation 3 ;
|
||||
client 512–2048 Mio, vue 6, simulation 4, 30 FPS, fenêtre 1280×720. La mémoire
|
||||
native, graphique et macOS s'ajoute au heap. Aucun engagement de tenir dans
|
||||
une consommation totale de 2,75 Gio. Ne pas exécuter la compilation en même
|
||||
temps qu'une séance. Augmenter seulement après mesure :
|
||||
|
||||
```sh
|
||||
python3 scripts/duo_lab.py server --memory 1024
|
||||
python3 scripts/duo_lab.py client --memory 2560
|
||||
```
|
||||
|
||||
Le mode fichier reste le défaut. Pour une préparation neuve avec le site local :
|
||||
|
||||
```sh
|
||||
python3 scripts/duo_lab.py prepare --storage database --server-id UUID_DU_SERVEUR \
|
||||
--jdbc jdbc:mariadb://127.0.0.1:33077/BASE_DE_TEST
|
||||
SANCTUARY_COMMUNITY_DB_PASSWORD='' python3 scripts/duo_lab.py server
|
||||
```
|
||||
|
||||
La base doit déjà avoir le schéma communautaire v3. Le site doit utiliser le
|
||||
même identifiant de serveur. Aucun schéma ni sauvegarde n'est migré ici.
|
||||
|
||||
## Personnages et séances
|
||||
|
||||
```text
|
||||
/duo spawn Alice
|
||||
/duo spawn Bob
|
||||
/duo list
|
||||
/duo remove Alice
|
||||
/duo trace start
|
||||
/duo trace stop
|
||||
```
|
||||
|
||||
Les personnages portent le préfixe `Lab_`, sont créatifs et apparaissent près
|
||||
de la source de commande. Limite de quatre, noms uniques de 1–12 caractères
|
||||
ASCII alphanumériques ou `_`. Ce sont des ServerPlayer sans transport réseau
|
||||
ni rendu, pas des IA et pas des clients réseau supplémentaires. On peut les
|
||||
cibler avec les commandes natives, par exemple `tp Lab_Bob ~2 ~ ~`.
|
||||
Ils ne valident pas seuls le protocole d'un deuxième client réel.
|
||||
|
||||
La trace consigne noms de test, dimension, positions et angles toutes les deux
|
||||
secondes dans `build/duo/server/duo-sessions/`. Activation explicite, arrêt au
|
||||
bout de cinq minutes maximum ou à la fermeture du serveur. Pas de frappe clavier,
|
||||
mot de passe, navigateur ou audio enregistré.
|
||||
|
||||
Pour la relecture visuelle, identifier la fenêtre Minecraft puis lancer :
|
||||
|
||||
```sh
|
||||
python3 scripts/duo_lab.py windows
|
||||
python3 scripts/duo_lab.py record --window ID_FENETRE_MINECRAFT --seconds 120
|
||||
```
|
||||
|
||||
Une séquence PNG horodatée toutes les deux secondes, un index HTML et une fiche
|
||||
de notes sont écrits dans `build/duo/sessions/`. Ctrl-C termine la capture ; limite
|
||||
de cinq minutes. macOS peut demander l'autorisation de capture. Cette version
|
||||
ne produit pas une vidéo continue et peut manquer une interaction très courte.
|
||||
On ne démarre une capture qu'au début d'une scène annoncée.
|
||||
|
||||
## Trois premières scènes
|
||||
|
||||
1. Ouvrir la Gazette, retrouver un auteur, lire un article et répondre. Vérifier
|
||||
que l'autre interface retrouve la réponse, puis noter les hésitations.
|
||||
2. Créer une demande avec lieu et matériaux, la suivre, lire son infobulle sur
|
||||
la carte, ouvrir la discussion puis arrêter le suivi.
|
||||
3. Afficher deux figurants, tester les interactions de proximité et comparer
|
||||
la fluidité à un seul joueur. Garder un véritable second client pour une
|
||||
vérification réseau ultérieure si nécessaire.
|
||||
|
||||
Le récap Discord se trouve dans `discord-beta130-160.md` ; les originaux des
|
||||
quatre illustrations sont copiés avec provenance dans `build/discord-beta130-160/`.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- `./gradlew check build assemblePack` : 252 GameTests, 229 réussites et les
|
||||
mêmes 23 échecs que beta.159. Aucun échec ajouté ; comparaison dans
|
||||
`build/duo160-server-failures.json`, log `build/beta160-check-build.log`.
|
||||
- Contrôles restants, compilation, pack et client natif Vulkan réussis avec la
|
||||
suite serveur déjà exécutée exclue (`-x :sanctuary:runGameTest`) : 137 tâches,
|
||||
`build/beta160-release.log`. Le test `ShaderDefaults160ClientChecks` vérifie
|
||||
préférence absente, objet vide, ON explicite, OFF enregistré et réactivation,
|
||||
avec intensité personnelle conservée. Marqueur `SHADER160_DEFAULTS_PASS`.
|
||||
- Module de test reconstruit après ajustement du transport sans réseau : build
|
||||
réussi. L'export de lancement inclut les arguments fournis par Loom et élimine
|
||||
son argfile imbriqué, que Java ne peut pas réinterpréter depuis un autre argfile.
|
||||
- Serveur réel à 768 Mio : démarrage sur loopback, monde plat neuf ; création de
|
||||
quatre acteurs, doublon et cinquième refusés, retrait et acteur absent vérifiés.
|
||||
Le journal produit 56 observations JSON valides puis s'arrête sur commande.
|
||||
- Client réel séparé, Vulkan, 2048 Mio, shader OFF : connexion jusqu'à l'écran
|
||||
HELLO_WORLD vérifiée visuellement. La création du profil et le parcours en jeu
|
||||
avec le joueur restent à faire ensemble ; aucune session ergonomique humaine
|
||||
terminée ni validation d'un deuxième client réseau revendiquée.
|
||||
- Mesure ponctuelle avant l'entrée du joueur : serveur ~313 Mio de heap utilisé,
|
||||
client au menu ~289 Mio. Ce ne sont ni des pics ni une mesure de RAM système
|
||||
totale. Les figurants ne possèdent ni sockets ni fenêtre graphique.
|
||||
- Capture ciblée macOS essayée sur quatre secondes : séquence PNG, manifeste,
|
||||
index HTML et fiche de notes créés. Aucun enregistrement continu ou micro.
|
||||
- Export MRpack : intégrité ZIP, version et JAR embarqué vérifiés. Les 29 ressources
|
||||
de shaders sont inchangées depuis beta.159. JAR et packs beta.154–159 conservés.
|
||||
- Skill personnel `sanctuary-session-notes` installé et validé séparément dans
|
||||
`~/.codex/skills/` pour classer les dictées en Markdown. Il ne change pas le
|
||||
modèle du tour courant ; Luna/low peut être choisi dans une tâche dédiée.
|
||||
|
||||
Le serveur local utilise la base de développement du site, schéma v3 déjà présent,
|
||||
sous le même scope. Aucune validation de liaison de comptes hors ligne : cette
|
||||
opération reste réservée au mode authentifié. Aucun déploiement public, canal
|
||||
packwiz, serveur personnel ou instance Prism modifié.
|
||||
|
||||
## Artefacts locaux
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.160.jar` — SHA-256
|
||||
`4c19e620e989050488c9a96aaddaf897a74115a9fb4c5099e5518ae32db5b2e8`.
|
||||
- `build/Sanctuary-beta.160.mrpack` — SHA-256
|
||||
`272e3460f35c8d9a4de689d30e4d952c3121923ba5fe30a23c83b3e76bd23bac`.
|
||||
- `build/duo/Jouer.command` et `Serveur.command` : raccourcis locaux de séance.
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
# Communauté, économie et aventures — cadrage du 25 septembre 2026
|
||||
|
||||
Statut : **cadrage de conception**. La réalisation du premier prototype d'arène
|
||||
autorisé ensuite est suivie séparément dans [beta.167](horde-lab-beta167.md) ;
|
||||
elle ne vaut pas livraison de l'ensemble des systèmes décrits ici. Cette note
|
||||
conserve la discussion du créateur et distingue ses décisions des propositions
|
||||
à éprouver. Base : dernier `origin/main` vérifié, beta.166 (`7979f33`).
|
||||
Le créateur demande finalement une branche dédiée, puis une intégration sur
|
||||
`main` à ne pas oublier. Branche : `codex/communaute-economie-beta167`.
|
||||
La prochaine livraison de code visée est **beta.167** ; ce numéro est désormais
|
||||
préparé dans les métadonnées du prototype, sans publication. On avance par petits résultats
|
||||
jouables, sans figer maintenant toute l'économie.
|
||||
|
||||
## Ce que l'on cherche à produire
|
||||
|
||||
Un serveur semi-anarchique dont l'État assure une administration forte, trace
|
||||
les événements, régule les prix du marché et pose des frontières. Les joueurs
|
||||
peuvent chercher leurs propres moyens de les franchir. Le système doit susciter
|
||||
la coopération, la multiplication, la fabrication, la destruction et la recherche.
|
||||
L'objectif est une émulation collective, avec des conséquences dans le monde.
|
||||
|
||||
Minecraft, le site et Discord prolongent la même vie communautaire. La BDD doit
|
||||
permettre d'analyser les activités à la journée et d'en rendre compte sur le site.
|
||||
Le serveur Minecraft conserve l'autorité sur les actions et récompenses de jeu.
|
||||
|
||||
## Décisions et intentions exprimées par le créateur
|
||||
|
||||
### Tableau et Gazette
|
||||
|
||||
- Le tableau propose **une seule sorte de message général**, sans les quatre
|
||||
catégories obligatoires actuelles. Annonce, demande, besoin, information,
|
||||
rendez-vous et découverte sont des usages de ce même message.
|
||||
- La Gazette accueille les récits, photos et discussions durables ; le tableau
|
||||
sert aux messages immédiats et à l'organisation. Les références Facebook et
|
||||
Twitter expriment ces usages, pas une demande de reproduire leurs fonctions.
|
||||
- Un message peut être associé à une quête donnant de l'XP aux autres joueurs.
|
||||
Une découverte peut être publiée pour organiser son exploration ensemble.
|
||||
Tout message n'a pas à devenir une quête.
|
||||
|
||||
### Quêtes canoniques, coopération et récompenses de prestige
|
||||
|
||||
- Les panneaux émeraude, rubis et saphir testés avec de vrais joueurs conviennent
|
||||
au gain d'XP solo, mais tendent à isoler les participants. Conserver un intérêt
|
||||
solo tout en développant des raisons concrètes de coopérer.
|
||||
- Le tableau accessible dans un menu et ses quêtes proposées pour une durée
|
||||
limitée sont une piste appréciée par le créateur.
|
||||
- Prévoir des quêtes canoniques reconnaissables, par exemple **chasse aux
|
||||
zombies**, avec XP selon les objectifs accomplis, quotas et paliers de
|
||||
récompenses. La référence aux passes de progression de jeux comme Rocket
|
||||
League concerne cette progression visible ; aucun modèle payant n'est demandé.
|
||||
- Des quêtes difficiles à obtenir ou à accomplir peuvent donner des **capes et
|
||||
familiers exclusifs**, recherchés pour leur valeur cosmétique et intrinsèque.
|
||||
Nature de l'exclusivité, disponibilité et capacités des familiers restent à
|
||||
préciser ; ne pas conclure que tous les familiers sont purement cosmétiques.
|
||||
- La discussion retient les deux usages dans une même quête : avancer seul
|
||||
pendant sa partie normale et rejoindre une horde collective déclenchée par
|
||||
bounty. Les zombies remplacent les squelettes comme première piste liée à
|
||||
l'île abandonnée ; aucun scénario d'ossuaire n'est retenu.
|
||||
|
||||
### Familiers comme incubateurs d'XP
|
||||
|
||||
Idée ajoutée par le créateur : les familiers **multiplient leur XP stockée au
|
||||
fil des blocs parcourus à pied, posés et cassés**. Ils servent d'**incubateurs
|
||||
d'XP**, avec une croissance modérée pour éviter un système trop puissant.
|
||||
Ce mécanisme peut donner un rôle aux familiers dans les quêtes.
|
||||
|
||||
« Multiplier » décrit ici l'intention de faire fructifier la réserve ; aucune
|
||||
formule, croissance exponentielle par action, valeur de rendement ou limite
|
||||
n'est décidée. L'origine de l'XP déposée, sa récupération et les conditions de
|
||||
présence du familier restent à choisir, ainsi que la personne dont les actions
|
||||
comptent. Cette idée est à concevoir, pas une nouvelle capacité livrée.
|
||||
|
||||
### État, navets et loterie
|
||||
|
||||
- L'État joue à la fois un rôle d'arbitre et d'acteur du monde. Ses contraintes
|
||||
doivent donner des occasions de jouer et de s'organiser.
|
||||
- Les navets s'achètent auprès d'un **PNJ le dimanche**. Ils se revendent pendant
|
||||
la semaine et **pourrissent après une semaine s'ils ne sont pas vendus**.
|
||||
Il ne s'agit pas d'une culture récoltée par les joueurs.
|
||||
- Des tickets trouvés ou achetés pendant la semaine servent à la loterie du
|
||||
dimanche, avec de **vraies machines manipulant des stocks d'objets**.
|
||||
- Ces machines peuvent notamment dupliquer ou diviser un stock. Les propositions
|
||||
précédentes de simples permis et réductions ne définissent pas ce mécanisme.
|
||||
Les privilèges temporaires restent une idée initiale possible, sans catalogue
|
||||
de récompenses approuvé.
|
||||
- Le rapport entre ces opérations et le **ballast des Backrooms** doit être
|
||||
pensé dès leur conception.
|
||||
|
||||
### Bounties, cartes et extensions
|
||||
|
||||
- Les bounties sont des **objets collectionnables que l'on utilise quand on est
|
||||
prêt**. Elles peuvent être offertes comme occasions d'aventure.
|
||||
- Réutiliser le système de cartes et en créer à la volée pour ces aventures.
|
||||
La forme exacte de la carte et son geste d'activation restent à choisir.
|
||||
- Prévoir **huit structures** permettant d'étendre les huit extensions de l'île
|
||||
principale, en lien avec un **bloc originel sur l'île**, puis des **ancres**
|
||||
permettant de générer des structures d'aventure.
|
||||
- Les objectifs évoqués comprennent primes, objets clés à retrouver et
|
||||
structures à détruire. L'ensemble doit aussi permettre la contrebande
|
||||
organisée et les initiatives des joueurs.
|
||||
- L'ordre de déblocage entre structures, extensions et bloc originel n'est pas
|
||||
encore fixé. Ne pas transformer cette intention en règle « huit sur huit ».
|
||||
|
||||
### Familles d'objets retenues et prochaine scène du labo
|
||||
|
||||
Le créateur retient le tableau fonctionnel suivant :
|
||||
|
||||
| Famille | Fonction |
|
||||
| --- | --- |
|
||||
| **Carte de découverte** | Indiquer un lieu existant à explorer ; transmettre une information. |
|
||||
| **Carte d'épreuve** | Déclencher une activité à l'activation : horde, défense, recherche… |
|
||||
| **Clé ou relique** | Ouvrir un accès ou activer un mécanisme précis dans le monde. |
|
||||
|
||||
Piste accessoire ajoutée : des **cartes collectionnables générées par le jeu**.
|
||||
Leur sujet, leur présentation, leur rareté et leur éventuel lien avec les cartes
|
||||
fonctionnelles restent ouverts. Ne pas leur attribuer automatiquement un pouvoir
|
||||
ou une récompense : collection et activation sont deux usages à distinguer.
|
||||
|
||||
La prochaine scène à **imaginer dans le laboratoire** est une **arène ronde
|
||||
avec un nouveau bloc interactif au centre**. La carte de horde est la première
|
||||
carte d'épreuve envisagée : elle fait apparaître un groupe de monstres par vagues.
|
||||
Le nom, l'apparence, les dimensions et les règles du bloc restent à concevoir.
|
||||
Cette décision portait initialement sur la conception. Le créateur a ensuite
|
||||
autorisé un premier essai jouable ; son état réel est dans le
|
||||
[contrat du prototype](horde-lab-beta167.md).
|
||||
|
||||
Parcours proposé pour ce prototype : présenter une carte au bloc → lire l'épreuve
|
||||
et ses récompenses → rejoindre le groupe → lancer explicitement → affronter les
|
||||
vagues → consulter le résultat. Une activation au clic droit, des apparitions
|
||||
réparties au bord du cercle et un état visuel du bloc sont des propositions.
|
||||
La consommation de carte, l'engagement des participants, les arrivées tardives,
|
||||
la défaite, la déconnexion et les récompenses seront précisés avant le code.
|
||||
|
||||
Le laboratoire doit utiliser une scène de développement neuve et isolée ; ne pas
|
||||
réécrire le monde du labo déjà conservé ni une sauvegarde personnelle. La forme
|
||||
transportable d'une balise d'épreuve reste une possibilité ultérieure. Ce bloc
|
||||
d'arène n'est pas encore assimilé au bloc originel ou à une ancre d'expansion.
|
||||
|
||||
### Données et liens entre les services
|
||||
|
||||
- Intégrer les statistiques du jeu à la BDD quand elles sont disponibles et
|
||||
raccorder progressivement les systèmes déjà en place.
|
||||
- Viser la traçabilité des échanges et événements, les analyses quotidiennes et
|
||||
les comptes rendus sur le site.
|
||||
- Exploiter le lien d'identité Discord/site/Minecraft pour de futures interactions
|
||||
personnelles, dont les notifications. Leur contenu et leur fréquence restent
|
||||
à définir ; cette discussion n'autorise aucun envoi de message.
|
||||
|
||||
## Ce qui existe réellement sur le socle beta.166
|
||||
|
||||
| Socle vérifié dans les sources et contrats | Limite actuelle |
|
||||
| --- | --- |
|
||||
| Gazette, photos, réponses, tableau, abonnements et repères de demandes ; stockage fichier ou MariaDB partagé | Quatre catégories `info/work/need/event` ; matériaux et récompenses descriptifs, sans livraison ni XP automatique |
|
||||
| Compteurs serveur publiés en base toutes les 30 secondes | Dernier instantané : durée de fonctionnement, morts, joueurs connectés, état ; pas un historique individuel quotidien complet |
|
||||
| Activité matérielle locale datée du Blocodex : minage, pose, fabrication, ramassage et jet | Agrégats journaliers sans distinction par joueur ou dimension ; ne prouvent ni échange, ni stock, ni ballast |
|
||||
| Cartes au trésor physiques et import de leurs repères dans l'atlas | Destinations provenant de plans existants ; aucun moteur de bounty activable ni de génération d'aventure à la demande |
|
||||
| Génération d'expansions et quatre anciennes expéditions ouvertes | Les huit nouveaux déblocages, le bloc originel et les ancres restent à concevoir |
|
||||
| Inscription web et reprise des codes d'accès ; lien aux identités Discord | Pas de service de notifications personnelles livré par ce chantier ; le dernier contrat conserve une validation OAuth réelle à terminer |
|
||||
|
||||
Références : [communauté](community-contract-v1.md),
|
||||
[compteurs beta.161](session-fixes-beta161.md),
|
||||
[activité datée](blocodex.md#relevés-datés--portée-de-lalpha23),
|
||||
[cartes beta.059](atlas-markers-beta059.md), [expansions](expansion.md),
|
||||
[inscription beta.166](inscription-web-beta166.md).
|
||||
Cet état décrit le dépôt, pas une vérification du déploiement public.
|
||||
|
||||
## Propositions de fonctionnement à valider
|
||||
|
||||
Pour les quêtes canoniques, la proposition discutée associe des **paliers
|
||||
personnels à un effort collectif**. Émeraude pourrait accueillir les contrats
|
||||
accessibles, rubis les opérations coordonnées, saphir les aventures rares et
|
||||
exigeantes. Cette répartition n'est pas une règle arrêtée.
|
||||
|
||||
Premier essai désormais envisagé : une chasse aux zombies avec paliers d'XP
|
||||
personnels, à laquelle contribue aussi une horde collective activée par carte.
|
||||
Une jauge commune et des préparatifs restent des options, sans scénario imposé.
|
||||
Les nombres 10/30 cités pendant la discussion sont illustratifs. Participation
|
||||
au-delà du dernier coup et conditions de maîtrise pour les trophées restent à définir.
|
||||
Un carnet pourrait présenter les objectifs et gains ; l'échéance de l'offre et
|
||||
le moment d'activation d'une bounty obtenue seraient distincts.
|
||||
|
||||
L'incubation d'XP pourrait accompagner ces parcours d'exploration, de construction
|
||||
et de minage. Avant tout essai, proposer puis mesurer un rendement et un plafond,
|
||||
en examinant les trajets répétitifs, les boucles pose/casse et le cumul de
|
||||
familiers. Ce sont des points d'équilibrage à décider, sans taux ni interdiction
|
||||
déjà validés. Toute future variation de réserve doit pouvoir être expliquée par
|
||||
les actions serveur enregistrées et rester cohérente avec les récompenses de quête.
|
||||
|
||||
Le message général pourrait recevoir des éléments facultatifs : lieu, rendez-vous,
|
||||
objectif et récompense. Le tableau rassemble les participants ; la Gazette garde
|
||||
le récit. La bounty peut être liée à une publication sans que poster un message
|
||||
crée automatiquement une aventure.
|
||||
|
||||
Parcours proposé : obtenir une carte → la conserver ou l'échanger → réunir un
|
||||
groupe → l'activer → accomplir l'objectif → recevoir la récompense. Conserver
|
||||
ensuite une carte souvenir portant le résultat et les participants est une option.
|
||||
Échangeabilité, perte, vol, copie et consommation de l'objet restent à décider.
|
||||
|
||||
Deux usages possibles des cartes : révéler un lieu existant, ou préparer une
|
||||
nouvelle aventure à l'activation dans une ancre. Une copie cartographique pourrait
|
||||
partager les indications sans multiplier les droits à récompense. Ce n'est pas
|
||||
encore un contrat implémenté. Les Backrooms conservent leur intention spécifique
|
||||
de découverte sans coordonnées ni carte automatique.
|
||||
|
||||
Exemple de machine, **sans valeur d'équilibrage approuvée** : un ticket et
|
||||
64 lingots engagés donnent 128 ou 32 lingots. Probabilités, stocks admissibles,
|
||||
fréquence, financement et comportement des objets uniques sont à définir.
|
||||
Conserver l'échéance d'origine des navets lors d'un transfert ou d'une duplication
|
||||
est proposé pour que ces opérations ne rajeunissent pas les lots.
|
||||
|
||||
Pour le ballast, une perte pourrait laisser un dépôt, une duplication une trace
|
||||
architecturale ou une anomalie. Aucune équivalence quantitative n'est décidée.
|
||||
Le [contrat cosmologique](cosmologie.md#le-ballast-de-léconomie) reste ouvert sur
|
||||
matière retirée, empreinte ou combinaison des deux. Une trace ne donne pas à elle
|
||||
seule le droit de créer des objets récupérables.
|
||||
|
||||
Boucle envisagée : **production et échanges → machine → ballast → lieu à
|
||||
explorer → bounty → expédition → trouvailles et nouveaux échanges**.
|
||||
Le lien automatique entre chaque étape est une proposition à éprouver.
|
||||
|
||||
## Ordre de travail proposé
|
||||
|
||||
**Dernière orientation : cadrer d'abord la scène d'arène ronde du labo et son
|
||||
bloc central**, pour rendre la carte d'épreuve concrète. Le tableau ci-dessous
|
||||
conserve les dépendances générales ; son ordre initial n'impose pas de terminer
|
||||
la BDD ou la simplification du tableau avant de concevoir cette scène.
|
||||
|
||||
| Étape | Résultat concret à obtenir | Ce qui doit être précisé juste avant |
|
||||
| --- | --- | --- |
|
||||
| 1. Simplifier le tableau | Publier et lire un message général en jeu et sur le site, retrouver les anciennes annonces et leurs suivis | Présentation des champs facultatifs ; compatibilité des anciennes catégories sans effacer l'historique |
|
||||
| 2. Observer l'existant | Produire un premier compte rendu quotidien depuis des données serveur réelles en BDD | Périmètre des statistiques, unités, identité joueur/monde, jours, visibilité et reprise après panne |
|
||||
| 3. Jouer une première bounty | Obtenir une carte, la garder, l'activer et terminer un objectif vérifié par le serveur, avec une seule attribution de récompense | Un objectif simple, par exemple une livraison ; rôle du groupe, financement et nature de l'XP |
|
||||
| 4. Éprouver l'économie du dimanche | Un PNJ vend des navets qui vieillissent ; une machine engage un stock et rend son résultat, chaque opération étant tracée | Cours et revente, échéance exacte, tickets, hasard et conversion en ballast |
|
||||
| 5. Relier les aventures au territoire | Définir les huit structures, puis éprouver une première activation et une expédition par ancre avant de décliner les huit | Articulation avec les quatre anciennes régions, emplacement, coûts, graine/version et protection des terrains existants |
|
||||
| 6. Faire vivre les prolongements | Alimenter les nouvelles régions des Backrooms avec le ballast validé ; ouvrir les notifications choisies et les récits du site | Contrat de ballast livré avant BR-01 ; règles de publication et préférences Discord |
|
||||
|
||||
Cet ordre est une proposition de départ, pas six grosses livraisons promises.
|
||||
Chaque étape peut être divisée selon les essais. Les rapports du site commencent
|
||||
à l'étape 2 ; les Backrooms et notifications sont des suites distinctes.
|
||||
Les nouveaux systèmes produisent leurs événements dès leur première livraison.
|
||||
Le premier parcours bounty peut utiliser un objectif existant sans attendre la
|
||||
génération des huit structures.
|
||||
|
||||
## Points à trancher au fil de ces premières étapes
|
||||
|
||||
1. **Récompenses** : quelle XP, payée par qui, attribuée à qui dans un groupe,
|
||||
pour quelle preuve d'accomplissement ?
|
||||
2. **Bounties** : carte physique liée à l'atlas ou autre présentation ; échange,
|
||||
copie, vol, perte, activation, abandon, échec et souvenir ?
|
||||
3. **Navets** : sept jours après achat ou échéance hebdomadaire commune ; horloge
|
||||
pendant les arrêts, cours de revente et devenir des navets pourris ?
|
||||
4. **Machines et ballast** : quels stocks, probabilités et résultats ; quelle
|
||||
part est une trace, une perte ou une matière récupérable ?
|
||||
5. **Territoire** : emplacement des huit structures, ordre d'ouverture et rôle
|
||||
du bloc originel ; frontières franchissables par quels moyens de jeu ?
|
||||
6. **Information** : ce que l'administration technique enregistre, ce que l'État
|
||||
sait dans la fiction, ce que le public voit et ce que Discord signale ?
|
||||
7. **Incubateurs d'XP** : dépôt initial, actions reconnues, croissance et plafond,
|
||||
familier porté ou présent, cumul, transfert/retrait et articulation avec les
|
||||
quêtes ? L'XP incubée et les récompenses directement attribuées doivent être
|
||||
distinguées pour éviter un double compte.
|
||||
|
||||
Ces distinctions doivent laisser exister secrets, découverte et contrebande.
|
||||
Un objet ramassé après un jet n'est pas automatiquement une vente. Les compteurs
|
||||
cumulés historiques ne permettent pas de reconstruire les journées antérieures.
|
||||
Une période non observée doit rester identifiable, sans inventer des zéros.
|
||||
|
||||
## Conditions de réalisation
|
||||
|
||||
Avant des écritures réelles : contrat de données additif, événements identifiés
|
||||
et datés, reprise sans double récompense ni double consommation, comportement
|
||||
explicite en cas de panne et séparation entre résultat tenté et résultat acquis.
|
||||
La BDD sert les analyses sans imposer des requêtes bloquantes au thread de jeu.
|
||||
Les frontières, prix, récompenses et pertes sont des règles serveur.
|
||||
|
||||
Aucune modification de monde, migration, régénération ou activation d'expansion
|
||||
n'est autorisée par cette note. Leurs futurs contrats doivent préserver les
|
||||
sauvegardes et secteurs existants. L'évolution des anciens contenus communautaires
|
||||
devra également être explicitée avant de changer leur stockage.
|
||||
|
||||
La première passe était documentaire. La réalisation autorisée ensuite porte
|
||||
uniquement sur le laboratoire de horde, selon son contrat propre. Les autres
|
||||
systèmes décrits comme futurs le restent.
|
||||
|
||||
### Retour sur main et prochaine version
|
||||
|
||||
- **À faire avant livraison : intégrer le travail validé sur `main`**, vérifier
|
||||
le résultat après intégration et reprendre le travail depuis cette base.
|
||||
- Le premier périmètre proposé était la simplification du tableau. La discussion
|
||||
se concentre maintenant sur la conception de l'arène du labo et du bloc central ;
|
||||
le périmètre de code de beta.167 est maintenant le prototype de horde du labo.
|
||||
L'ensemble de cette feuille
|
||||
de route n'est pas promis dans une seule version.
|
||||
- À la première livraison de code, revérifier le compteur disponible, synchroniser
|
||||
`mod_version`, `pack_version` et `packwiz/pack.toml`, puis exécuter les contrôles
|
||||
requis. Le prototype prépare désormais ces trois valeurs à beta.167.
|
||||
- Aucun tag, push, artefact public, canal packwiz ou déploiement personnel n'est
|
||||
effectué par cette prise de notes.
|
||||
|
||||
## Ajustement du 26 septembre — cartes de horde
|
||||
|
||||
La carte est consommable et invoque immédiatement des vagues là où on se trouve.
|
||||
Aucune inscription : on participe spontanément en arrivant sur le combat.
|
||||
Les monstres portent les butins spéciaux, ramassés librement par les joueurs.
|
||||
Chaque carte possède une illustration mappifiée (monstres, textures des butins),
|
||||
une identité et une difficulté lisible. Le décor circulaire reste une piste
|
||||
pour les futurs donjons. [Premier labo à trois cartes](horde-cartes-beta168.md).
|
||||
|
||||
## Retour de combat du 26 septembre — familiers et horde
|
||||
|
||||
Le créateur constate que les familiers ne sont pas utiles au combat : prévoir
|
||||
une refonte de leur contribution, à évaluer en combat réel contre une horde.
|
||||
Ce constat ne prouve pas une panne technique ; diagnostic des comportements,
|
||||
rôles et lisibilité encore à faire. Aucune refonte des familiers livrée dans ce lot.
|
||||
|
||||
Les [cartes beta.169](horde-invasion-beta169.md) restent inconnues avant leur
|
||||
prise en main. Elles ouvrent une invasion continue de monstres variés, dont la
|
||||
cadence accélère, avec butins propres aux espèces, rubis et saphirs. Apparition et
|
||||
mort réelle utilisent les runes SGA et les particules natives Minecraft.
|
||||
@@ -0,0 +1,96 @@
|
||||
# beta.157 — photo obligatoire et galerie de captures
|
||||
|
||||
## Contrat de migration communautaire v2
|
||||
|
||||
La génération, les chunks et les sauvegardes Minecraft ne changent pas.
|
||||
Seul le document communautaire évolue : champ `photo`, JPEG encodé en base64,
|
||||
48 Kio binaires maximum, dimensions de 1 × 1 à 960 × 540 pixels.
|
||||
Les captures originales ne sont jamais modifiées. Les copies destinées à la Gazette
|
||||
sont redimensionnées et réencodées, sans métadonnées, avant transmission.
|
||||
|
||||
Les nouveaux articles exigent une photo valide. Les articles v1 restent lisibles ;
|
||||
leur prochaine modification exige une photo. Avis, intendance et réponses restent
|
||||
textuels. La modération ne nécessite pas d'ajouter une photo à un ancien article.
|
||||
La photo est enregistrée atomiquement avec le texte, auteur et révision ; pas de
|
||||
référence à un chemin local, d'URL distante ou de média orphelin persistant.
|
||||
|
||||
Fichier : v1 est lu sans modification. À la première écriture réussie, une copie
|
||||
exacte `sanctuary-community.json.v1.bak` est créée sans écrasement, puis le document
|
||||
v2 est écrit atomiquement. Un backup différent bloque la migration. La limite de
|
||||
64 Mio du document reste appliquée, photos comprises. Un retour à beta.156 exige
|
||||
de restaurer la copie v1 et perd les changements communautaires postérieurs.
|
||||
Ne pas restaurer pendant que le serveur tourne.
|
||||
|
||||
MariaDB : arrêter les écritures du site et du serveur, sauvegarder la base, exécuter
|
||||
explicitement `community/schema-v1-to-v2.sql` ou la migration Laravel équivalente,
|
||||
puis lancer les deux applications v2. Migration additive : colonne `photo` nullable,
|
||||
aucune suppression ni réécriture des articles existants. Le mod n'exécute aucune
|
||||
migration SQL au démarrage. Les applications v1 refusent le schéma v2. Pas de
|
||||
rollback automatique destructif ; restaurer la sauvegarde avec les deux logiciels
|
||||
v1 si nécessaire. Aucun changement n'est appliqué aux bases ou mondes personnels
|
||||
par la préparation de cette livraison.
|
||||
|
||||
Réseau : canaux v1 conservés, action `photo` par fragments de 8 000 caractères,
|
||||
au plus 9 fragments et 65 536 caractères, accusés un par un. Une seule photo
|
||||
transitoire par joueur, liée à l'UUID de publication, expire après deux minutes ;
|
||||
limite globale de 128 transferts. Les listes ne transportent pas les photos ;
|
||||
seul le détail d'un article les contient. Le plafond des réponses
|
||||
passe à 262 144 caractères pour une photo et une discussion complète ; les
|
||||
clients et le serveur doivent être mis à jour ensemble vers beta.157. Les
|
||||
anciens clients ne peuvent plus créer d'article sans photo.
|
||||
|
||||
Galerie : grille de 2 ou 3 colonnes selon la taille d'interface, molette et barre
|
||||
native, 24 captures par page parmi les 4 096 PNG les plus récents par nom, lecture
|
||||
en arrière-plan. Aperçu puis assignation explicite ; Annuler conserve le brouillon.
|
||||
Les captures illisibles restent visibles mais désactivées. Limites sources :
|
||||
32 Mio, 8 192 pixels par axe et 32 millions de pixels. Textures libérées à la
|
||||
fermeture. Le site accepte PNG/JPEG et nécessite PHP GD ; le mod propose les PNG
|
||||
du dossier `screenshots` de son instance.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- Tests natifs Vulkan Minecraft 26.3 : galerie FR/EN aux échelles 2/3/4,
|
||||
molette, pagination, aperçu, annulation avec brouillon conservé, bouton Publier
|
||||
désactivé sans photo, envoi fragmenté, photo visible et relecture après
|
||||
réouverture de l'adaptateur. Parcours réussi en fichier et MariaDB.
|
||||
- Tests métier : photo manquante/invalide, révision, auteur, conservation après
|
||||
modification, reprise idempotente, redémarrage fichier/SQL, ancien article v1,
|
||||
copie de migration exacte, fragments hors ordre/incomplets/expirés.
|
||||
- Site : 17 tests HTTP, 97 assertions réussies ; photo obligatoire, faux fichier,
|
||||
dimensions refusées, réduction, lecture, isolement serveur, auteur, masquage,
|
||||
anciennes publications et conservation après modification.
|
||||
- Échange réel Java/PHP sur une MariaDB isolée : création en jeu lue par le site,
|
||||
modification et réponse Web relues en Java, article/photo du site décodés par
|
||||
Java. Migration Laravel et alternative SQL testées sur bases de développement.
|
||||
- `./gradlew check build assemblePack` : 252 GameTests serveur terminés en
|
||||
3,457 minutes, 229 réussis et les mêmes 23 échecs que la baseline beta.154/155
|
||||
(liste comparée intégralement, aucun nouvel identifiant en échec). La commande
|
||||
générale échoue donc après 6 min 59 s ; cette limite préexistante reste ouverte.
|
||||
- Régression native du menu pause : FR/EN, échelles 2/3/4, carte, défilements
|
||||
indépendants, pagination, article et avis longs, réponse, options et reprise :
|
||||
réussie avec les articles munis d'une photo.
|
||||
- Construction et assemblage séparés, sans relancer les 23 GameTests connus :
|
||||
`check build assemblePack -x :sanctuary:runGameTest` réussit (avec le parcours
|
||||
client natif du menu pause), 2 min 56 s.
|
||||
|
||||
Les captures d'essai sont des mires générées dans une instance de développement,
|
||||
sans accès aux captures ou sauvegardes personnelles. Pas de déploiement sur le
|
||||
site public, serveur personnel, canal packwiz ou Prism. Les JAR beta.154–156
|
||||
restent présents ; JAR et MRpack beta.156 gardent leurs empreintes antérieures.
|
||||
|
||||
|
||||
Le site correspondant est sur `codex/community-photos`, commits `e3ae3eb` et
|
||||
`9626e95`, sans push ni publication. Logs de développement :
|
||||
`build/photos157-client-file.log`, `build/photos157-client-database.log`,
|
||||
`build/photos157-interop-*.log`, `build/photos157-check-build.log` et
|
||||
`build/photos157-final-build.log`.
|
||||
|
||||
## Artefacts locaux
|
||||
|
||||
JAR `mods/sanctuary/build/libs/sanctuary-beta.157.jar` et pack
|
||||
`build/Sanctuary-beta.157.mrpack`, versions et JAR embarqué vérifiés.
|
||||
Les 29 ressources de shaders sont identiques à beta.156 (socle beta.151).
|
||||
Reçu complet : `build/photos157-validation.json`.
|
||||
|
||||
- SHA-256 mods/sanctuary/build/libs/sanctuary-beta.157.jar: `96ff6d22ab593eb113cde63239ff4839f6a75b9a165fbec93a0aaab3c09dee7c`.
|
||||
- SHA-256 build/Sanctuary-beta.157.mrpack: `b03da5f67a3dcf8051666641dccd379a52b8c3c09308e1654b00440a37693e96`.
|
||||
@@ -0,0 +1,26 @@
|
||||
# beta.165 — Gazette en article et conversation
|
||||
|
||||
Photo en tête, titre et description dessous dans le même conteneur sombre.
|
||||
Auteur, visage, date et actions sous le conteneur. Réponses à droite avec
|
||||
défilement indépendant et saisie fixe ; sur GUI étroit, réponses sous l’article.
|
||||
Retour et Actualiser restent en haut, comme pour le tableau.
|
||||
|
||||
Interface cliente uniquement, sans changement de stockage ni de monde.
|
||||
Test client Vulkan réussi en FR/EN aux échelles GUI 2, 3 et 4 :
|
||||
position du panneau de réponses, repli sous l’article, défilement disponible,
|
||||
brouillon conservé après redimensionnement et réponse envoyée puis relue.
|
||||
Le scénario existant du tableau et celui du menu pause passent également.
|
||||
Captures conservées dans `build/qa-beta165/` (image synthétique de test).
|
||||
Inspection visuelle FR GUI 2 et GUI 4 effectuée.
|
||||
Journal : `build/beta165-client.log`.
|
||||
|
||||
`check build assemblePack --continue` exécuté : mêmes 23 GameTests en échec
|
||||
qu’en beta.164, aucun nouvel échec. Comparaison enregistrée dans
|
||||
`build/session165-server-failures.json`. La vérification générale reste en échec.
|
||||
Assemblage séparé réussi avec `build assemblePack :sanctuary-test:exportDuoLaunch
|
||||
-x :sanctuary:check` après cette vérification.
|
||||
|
||||
JAR local : `mods/sanctuary/build/libs/sanctuary-beta.165.jar`.
|
||||
SHA-256 : `03a954caf68089862b1d237dcf1ebaa501e3ce3cfedb1f5184e9412e73fd711d`.
|
||||
Anciens JAR conservés ; aucune publication distante.
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
# beta.168 — laboratoire de trois cartes de horde
|
||||
|
||||
**Actualisation beta.169 :** les [nouvelles cartes à invasion continue](horde-invasion-beta169.md)
|
||||
se révèlent en main, mélangent les espèces et accélèrent leurs apparitions. Les
|
||||
cartes beta.168 conservent les règles historiques décrites ci-dessous.
|
||||
|
||||
Suite du [prototype beta.167](horde-lab-beta167.md), branche
|
||||
`codex/cartes-horde-beta168`. Minecraft 26.3, dépendances inchangées.
|
||||
|
||||
## Décision de séance
|
||||
|
||||
Le décor circulaire est conservé comme piste pour de futurs donjons. Le nouveau
|
||||
labo le reproduit **sans socle** : c'est la carte consommable qui invoque la horde
|
||||
à la position du joueur. Apparition immédiate, sans inscription, préparation,
|
||||
chef de groupe ni commande pour rejoindre. Les joueurs arrivent et combattent
|
||||
spontanément. Les récompenses sont des objets lâchés par les monstres : ramassage
|
||||
libre, sans attribution, partage automatique ni coffre final.
|
||||
|
||||
## Essai jouable
|
||||
|
||||
Trois cartes natives Minecraft, tenues à deux mains avec la main secondaire vide :
|
||||
|
||||
| Carte | Vagues de zombies | Butins possibles |
|
||||
| --- | --- | --- |
|
||||
| Errants | 3 / 5 / 7 | Fer, émeraude |
|
||||
| Meute | 4 / 6 / 8 | Or, émeraude |
|
||||
| Légion | 6 / 9 / 12, un zombie sur deux casqué | Diamant, émeraude |
|
||||
|
||||
Chaque exemplaire reçoit une graine et une illustration 128 × 128 persistante,
|
||||
verrouillée contre les mises à jour géographiques. Composition libre en palette de carte, sans cadre ni grille, à partir des
|
||||
visages texturés des zombies et des textures natives de butin. Positions continues,
|
||||
tailles et inclinaisons variées. Chaque visage correspond à **un monstre réel**
|
||||
sur les trois vagues : 15, 18 ou 27 visages ; les casques correspondent aux porteurs
|
||||
réels. Chaque icône de butin correspond aussi à un objet porté. La densité et les
|
||||
casques traduisent la difficulté. Ce rendu assemble des visages texturés, sans corps. Les trois familles ont des vagues fixes ; leur graine fait varier
|
||||
l'illustration, le choix des butins et leurs porteurs.
|
||||
|
||||
Au moins un porteur de butin par vague ; les autres ont une chance sur quatre
|
||||
d'en porter. Chaque porteur laisse un seul objet, choisi à parts égales dans la
|
||||
paire annoncée. Les casques ne sont pas du butin. Pas d'XP économique ajoutée.
|
||||
Les butins déjà lâchés restent au sol si la horde est interrompue.
|
||||
|
||||
Les joueurs dans les 15 blocs participent automatiquement ; partir ou mourir
|
||||
ne supprime pas la horde. Les nouveaux arrivants peuvent intervenir pendant
|
||||
n'importe quelle vague. Six secondes entre vagues, trois minutes maximum par
|
||||
vague ; disparition d'un monstre vivant ou sortie d'un monstre du périmètre
|
||||
interrompt et nettoie les survivants sans butin supplémentaire.
|
||||
|
||||
L'emplacement des trois vagues est contrôlé avant consommation : chunks chargés,
|
||||
sol, collisions, absence de fluide et frontière. Une horde voisine bloque une
|
||||
nouvelle invocation. Les cartes copiées gardent la même identité : le registre
|
||||
serveur des identités consommées empêche une seconde invocation. Il utilise la
|
||||
sauvegarde native du monde ; aucune garantie transactionnelle supplémentaire en
|
||||
cas de coupure brutale avant sauvegarde n'est revendiquée dans ce labo.
|
||||
|
||||
## Monde et lancement
|
||||
|
||||
Nouveau dossier `build/horde-168/`, monde `horde-flat-168`, graine **168**, génération
|
||||
de scène **ready-v1**, port **127.0.0.1:25578**. L'ancien laboratoire beta.167 et ses
|
||||
sauvegardes sont conservés. Aucun monde existant n'est converti. L'ancien identifiant
|
||||
`sanctuary:trial_pedestal` et la carte beta.167 restent enregistrés.
|
||||
Les cartes beta.168 sont des cartes natives avec données `sanctuary_horde_v1` ;
|
||||
le nouveau SavedData `sanctuary:horde_spent_v1` ne concerne que leurs consommations.
|
||||
|
||||
```sh
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/horde_lab.py prepare
|
||||
python3 scripts/horde_lab.py server
|
||||
# Autre terminal
|
||||
python3 scripts/horde_lab.py client
|
||||
```
|
||||
|
||||
Trois cartes, équipement et vision nocturne de test à la première connexion. Clic droit dans le vide avec une
|
||||
carte près du centre du cercle. `/horde cartes` redonne trois nouveaux exemplaires
|
||||
hors combat ; `/horde retour` ramène et soigne hors combat. Pour cet essai,
|
||||
l'invocation est volontairement limitée au centre du labo enregistré, et le rayon
|
||||
d'apparition est de neuf blocs autour de l'utilisateur. Aucun usage dans les
|
||||
mondes ordinaires, aucune recette ni distribution économique de ces cartes.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- `check build assemblePack` et parcours client Vulkan réussis : **254/254**
|
||||
GameTests dans `build/horde168-validation.log`. Cette passe précède les derniers
|
||||
ajustements de composition libre et le verrouillage des anciennes commandes du socle.
|
||||
- Rejeu final sur ces ajustements : groupe `horde168`, deux tests serveur requis
|
||||
réussis, parcours client Vulkan sur les trois cartes, puis assemblage du pack :
|
||||
**BUILD SUCCESSFUL**, journal `build/horde168-final.log`. Le rejeu cible exclut
|
||||
`:sanctuary:check`, déjà exécuté dans la passe complète.
|
||||
- Serveur : refus sans consommation sur terrain bloqué, consommation immédiate,
|
||||
trois vagues de chaque difficulté, images déterministes par graine, copie épuisée
|
||||
refusée, anciennes commandes du socle inopérantes sur les cartes, nombre exact
|
||||
d'objets lâchés conforme à l'illustration, nettoyage sans butin en cas d'interruption.
|
||||
- Client réel : tenue native à deux mains, clic droit, consommation, arrivée/départ
|
||||
spontanés sans annulation de la horde, retour sans inscription, trois victoires,
|
||||
butins physiques. Marqueur `HORDE168_CLIENT_PASS` ; six captures dans
|
||||
`build/horde168-evidence/`. Inspection visuelle des cartes sans cadre ajouté.
|
||||
- Préparation du script répétée sans modifier ses fichiers existants ; Python compilé,
|
||||
libellés FR/EN et liens documentaires vérifiés. Nouveau serveur dédié démarré avec
|
||||
marqueur `ready-v1` dans le monde beta.168 ; l'ancien monde beta.167 est préservé.
|
||||
|
||||
L'équilibrage humain reste à essayer : le client automatisé est invulnérable.
|
||||
La suite générale a réussi lors de cette exécution ; cela ne constitue pas une
|
||||
correction du test de familier intermittent signalé en beta.167 (source inchangée).
|
||||
Pas de publication ni de mise à jour Prism pour cet essai.
|
||||
@@ -0,0 +1,87 @@
|
||||
# beta.171 — familles dominantes et chaos dimensionnel
|
||||
|
||||
> La beta.172 retire la restriction au laboratoire et permet de consommer ces
|
||||
> cartes directement en Survie ou en Créatif. Voir
|
||||
> [le contrat runtime beta.172](horde-runtime-beta172.md).
|
||||
|
||||
Suite de la [carte native beta.170](horde-map-native-beta170.md), branche
|
||||
`codex/horde-families-beta171`. Minecraft 26.3, dépendances inchangées.
|
||||
|
||||
## Profil mémorisé par la carte
|
||||
|
||||
La première prise en main fixe, côté serveur, la dimension de découverte et une
|
||||
famille dominante. Cette identité rejoint la difficulté et la graine dans les
|
||||
données de l'objet (`loot_version=3`) : déplacer ou cloner ensuite la carte ne
|
||||
peut pas relancer son tirage.
|
||||
|
||||
Les familles sont zombies, squelettes, creepers, arthropodes, pillards,
|
||||
cauchemars de surface, infernaux, piglins, flétris et créatures de l'End. Dans
|
||||
92 % des tirages, la famille appartient à la dimension de découverte ; une
|
||||
incursion complète étrangère reste donc exceptionnelle.
|
||||
|
||||
Chaque carte conserve au minimum 10/12, 13/18 ou 17/27 positions de sa famille
|
||||
dominante. Les autres positions sont mélangées de façon déterministe : monstres
|
||||
différents mais natifs de la dimension, puis intrus interdimensionnels plus
|
||||
rares. Errants peut ne recevoir aucun intrus, Meute en reçoit un et Légion deux ;
|
||||
les 8 % d'incursions étrangères constituent l'exception spectaculaire.
|
||||
|
||||
Le catalogue contient 34 créatures adaptées à une apparition dans le cercle :
|
||||
variantes de zombies et squelettes, creeper, slime, araignées, silverfish,
|
||||
sorcière, pillards, évocateur, ravageur, breeze, phantom, creaking, blaze,
|
||||
squelette Wither, cubes, piglins, hoglin, zoglin, ghast, enderman, endermite et
|
||||
shulker. Les boss et monstres strictement aquatiques ne sont volontairement pas
|
||||
invoqués : ils demanderaient un contrat d'arène différent. Piglins et hoglins
|
||||
de carte ne se transforment pas hors du Nether.
|
||||
|
||||
## Carte et récompenses
|
||||
|
||||
L'illustration 128 × 128 est divisée horizontalement. En haut, une piste en
|
||||
serpentin montre chaque monstre dans l'ordre exact où il jaillira. En bas, toutes
|
||||
les piles garanties sont affichées sans hiérarchie ; elles peuvent légèrement
|
||||
se chevaucher quand la récompense est dense.
|
||||
|
||||
Chaque espèce apporte une ressource cohérente et généreuse : poudre pour les
|
||||
creepers, bâtons de blaze, larmes de ghast, perles de l'End, carapaces de shulker,
|
||||
or des piglins, émeraudes des pillards, etc. Rubis et saphirs Sanctuary restent
|
||||
garantis sur chaque carte. Les butins natifs peuvent encore s'ajouter.
|
||||
|
||||
Les cartes beta.168 restent à trois vagues de zombies. Les cartes beta.169/170
|
||||
déjà découvertes gardent leur roster version 2. Seules les nouvelles cartes
|
||||
beta.171 emploient le profil dimensionnel version 3.
|
||||
|
||||
## Communication et essai
|
||||
|
||||
Aucun parcours Horde, y compris le socle beta.167, n'envoie désormais de texte
|
||||
dans le chat ou la barre d'action. Découverte, refus, brèche, arrivées,
|
||||
progression, morts et résultat passent par les connexions et glyphes de
|
||||
particules natifs décrits en beta.170.
|
||||
|
||||
Importer `Sanctuary-Test-beta.171.mrpack` dans Prism et créer un **nouveau** monde
|
||||
nommé exactement `horde-flat-168`. Le profil construit le cercle et donne trois
|
||||
cartes. Pour un tirage neuf, prendre « Carte de horde inconnue » dans **Outils et
|
||||
utilitaires**, la tenir en main, revenir en survie puis l'utiliser dans le cercle.
|
||||
|
||||
La distribution depuis les campements de survie reste un ticket futur. Ce
|
||||
chantier ne modifie aucune génération, aucun chunk, aucune sauvegarde, aucune
|
||||
instance Prism et aucun canal packwiz. L'objet suit le modèle des cartes
|
||||
d'exploration distinctes de Minecraft 26.3, dont la carte des campements
|
||||
abandonnés décrite dans les
|
||||
[notes officielles Minecraft Java 26.3](https://www.minecraft.net/en-us/article/minecraft-java-edition-26-3).
|
||||
|
||||
## Vérifications
|
||||
|
||||
- GameTests `horde171` : majorité garantie, pondération par dimension, intrus
|
||||
possibles, large catalogue, profil versionné, image et récompenses exactes.
|
||||
- Régressions `horde167` à `horde170` : compatibilité des trois anciens contrats.
|
||||
- Parcours client Vulkan : découverte, trois cartes séparées, familles visibles,
|
||||
accélération, particules, morts, victoire et butins physiques.
|
||||
- Matrice complète `./gradlew check build` : 259/259 GameTests requis et
|
||||
compilation complète réussis.
|
||||
- Pack normal : `Sanctuary-beta.171.mrpack`, SHA-256
|
||||
`8f2642adfbcf499330889070b7c05efed09a181b82115e93da7023cdd5aee554`.
|
||||
- Pack laboratoire : `Sanctuary-Test-beta.171.mrpack`, SHA-256
|
||||
`2d64d6b046e265ccee4686aedc98d73d8ec8c012f5b5f6fd64e1a6180adfabbc`.
|
||||
- JAR Sanctuary embarqué dans les deux packs : SHA-256
|
||||
`0622cbd45cb20c763e3db9f351ebf26ea4869a3065655f01f66f97cc9c45d5c1` ;
|
||||
JAR laboratoire :
|
||||
`9c646a95a106680d34f05a4c4d77a9893153c04ab9ad8683f636a99eabc74016`.
|
||||
@@ -0,0 +1,114 @@
|
||||
# beta.169 — cartes de horde à invasion continue
|
||||
|
||||
> La [beta.170](horde-map-native-beta170.md) remplace ces cartes remplies par un
|
||||
> objet-carte Sanctuary natif et remplace les messages par un langage de
|
||||
> particules. La compatibilité avec les exemplaires beta.168/beta.169 demeure.
|
||||
|
||||
Suite du [laboratoire beta.168](horde-cartes-beta168.md), branche
|
||||
`codex/horde-interruption-beta169`. Minecraft 26.3, dépendances inchangées.
|
||||
|
||||
## Résultat jouable
|
||||
|
||||
Une nouvelle carte est **inconnue tant qu'un joueur ne la tient pas**. Son nom,
|
||||
sa difficulté, son contingent et son principe de butin se révèlent alors et
|
||||
restent attachés à cet exemplaire. Le dessin natif 128 × 128 existait déjà mais
|
||||
n'est visible en jeu qu'une fois la carte dépliée en main. Une copie non tenue
|
||||
reste inconnue ; toutes les copies gardent cependant la même identité de
|
||||
consommation.
|
||||
|
||||
Le clic droit consomme la carte et ouvre une invasion à la position du joueur.
|
||||
Il n'y a plus de vague, de pause ni de compte à rebours : le premier monstre
|
||||
jaillit immédiatement, puis les suivants arrivent un par un, chaque fois plus
|
||||
vite. Une victoire est accordée seulement quand tout le contingent est apparu
|
||||
et que tous ses membres ont réellement été vaincus.
|
||||
|
||||
| Carte révélée | Contingent | Espèces | Cadence d'arrivée |
|
||||
| --- | ---: | --- | --- |
|
||||
| Errants · accessible | 12 | zombie, zombie momifié | 2,9 s → 0,9 s |
|
||||
| Meute · périlleuse | 18 | précédents, noyé, squelette, araignée | 2,4 s → 0,5 s |
|
||||
| Légion · féroce | 27 | précédents, vagabond, embourbé, desséché, araignée venimeuse, sorcière, pillard | 2,1 s → 0,3 s |
|
||||
|
||||
La graine de chaque carte décale l'ordre du roster et détermine les quantités de
|
||||
butin et les gemmes bonus. Le dessin montre un œuf pour **chaque monstre réel**
|
||||
et les objets garantis qu'il apportera. Sa densité et sa variété rendent la
|
||||
difficulté lisible après révélation.
|
||||
|
||||
## Butin généreux et lié aux monstres
|
||||
|
||||
Chaque ennemi porte au moins une pile garantie, en plus de ses éventuels butins
|
||||
Minecraft natifs : chair putréfiée, sable, cuivre, flèches, ficelle, glace
|
||||
compacte, champignons, os, yeux d'araignée, redstone/poudre lumineuse ou
|
||||
émeraudes selon son espèce. Les quantités sont volontairement généreuses.
|
||||
|
||||
Le premier et le deuxième monstre garantissent respectivement un rubis et un
|
||||
saphir. Les suivants ont une chance sur cinq d'ajouter une gemme alternée. Les
|
||||
objets sont physiques, sans propriétaire : ils restent au sol, peuvent être
|
||||
ramassés par tous et les gains déjà obtenus survivent à une interruption.
|
||||
|
||||
Chaque apparition conserve la spirale de glyphes SGA natifs et chaque mort réelle
|
||||
disperse les runes avec une impulsion de fumée. Un nettoyage technique ne produit
|
||||
ni rune de mort ni récompense supplémentaire.
|
||||
|
||||
## Combat ouvert et sécurité
|
||||
|
||||
Les joueurs vivants dans les 32 blocs et 16 blocs de hauteur participent
|
||||
automatiquement. Partir, mourir ou revenir n'annule pas l'invasion. Les monstres
|
||||
peuvent dépasser l'ancienne limite du socle sans être supprimés ni compter comme
|
||||
morts. Une disparition réelle d'entité, un chunk central déchargé, un délai total
|
||||
de six minutes ou un futur point d'arrivée devenu impraticable interrompent la
|
||||
session avec un motif distinct.
|
||||
|
||||
Tous les emplacements prévus sont vérifiés avant consommation, sans chargement ni
|
||||
écriture de terrain. Au moment d'une arrivée, douze positions proches sont
|
||||
essayées afin qu'un joueur ou un autre monstre ne bloque pas artificiellement la
|
||||
brèche. Une horde voisine empêche toujours une seconde invocation.
|
||||
|
||||
## Compatibilité et laboratoire
|
||||
|
||||
Les cartes beta.168 sans `loot_version` restent en version 1 : trois vagues de
|
||||
zombies, dessin et récompenses historiques inchangés. Les nouvelles cartes sont
|
||||
en version 2 et utilisent l'invasion continue. Le registre sauvegardé
|
||||
`sanctuary:horde_spent_v1`, les identifiants et le monde
|
||||
`build/horde-168/server/horde-flat-168` sont conservés ; aucune conversion de
|
||||
monde ni régénération de chunk.
|
||||
|
||||
```sh
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/horde_lab.py prepare
|
||||
python3 scripts/horde_lab.py server
|
||||
# Autre terminal
|
||||
python3 scripts/horde_lab.py client
|
||||
```
|
||||
|
||||
`/horde cartes` fournit trois cartes inconnues supplémentaires hors combat.
|
||||
`/horde retour` ramène et soigne hors combat. Le prototype reste limité au centre
|
||||
du labo : aucune recette ou distribution économique n'est encore ajoutée aux
|
||||
mondes ordinaires.
|
||||
|
||||
Pour l'essai le plus simple, importer `Sanctuary-Test-beta.169.mrpack` dans Prism,
|
||||
puis créer un **nouveau** monde nommé exactement `horde-flat-168`. Le profil de
|
||||
test présélectionne **Sanctuary — Test rapide** et reconnaît ce nom pour construire
|
||||
une seule fois le cercle, remettre les cartes et placer le joueur. Un autre nom
|
||||
crée un monde de test plat ordinaire ; le MRpack normal n'active pas ce laboratoire.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- `horde169` : 3/3 GameTests ciblés ; `horde167` et `horde168` restent verts
|
||||
dans la matrice de compatibilité.
|
||||
- Parcours client Vulkan : PASS sur les trois cartes, avec révélation en main,
|
||||
départ/retour, cadence accélérée, roster varié, victoire et butins physiques.
|
||||
- Matrice complète rejouée par l'assemblage : 256/256 GameTests requis, puis
|
||||
`check`, `build`, `assemblePack` et `assembleTestPack` réussis. Une première
|
||||
passe sous forte charge avait fait échouer le déplacement aérien historique
|
||||
`companion019` ; ses 21 tests isolés puis la seconde matrice complète passent.
|
||||
- Pack normal : [Sanctuary-beta.169.mrpack](../build/Sanctuary-beta.169.mrpack),
|
||||
SHA-256 `72fcf169004cc4dc7e1bb3caf72c1e66cc935c79ac16173de9db8ef86d66eaea`.
|
||||
- Pack laboratoire :
|
||||
[Sanctuary-Test-beta.169.mrpack](../build/Sanctuary-Test-beta.169.mrpack),
|
||||
SHA-256 `a162f7e9ce88a177ac99934d4c9a7061ac67be062a31a36965a191dd3eb0423a`.
|
||||
|
||||
Les deux manifestes exportés déclarent `beta.169`, Minecraft 26.3 et Fabric
|
||||
Loader 0.19.5. Le pack laboratoire contient en plus
|
||||
`sanctuary-test-beta.169.jar` ; aucun canal packwiz ni aucune instance Prism n'a
|
||||
été modifié.
|
||||
@@ -0,0 +1,120 @@
|
||||
# beta.167 — prototype de carte de horde et socle d'épreuve
|
||||
|
||||
Branche `codex/communaute-economie-beta167`, socle beta.166. Premier essai issu du
|
||||
[cadrage communautaire](ecosysteme-communaute-economie.md). Minecraft **26.3**,
|
||||
dépendances inchangées ; vérifications graphiques Vulkan uniquement.
|
||||
|
||||
## Contrat de ce prototype
|
||||
|
||||
Une arène ronde de laboratoire, diamètre extérieur 41 blocs, accueille un
|
||||
**socle d'épreuve** au centre (`sanctuary:trial_pedestal`). Une **carte d'épreuve ·
|
||||
Horde de zombies** (`sanctuary:horde_trial_card`) permet d'ouvrir les inscriptions.
|
||||
Ces identifiants sont nouveaux et stables. Le clic droit ouvre les contrôles.
|
||||
|
||||
De un à quatre joueurs s'inscrivent volontairement, passent prêts, puis l'hôte
|
||||
lance. Cinq secondes précèdent la première vague ; six secondes séparent les
|
||||
suivantes. Trois vagues : 4, 6 et 8 zombies en solo ; deux zombies supplémentaires
|
||||
par coéquipier et par vague. Chaque vague dispose de trois minutes. L'effectif
|
||||
est fixé au départ, sans renfort tardif. Tous les zombies doivent être vaincus.
|
||||
|
||||
Le socle passe du vert au bleu pendant l'attente, rouge pendant le combat, or
|
||||
après victoire, sombre après interruption. Un affichage indique la vague et les
|
||||
ennemis restants. La carte du prototype reste réutilisable : **aucun butin ni XP
|
||||
de récompense** n'est distribué. Les statistiques natives de combat continuent
|
||||
d'exister. Ni capes, ni familiers exclusifs, ni incubation d'XP ne sont livrés.
|
||||
|
||||
Quitter le rayon de 15 blocs, mourir, se déconnecter ou passer en créatif/spectateur
|
||||
retire de l'épreuve. Les autres participants peuvent continuer. Le départ de
|
||||
l'hôte avant lancement annule les inscriptions. Un groupe vide, un délai écoulé,
|
||||
une difficulté Paisible ou la disparition d'un ennemi vivant interrompt l'épreuve.
|
||||
Un ennemi disparu n'est jamais compté comme une victoire. Les zombies de test ne
|
||||
brûlent pas au soleil, ne ramassent pas d'objets et ne frappent que les participants.
|
||||
Le laboratoire désactive le PvP et conserve l'inventaire à la mort.
|
||||
|
||||
## Données, interruption et limites
|
||||
|
||||
Les sessions sont volontairement éphémères. Un arrêt interrompt la partie ; les
|
||||
zombies de l'épreuve sont retirés. Après un crash, leurs tags permettent de retirer
|
||||
les survivants au chargement. Aucune dette, récompense différée ou carte consommée
|
||||
n'existe à récupérer. Le socle est réarmé à la réouverture du labo.
|
||||
|
||||
Le socle et la carte n'ont pas de recette ni de génération naturelle. Un socle
|
||||
posé hors de la scène explicitement préparée ne lance rien. Le client ne peut
|
||||
ni installer une arène, ni choisir ses monstres, ni charger une région par paquet.
|
||||
Le serveur contrôle identité de session, révision, distance, inscription et état.
|
||||
|
||||
Le seul nouveau marqueur de scène est `horde-lab-167.txt`, dans le nouveau monde
|
||||
de développement : préparation réservée avant construction, état prêt après.
|
||||
Une préparation incomplète ou un socle manquant est conservé et refusé au
|
||||
redémarrage. Le volume entier doit être vide avant la première pose. Aucune
|
||||
ancienne sauvegarde, aucun chunk Sanctuary ni format de données existant n'est
|
||||
converti. Ne pas installer les nouveaux blocs dans un monde destiné à être
|
||||
rouvert avec beta.166 ; conserver le nouveau labo pour ce prototype.
|
||||
|
||||
Il reste à éprouver le rythme et la coopération avec des joueurs humains. Les
|
||||
cartes de découverte, clés/reliques, cartes collectionnables, quêtes quotidiennes,
|
||||
échanges, ballast et huit extensions restent de la conception.
|
||||
|
||||
## Essayer le laboratoire
|
||||
|
||||
```sh
|
||||
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/horde_lab.py prepare
|
||||
python3 scripts/horde_lab.py server
|
||||
# Dans un autre terminal :
|
||||
python3 scripts/horde_lab.py client
|
||||
```
|
||||
|
||||
Dossier neuf `build/horde-167/`, monde `horde-flat-167`, graine **167**, port
|
||||
loopback **25576**. Le script reprend seulement les arguments de lancement exportés,
|
||||
pas les sauvegardes ni paramètres du labo duo. Chaque connexion rejoint le cercle
|
||||
en aventure ; un équipement initial et des capacités de combat 10 cœurs/10 icônes
|
||||
de faim sont fournis à ce personnage de test. Hors épreuve, `/horde retour` ramène
|
||||
et soigne. L'ancien `build/duo/` et les installations personnelles sont préservés.
|
||||
|
||||
À l'accueil, créer son personnage si demandé. Au socle : **Présenter ma carte**,
|
||||
**Prêt**, puis **Lancer l'épreuve**. Les amis rejoignent et passent prêts depuis
|
||||
le même socle. Ce profil local hors ligne ne valide pas l'identité Discord et ne
|
||||
doit pas être exposé sur Internet.
|
||||
|
||||
## Vérifications et livraison
|
||||
|
||||
- `./gradlew check build assemblePack :sanctuary-test:exportDuoLaunch` exécuté :
|
||||
**252/253 GameTests réussis**, dont le scénario de horde. Le contrôle général
|
||||
échoue sur `Companion019GameTests.creatureProfilesActuallyMove` (déplacement
|
||||
aérien attendu). Journal : `build/horde167-full.log`.
|
||||
- Rejeu `check build` avec les groupes `companions,horde167` : **21/22 tests**,
|
||||
même échec de déplacement, horde réussie. Le code et le scénario de familier
|
||||
sont inchangés par ce chantier ; aucune conclusion de non-régression complète
|
||||
n'est revendiquée. L'[audit antérieur](audit-dragon-beta165.md) décrit notamment
|
||||
l'aléa du scénario. Journal : `build/horde167-focused-client.log`.
|
||||
- Validation finale ciblée : `runGameTest runClientGameTest assemblePack
|
||||
:sanctuary-test:exportDuoLaunch`, groupe `horde167`, client Horde167/Vulkan,
|
||||
avec `-x :sanctuary:check` pour ne pas rejouer les contrôles précédents.
|
||||
**BUILD SUCCESSFUL**, deux tests serveur requis réussis (dont le scénario
|
||||
horde), puis parcours client complet. Cette exclusion ne transforme pas le
|
||||
contrôle général précédent en réussite. Journal : `build/horde167-delivery.log`.
|
||||
- Serveur : deux personnages simulés, carte requise, consentement de chacun,
|
||||
lancement réservé à l'hôte, double lancement refusé, trois vagues adaptées
|
||||
à l'effectif, victoire, carte conservée, nettoyage à la sortie et interruption
|
||||
sur disparition d'un ennemi vivant. Une scène existante est refusée sans
|
||||
réécriture par le constructeur du labo.
|
||||
- Client natif Vulkan : clic droit réel sur le bloc, paquets d'inscription,
|
||||
préparation et lancement, trois vagues avec dégâts serveur natifs, victoire
|
||||
après 18 zombies en solo. Captures FR/EN, GUI 2/3 ; inspection visuelle de
|
||||
l'arène, du socle, des contrôles, du combat et du résultat. Marqueur
|
||||
`HORDE167_CLIENT_PASS`. Huit captures conservées dans `build/horde167-evidence/`.
|
||||
- Script Python compilé ; préparation répétée avec **cinq fichiers identiques**.
|
||||
Libellés FR/EN et liens locaux vérifiés. Démarrage dédié réel sur loopback,
|
||||
création de la nouvelle scène puis arrêt propre, journal
|
||||
`build/horde167-lab-server.log`. Redémarrage dédié réussi avec le marqueur
|
||||
`ready-v1` et le socle existant, sans reconstruire la scène ; journal
|
||||
`build/horde167-lab-restart.log`.
|
||||
- Packwiz assemblé et vérifié ; JAR interne beta.167 avec classes et ressources
|
||||
attendues. SHA-256 du JAR :
|
||||
`4d6cb640177d7904043c3eefe693a75fbdc4f4df48407adaae04cc7d632057e6`.
|
||||
|
||||
La coopération entre plusieurs clients humains et l'équilibrage restent à
|
||||
éprouver ; les personnages simulés ne les remplacent pas. Aucun artefact public,
|
||||
tag, canal packwiz ou instance Prism mis à jour. Prototype intégré localement sur `main`, conformément à la demande du créateur.
|
||||
@@ -0,0 +1,102 @@
|
||||
# beta.170 — carte de horde native et langage de particules
|
||||
|
||||
> La [beta.171](horde-families-beta171.md) ordonne les monstres et les butins sur
|
||||
> la carte, ajoute les familles dominantes et étend le silence visuel au socle.
|
||||
|
||||
Suite de l'[invasion continue beta.169](horde-invasion-beta169.md), branche
|
||||
`codex/horde-map-native-beta170`. Minecraft 26.3, dépendances inchangées.
|
||||
|
||||
## Objet et découverte
|
||||
|
||||
`sanctuary:horde_trial_card` est désormais une sous-classe de la carte native
|
||||
Minecraft, au lieu d'un objet ordinaire ou d'une carte remplie renommée. Son
|
||||
identifiant reste stable. Un exemplaire vierge est disponible dans l'onglet
|
||||
créatif **Outils et utilitaires** sous le nom « Carte de horde inconnue ».
|
||||
|
||||
Le premier passage en main lui attribue côté serveur :
|
||||
|
||||
- un identifiant de carte natif distinct ;
|
||||
- une difficulté parmi Errants, Meute et Légion ;
|
||||
- une graine propre, donc un ordre de monstres et des butins propres ;
|
||||
- une illustration verrouillée 128 × 128 correspondant au contenu réel.
|
||||
|
||||
Deux nouveaux exemplaires vierges découvrent deux identités différentes. Ranger
|
||||
puis reprendre le même exemplaire ne le relance jamais : son identité, son dessin
|
||||
et sa difficulté restent stables. Cette distinction évite de choisir sa
|
||||
difficulté en changeant simplement d'emplacement d'inventaire. Si plusieurs
|
||||
cartes créatives vierges sont empilées, seule celle tenue se découvre ; les
|
||||
autres retournent vierges dans l'inventaire et attendent leur propre prise en main.
|
||||
|
||||
La carte appartient à `#minecraft:clonable_maps`. La recette native 26.3 avec
|
||||
des cartes vierges peut donc dupliquer un exemplaire déjà découvert ; les copies
|
||||
conservent volontairement la même identité et la même consommation, comme les
|
||||
cartes d'exploration natives. Les cartes remplies beta.168 et beta.169 restent
|
||||
reconnues et jouables.
|
||||
|
||||
Cette approche s'inspire des cartes d'exploration devenues des objets distincts
|
||||
en 26.3, et notamment de la nouvelle carte des campements abandonnés. Sanctuary
|
||||
fait un choix différent sur un point : sa carte inconnue est volontairement
|
||||
accessible dans l'inventaire créatif pour le laboratoire. Voir les
|
||||
[notes officielles Minecraft Java 26.3](https://www.minecraft.net/en-us/article/minecraft-java-edition-26-3).
|
||||
|
||||
## Langage visuel sans chat
|
||||
|
||||
Le parcours des cartes de horde n'envoie plus de message Sanctuary dans le chat
|
||||
ni dans la barre d'action. Toute l'information immédiate est spatiale :
|
||||
|
||||
| Moment | Signal visuel |
|
||||
| --- | --- |
|
||||
| Découverte | 1, 2 ou 3 connexions convergent vers la carte selon la difficulté, avec glyphes SGA |
|
||||
| Activation valide | un anneau de connexions converge vers la brèche centrale |
|
||||
| Arrivée | la brèche se relie au monstre qui jaillit, entouré de glyphes |
|
||||
| Invasion en cours | une pulsation relie chaque monstre vivant à la brèche toutes les secondes |
|
||||
| Mort réelle | les glyphes se dispersent avec une brève impulsion |
|
||||
| Victoire | douze connexions convergent avec une explosion de totems |
|
||||
| Interruption | les connexions se dispersent avec une fumée sombre |
|
||||
| Usage impossible | un filet de fumée descend de la carte vers le joueur |
|
||||
|
||||
Les connexions utilisent la particule native `minecraft:vault_connection` ; les
|
||||
glyphes restent ceux de `minecraft:enchant`, donc l'alphabet SGA fourni par le
|
||||
jeu. Aucune texture, interface ou canal réseau de HUD propre à la carte n'est
|
||||
nécessaire.
|
||||
|
||||
Les messages du vieux socle beta.167 sont conservés uniquement pour ce prototype
|
||||
historique. Ils ne font pas partie du parcours de la carte native.
|
||||
|
||||
## Essai dans le laboratoire
|
||||
|
||||
Importer `Sanctuary-Test-beta.170.mrpack` dans Prism, puis créer un **nouveau**
|
||||
monde nommé exactement `horde-flat-168`. Le profil de test construit le cercle
|
||||
et fournit les trois cartes déterministes de démonstration. Pour vérifier la
|
||||
découverte aléatoire, prendre « Carte de horde inconnue » dans **Outils et
|
||||
utilitaires**, la tenir en main, revenir en survie puis l'utiliser dans le cercle.
|
||||
|
||||
Le MRpack normal contient le même objet, mais la distribution en survie depuis
|
||||
les campements n'est pas encore activée. Ce chantier adopte leur modèle d'objet
|
||||
et de découverte sans modifier la génération, les chunks ou les sauvegardes.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- GameTests `horde170` : type natif, entrée créative vierge, découverte unique,
|
||||
stabilité à la reprise, diversité entre deux exemplaires, illustration et
|
||||
compatibilité avec la recette native de duplication.
|
||||
- Parcours client Vulkan : présence dans Outils et utilitaires, découverte en
|
||||
main, trois difficultés, accélération continue, signaux de particules, victoire
|
||||
et butins ; aucun message de jeu Sanctuary émis par les cartes.
|
||||
- Matrice complète : 257/257 GameTests requis, puis `check build` réussi.
|
||||
- `assemblePack assembleTestPack` réussi après cette matrice ; intégrité ZIP,
|
||||
version `beta.170`, Minecraft 26.3, Fabric Loader 0.19.5 et JAR embarqués
|
||||
vérifiés.
|
||||
- Pack normal : [Sanctuary-beta.170.mrpack](../build/Sanctuary-beta.170.mrpack),
|
||||
SHA-256 `e96ad55115074e898e94c3eccff983bd76301b0aa577c83b8a5e3feca0a719f8`.
|
||||
- Pack laboratoire :
|
||||
[Sanctuary-Test-beta.170.mrpack](../build/Sanctuary-Test-beta.170.mrpack),
|
||||
SHA-256 `d84aa95c230b6484c9282534245401de0dcf069e9d4e5c9d5e7d6238bb2f84e0`.
|
||||
|
||||
Le JAR Sanctuary embarqué correspond au build vérifié, SHA-256
|
||||
`c8cd436bdc65c8f97ea6383f6f4ae63836b4b6e7dbf35918b2be86152e38d344`.
|
||||
Le profil laboratoire contient en plus `sanctuary-test-beta.170.jar`, SHA-256
|
||||
`ddb1023285a39f47d4fccd8377aa4ec297f41b9750eab3f6fe2868abcb62da50`.
|
||||
|
||||
Aucun canal packwiz, serveur personnel, monde existant ou instance Prism n'est
|
||||
modifié par cette livraison locale.
|
||||
@@ -0,0 +1,46 @@
|
||||
# beta.172 — cartes de horde utilisables en jeu
|
||||
|
||||
Suite du profil dimensionnel [beta.171](horde-families-beta171.md). Le contenu
|
||||
des cartes, leurs familles et leur format sauvegardé ne changent pas.
|
||||
|
||||
## Contrat runtime
|
||||
|
||||
Le clic droit sur une carte révélée tente d'ouvrir la brèche à la position
|
||||
actuelle du joueur. Aucun cercle, socle, monde de test ou enregistrement de
|
||||
laboratoire n'est requis. Le pouvoir fonctionne en Survie et en Créatif ; le
|
||||
mode Spectateur et la difficulté Paisible restent refusés.
|
||||
|
||||
Après validation de toutes les apparitions, l'identité de carte est marquée
|
||||
comme dépensée et un exemplaire est retiré de la main côté serveur. L'inventaire
|
||||
est immédiatement resynchronisé, y compris en Créatif. Une copie de la même
|
||||
carte ne peut donc pas rejouer l'invasion. Si la préparation échoue, la carte
|
||||
reste intacte.
|
||||
|
||||
L'invasion n'écrit aucun bloc. Chaque arrivée essaie plusieurs angles autour du
|
||||
joueur et cherche un sol libre entre six blocs sous et six blocs au-dessus de la
|
||||
hauteur d'activation. Aucun chunk supplémentaire n'est chargé. Une zone trop
|
||||
encombrée, liquide, sans sol ou hors frontière refuse proprement l'ouverture par
|
||||
le glyphe de particules existant.
|
||||
|
||||
Les joueurs créatifs présents dans le rayon restent associés à l'invasion de
|
||||
carte. Leur invulnérabilité Vanilla n'est pas modifiée : la brèche demeure
|
||||
dangereuse pour les joueurs en Survie, les créatures et l'environnement alentour.
|
||||
Le vieux socle beta.167 conserve son inscription réservée aux joueurs en Survie.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- GameTests `horde172` : activation et consommation hors laboratoire en Créatif
|
||||
puis en Survie ; centre de session égal à la position du joueur.
|
||||
- Terrain étagé : préparation, consommation et première apparition à la hauteur
|
||||
du sol local, sans plancher de laboratoire.
|
||||
- Régressions `horde167` à `horde172` : 10/10 GameTests requis.
|
||||
- Matrice complète `./gradlew check build` : 261/261 GameTests requis et
|
||||
compilation complète réussis.
|
||||
- Pack normal `Sanctuary-beta.172.mrpack` : SHA-256
|
||||
`9e0cc6f07195b094963c03bce52c971ad7072990ac7976dff34f7647be176d7e`.
|
||||
- Pack laboratoire `Sanctuary-Test-beta.172.mrpack` : SHA-256
|
||||
`60565cd9bf28708f398a32da38f1fce05789a1d6bfc513aab94cfe90932d82df`.
|
||||
- JAR Sanctuary embarqué dans les deux packs : SHA-256
|
||||
`fb72d4d3efcd50d3ba67b0899faf9ffca5c2451150c5f169e2a0d030c9b6d7ae` ;
|
||||
JAR laboratoire :
|
||||
`67a7399372f05e384a03461c45730edc0459cbe342cf57bcb280c5c849086620`.
|
||||
@@ -0,0 +1,68 @@
|
||||
# RENDER-149 — Distances PBR et SSR indépendantes
|
||||
|
||||
Branche `codex/separate-reflection-distances-beta149`, socle beta.148 `6f87144`.
|
||||
|
||||
- Deux réglages : Distance PBR et Distance SSR, chacun à 64 blocs par défaut.
|
||||
Choix 16, 32, 64, 128, 256 blocs, enregistrés séparément.
|
||||
- Le PBR conserve son propre fondu et sa propre limite de portée. Le SSR
|
||||
utilise sa distance pour les surfaces réfléchissantes et sa recherche de
|
||||
paysage. Le maillage et la sélection des surfaces couvrent le maximum des
|
||||
deux portées actives, afin que réduire l’une ne coupe pas l’autre.
|
||||
- Ancien défaut commun 32 migré une fois vers PBR 64 ; nouveau réglage SSR 64.
|
||||
Les anciennes valeurs PBR différentes de 32 restent conservées. Après la
|
||||
migration, un choix explicite 32 est conservé comme les autres valeurs.
|
||||
- Option nommée exactement « SSR » en français et en anglais, sans la mention
|
||||
« expérimental ». Les autres options expérimentales du jeu sont inchangées.
|
||||
- Intensités PBR 50 % et SSR 20 %, rendu solaire diffus de beta.148 conservés.
|
||||
SSR conserve sa dépendance au PBR et les limites du paysage visible/chargé
|
||||
et du budget de maillage.
|
||||
|
||||
- Les feuilles (et fibres de laine, même profil entièrement mat) ne reçoivent
|
||||
plus de large éclat spéculaire blanc. Leur relief normal reste présent.
|
||||
La pierre et le bois retrouvent exactement leur poids spéculaire précédent,
|
||||
comme les surfaces polies, les métaux, l’eau et le verre. La suppression
|
||||
concerne les profils non métalliques de rugosité 0,98, avec transition 0,90–0,98.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Contrôles de persistance indépendante, valeurs par défaut et libellés FR/EN.
|
||||
Scènes graphiques avec PBR variable/SSR 256 puis PBR 256/SSR variable.
|
||||
- Suite PBR/préférences Vulkan réussie : deux défauts 64 et persistance
|
||||
indépendante sur 16/32/64/128/256 ; libellés FR/EN et nom SSR vérifiés.
|
||||
Journal : `build/beta149-pbr-vulkan.log`.
|
||||
- Suites SSR Vulkan et OpenGL réussies avec les portées dissociées, F5,
|
||||
intersections, eau/verre/métal, soleil/lune, OFF/zéro et rechargement.
|
||||
Journaux : `build/beta149-ssr-vulkan.log`, `build/beta149-ssr-opengl.log`.
|
||||
Ces passes précèdent l’ajustement final du profil entièrement mat.
|
||||
- Suite PBR Vulkan finale avec feuillage réussie : 170 172 pixels de feuilles
|
||||
contrôlés, saturation relative 1,00007, relief visible sur 63 236 pixels.
|
||||
Journal : `build/beta149-foliage-vulkan.log` ; captures correspondantes
|
||||
dans `build/evidence/beta149-foliage-vulkan/`.
|
||||
- Bois et briques après recentrage sur le feuillage : aucun pixel modifié
|
||||
par rapport à la capture précédant l’ajustement, sur les zones de contrôle
|
||||
de 26 304 et 40 896 pixels respectivement.
|
||||
- `./gradlew check build` exécuté dans l’instantané isolé : 252 tests serveur,
|
||||
229 réussis et 23 échecs, mêmes identifiants qu’en beta.148. Aucun nouvel échec.
|
||||
Journal : `build/beta149-check-build-isolated.log` ; comparaison :
|
||||
`build/beta149-server-failures.json`. L’ajustement final du feuillage concerne
|
||||
seulement le shader et son test client. Le contrôle global reste en échec.
|
||||
|
||||
- Suite PBR OpenGL finale réussie également : 170 199 pixels de feuilles,
|
||||
saturation relative 1,00001, relief visible sur 63 263 pixels.
|
||||
Journal : `build/beta149-foliage-opengl.log` ; captures correspondantes
|
||||
dans `build/evidence/beta149-foliage-opengl/`.
|
||||
- Assemblage final réussi : `./gradlew build assemblePack -x :sanctuary:check`,
|
||||
sans répéter les contrôles purs/serveur exécutés dans l’instantané.
|
||||
Journal : `build/beta149-build-pack.log`. Cette exclusion ne transforme
|
||||
pas le contrôle complet en réussite.
|
||||
|
||||
## Artefact
|
||||
|
||||
[Sanctuary-beta.149.mrpack](../build/Sanctuary-beta.149.mrpack), copié aussi dans
|
||||
`sanctuary-beta/build/` avec les autres versions. Archive vérifiée, un seul JAR
|
||||
Sanctuary beta.149 identique au JAR construit, shaders identiques aux sources,
|
||||
libellés SSR et deux portées FR/EN vérifiés. Minecraft 26.3 / Loader 0.19.5.
|
||||
Taille : 10 520 465 octets. SHA-256 :
|
||||
`ef5519d9a0e2006b865b62371a6f8712db02ab138aa9698339d6fdb02fb4befa`.
|
||||
Reçu : `build/beta149-artifacts.json`. Aucune installation Prism ni publication.
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# beta.166 — inscription web et reprise de l’accueil
|
||||
|
||||
Intégration de la PR #1 de Chris (`8de569b`), avec son historique conservé par
|
||||
le merge `ad960ef`, sur beta.165 et ses 252 GameTests actualisés.
|
||||
|
||||
## Parcours
|
||||
|
||||
Le joueur ouvre son dossier `/candidature` sur le site, se connecte à Discord,
|
||||
dépose sa candidature et récupère le code après acceptation/parrainage. Le
|
||||
bouton **Récupérer mon code** de Hello World ouvre ce dossier, sur le site
|
||||
configuré par le serveur, avec la confirmation de lien native de Minecraft.
|
||||
Le code reste obligatoire avant la création du personnage. Gazette et tableau
|
||||
restent en consultation seule sur le site.
|
||||
|
||||
Sans configuration d’accès, le mod autonome garde l’accueil sans code.
|
||||
Avec une configuration partielle/invalide, l’accueil reste fermé. Un habitant
|
||||
déjà arrivé n’a pas à présenter de code ni à connaître le nouveau payload d’accès.
|
||||
Les autres contrôles de compatibilité Sanctuary restent appliqués.
|
||||
|
||||
## Contrat API de reprise
|
||||
|
||||
Les POST authentifiés `/api/v1/access-codes/verify` et `/redeem` reçoivent :
|
||||
`code`, `minecraft_username` et `admission_id` (UUID). Le serveur calcule cette
|
||||
admission à partir d’un identifiant de monde persistant et de l’UUID du joueur ;
|
||||
le client ne la choisit jamais. Un autre joueur ou un autre monde possède une
|
||||
admission différente, même à graine identique.
|
||||
|
||||
La consommation en base conserve `used_at` et `admission_id` dans la même
|
||||
transaction verrouillée. Une répétition de la même admission et du même pseudo
|
||||
retourne `valid: true, reason: resumed`, le pseudo, le Discord et l’admission.
|
||||
`verify` accepte aussi cette reprise ; le bouton de confirmation redevient
|
||||
accessible. Une autre admission retourne `already_used` sans identité. Un pseudo
|
||||
incorrect ou un code révoqué reste refusé. Les codes anciennement consommés sans
|
||||
reçu restent refusés ; leur récupération nécessite une réémission par le staff.
|
||||
|
||||
Le mod exige une réponse positive portant son admission exacte. Il n’accepte
|
||||
plus `already_used` sur la seule présence d’un pseudo et d’un Discord. Une
|
||||
réponse positive arrivée après déconnexion est quand même enregistrée, mais ne
|
||||
crée pas le personnage hors connexion. Si la réponse est entièrement perdue,
|
||||
le même code est vérifiable et consommable à nouveau au prochain accueil.
|
||||
Le reçu local sauvegardé permet de terminer une création déjà autorisée.
|
||||
Les erreurs réseau et de jeton proposent une nouvelle vérification.
|
||||
|
||||
## Migration et activation
|
||||
|
||||
1. Sauvegarder la base web et les fichiers de progression du serveur.
|
||||
2. Appliquer la migration additive web `add_admission_receipt_to_access_codes_table`
|
||||
(colonne UUID nullable, aucune donnée supprimée), puis déployer le service API.
|
||||
3. Installer beta.166 sur serveur et clients des nouveaux arrivants.
|
||||
4. Configurer `SANCTUARY_APP_URL` et `SANCTUARY_API_TOKEN`, ou
|
||||
`config/sanctuary/access.json` (`appUrl`, `token`). Le jeton reste serveur.
|
||||
Utiliser HTTPS hors du labo local et un serveur Minecraft en online-mode pour
|
||||
authentifier les UUID ; le labo offline ne prouve pas l’identité d’un compte.
|
||||
|
||||
Dans le monde, `data/sanctuary-admission-id` est créé atomiquement avant tout
|
||||
appel API et doit être sauvegardé avec `data/sanctuary-access.json`. Aucun code
|
||||
brut ni jeton n’est enregistré dans ces fichiers. Leur corruption ferme l’accès
|
||||
et conserve les preuves. Les formats habitants/starter et les chunks restent
|
||||
inchangés. Ne pas supprimer l’identifiant d’admission pour « réparer » un accès.
|
||||
|
||||
Retour arrière : conserver la colonne web et les reçus, désactiver explicitement
|
||||
l’accès si l’on revient à un ancien mod (cela réouvre l’accueil sans code).
|
||||
Ne pas exécuter le rollback de migration après consommation : il supprimerait
|
||||
les reçus nécessaires à la reprise. Pas de déploiement public implicite.
|
||||
|
||||
## Laboratoire
|
||||
|
||||
Le monde beta.160 existant est conservé. La reprise de l’introduction utilise
|
||||
un nouveau monde de développement ; les réglages graphiques et anciens JAR sont
|
||||
conservés. `scripts/duo_lab.py server --intro` joue la vraie introduction sur le
|
||||
monde plat ; sans cette option, le profil de test continue de la sauter.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- `check build assemblePack` réussit (10 min 12 s), avec **252/252 GameTests**.
|
||||
Trace : `build/access166-full.log`.
|
||||
- Smoke du contrat et du ledger : reprise, mauvais compte, mauvais reçu, absence
|
||||
de reçu, isolation entre mondes de même graine, persistance et corruption.
|
||||
- `access166Interop` réussit contre la vraie API PHP/MariaDB locale : réponse de
|
||||
consommation ignorée, nouveau ledger, nouvelle vérification et confirmation,
|
||||
refus d’un autre joueur et d’une autre admission (`build/access166-interop.log`).
|
||||
- Test client Vulkan : vrai handshake Hello World, endpoint HTTP contrôlé qui
|
||||
consomme puis répond 503, nouvelle vérification, confirmation et un seul reçu
|
||||
bound. Ce test n’utilise pas Discord et ne prétend pas vérifier OAuth.
|
||||
- Site : 12 tests inscription/candidature (68 assertions), puis 31 tests ciblés
|
||||
inscription/redirection Discord/communauté (258 assertions), tous réussis.
|
||||
Pint passe. Suite web entière : 74 réussis, 1 ignoré et 1 échec préexistant
|
||||
`PlayerModerationTest::test_an_administrator_can_open_a_player_profile`
|
||||
(ancien libellé « Nommer admin »). Reproduit dans un instantané de HEAD avant
|
||||
modification ; pas une régression de l’inscription.
|
||||
- Base locale sauvegardée avant la migration, puis migration additive appliquée.
|
||||
Le vrai OAuth Discord reste à valider avec les identifiants de l’application ;
|
||||
aucun contournement d’authentification ou compte de démonstration pour le joueur
|
||||
n’est installé. Un compte API jetable distinct sert uniquement à l’interop.
|
||||
- Aucun déploiement sur sanctuary-minecraft.net, aucun push/fusion distante,
|
||||
aucune modification de l’ancienne sauvegarde du labo ni des JAR archivés.
|
||||
|
||||
Après la passe complète, le retour immédiat de la vérification en cache a été
|
||||
ajusté pour qu’une nouvelle tentative ne reste pas sans réponse pendant la
|
||||
limite d’une seconde. Le scénario client exerce précisément cette reprise.
|
||||
`check build assemblePack` a ensuite réussi avec les 6 GameTests de progression
|
||||
ciblés (`build/access166-final.log`). La disposition compacte a été resserrée
|
||||
pour garder tous les contrôles dans un GUI de 240 pixels de haut ; ses captures
|
||||
FR/EN et assertions de limites sont vérifiées par le scénario client.
|
||||
|
||||
Le correctif web est enregistré dans `f36cc36`. Les identifiants OAuth fournis
|
||||
par l’utilisateur sont chargés depuis le `.env` privé et la redirection réelle
|
||||
vers Discord fonctionne avec le callback local. Le retour authentifié reste
|
||||
à confirmer après la connexion/autorisation de l’utilisateur.
|
||||
|
||||
La livraison finale et les captures compactes passent dans
|
||||
`build/access166-delivery.log` : scénario client Vulkan FR/EN et assemblage du
|
||||
pack. Cette dernière commande exclut `:sanctuary:check` pour éviter une troisième
|
||||
exécution générale après les deux contrôles réussis ci-dessus ; elle ne remplace
|
||||
pas ces résultats. La seule modification de jeu depuis le contrôle ciblé est
|
||||
la disposition compacte, couverte par ce dernier test client.
|
||||
|
||||
Le labo reste arrêté, configuré pour `duo-registration-166`. L'ancien
|
||||
`duo-flat-160` n'est pas modifié. Lancement prévu après connexion au dossier web :
|
||||
`python3 scripts/duo_lab.py server --memory 768 --intro`, puis le client habituel.
|
||||
@@ -0,0 +1,197 @@
|
||||
# WG-ECO-176 — strates, biomes et mares du laboratoire
|
||||
|
||||
Branche `codex/island-ecology-beta176`, base de visite beta.175 conservée au
|
||||
commit `ba4cf19`. Le créateur autorise l'essai décrit dans
|
||||
[les retours de visite](worldgen-retours-beta175.md).
|
||||
|
||||
## Contrat avant implémentation
|
||||
|
||||
Nouveau preset `sanctuary_test:ecology_v1`, réglage `ecology_v1_10`, graine
|
||||
initiale 42 puis témoins 0 et 173, diamètre 724. Relief brut identique à
|
||||
`sky_v1` ; matériaux, biomes, décorations et petites excavations des mares
|
||||
peuvent différer. Limite du ciel réservé : aucun bloc généré de 512 à 639.
|
||||
Les presets historiques et la génération normale restent inchangés.
|
||||
|
||||
- Géologie en couches ondulées de pierre, andésite, tuf, deepslate et touches
|
||||
de calcite. Conserver le diamant profond et enfoui.
|
||||
- Grandes régions automnales et de cerisiers, lisières et prairie/forêt.
|
||||
- Trois récifs avec identité humide/tropicale ; deux fragments supérieurs
|
||||
avec végétation courte. Le sélecteur sait dépasser l'ancienne limite 384.
|
||||
- Mares locales retenues, nombre d'essais borné, écriture dans le chunk
|
||||
propriétaire, sans demande de plan hydrologique régional.
|
||||
- Biomes propres au labo avec décorations sélectionnées : pas de structures
|
||||
natives, notamment mineshafts et villages. Aucun village Sanctuary créé
|
||||
dans cet essai ; aucun nouveau contenu d'expansion.
|
||||
|
||||
Aucune migration : nouveaux mondes de test seulement. La sauvegarde de
|
||||
visite beta.175 est conservée et ne doit pas être ouverte avec une autre
|
||||
empreinte de génération. Le nouveau monde peut être copié après arrêt
|
||||
propre vers un solo avec commandes et rendu 32 ; conserver l'original et
|
||||
les identifiants de génération. Aucun déploiement Prism ni publication.
|
||||
|
||||
## Vérification prévue
|
||||
|
||||
Compilation, `check build assemblePack assembleTestPack`, mesures natives
|
||||
sur trois graines, réouverture de 42, coupes géologiques et cartes de biomes
|
||||
à la surface réelle. Contrôler la stabilité des mares après ticks, l'absence
|
||||
de structures admissibles, le ciel vide et l'absence de plans hydrologiques.
|
||||
Le recensement exhaustif des minerais doit fonctionner avec mémoire bornée ;
|
||||
aucun petit échantillon ne sera présenté comme un total d'île.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le module optionnel `sanctuary-test` fournit trois composants natifs :
|
||||
`EcologyBiomes176` sélectionne les biomes, `EcologyStrata176` remplace les
|
||||
matériaux pendant leur génération, et `EcologyWater176` pose les mares avant
|
||||
les décorations. Le preset normal n'utilise aucun de ces composants.
|
||||
|
||||
Les biomes propres au laboratoire réutilisent des décorations de Minecraft
|
||||
26.3 : forêt automnale, cerisiers, jungle clairsemée, mangrove, marais et
|
||||
cavernes. Les deux fragments les plus hauts reçoivent de la végétation courte.
|
||||
Les minerais natifs, sources, lacs et structures sont exclus de cette palette ;
|
||||
les dépôts du laboratoire beta.175 restent responsables des ressources.
|
||||
|
||||
Une mare occupe un seul chunk, avec un rayon de 3 ou 4 blocs et deux niveaux
|
||||
d'eau. Le placement vérifie le fond et les parois avant toute écriture ; cinq
|
||||
essais au maximum, dans un tiers des chunks. Aucun calcul de bassin versant,
|
||||
chargement de voisin ni recherche hydrologique régionale. Les décorations
|
||||
natives peuvent ensuite ajouter des plantes ou de petites flaques de grotte.
|
||||
|
||||
Le test d'eau compare les positions de **toutes** les sources dans le voisinage
|
||||
immédiat avant et après 240 ticks natifs, y compris les blocs gorgés d'eau.
|
||||
Il refuse tout écoulement et toute perte/apparition de source, et vérifie
|
||||
qu'il reste de l'eau dans chaque mare. Les flaques indépendantes produites
|
||||
par les groupes de stalagmites ne doivent pas être confondues avec une fuite.
|
||||
|
||||
Le recensement exhaustif conserve une enveloppe finie de chunks jusqu'à la
|
||||
fin de la mesure. Il ne force plus des cycles d'expiration/sauvegarde durant
|
||||
`SERVER_STARTED`, qui dupliquaient les copies des chunks voisins en mémoire.
|
||||
Ce parcours est réservé au diagnostic `--whole-island`, jamais au lancement
|
||||
ordinaire du laboratoire.
|
||||
|
||||
## Reproduction
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/worldgen_lab.py prepare --profile ecology --seed 42 --run nouvel-essai
|
||||
python3 scripts/worldgen_lab.py server --profile ecology --seed 42 --run nouvel-essai
|
||||
```
|
||||
|
||||
Ajouter `--verify` pour les relevés et le contrôle des fluides. Pour un
|
||||
recensement complet, ajouter `--whole-island` à la préparation **et** au serveur.
|
||||
Le lanceur refuse de réutiliser un monde dont l'empreinte des sources a changé.
|
||||
Les fichiers `ecology-v1-cold/survey.json`, les coupes binaires et
|
||||
`worldgen-lab-metrics.json` sont écrits dans le dossier serveur du laboratoire.
|
||||
`scripts/ecology_atlas.py` produit l'atlas avec les dépendances de
|
||||
`scripts/sky-atlas-requirements.txt`.
|
||||
|
||||
## Mesures natives du 29 septembre
|
||||
|
||||
Série `science176d`, Java 25, Minecraft 26.3, heap serveur limité à 1 536 MiB.
|
||||
L'[archive des relevés](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/releves-beta176/)
|
||||
conserve les JSON, coupes, journaux, figures et empreintes SHA-256.
|
||||
|
||||
| Contrôle | Graine 42 | Graine 0 | Graine 173 |
|
||||
| --- | ---: | ---: | ---: |
|
||||
| Densités identiques à beta.175 | 50 000 / 50 000 | 50 000 / 50 000 | 50 000 / 50 000 |
|
||||
| Ensembles de structures natives admissibles | 0 | 0 | 0 |
|
||||
| Plans hydrologiques régionaux | 0 | 0 | 0 |
|
||||
| Mares témoins stables après 240 ticks | 8 / 8 | 8 / 8 | 8 / 8 |
|
||||
| Blocs d'air contrôlés en Y=512–639 | 1 474 560 | 1 474 560 | 1 474 560 |
|
||||
|
||||
Les cinq récifs de chaque graine portent de la végétation. Les mares témoins
|
||||
comprennent de la surface, des grottes et des récifs hauts. Le contrôle du ciel
|
||||
porte sur neuf chunks autour de chacun des cinq récifs ; ce n'est pas un scan
|
||||
exhaustif de toutes les décorations possibles sur toutes les graines.
|
||||
|
||||
Réouverture de 42 : cartes de biomes identiques, deux coupes de blocs identiques
|
||||
octet par octet, mêmes sources d'eau avant/après les 240 nouveaux ticks.
|
||||
|
||||
### Ressources de toute l'île, graine 42
|
||||
|
||||
Recensement des **2 000 chunks de l'enveloppe de l'île**, Y=0–639, après
|
||||
décoration ; aucune extrapolation des 81 chunks centraux. Durée du comptage :
|
||||
154,391 s, réservée au diagnostic. Les points de contrôle mémoire montrent
|
||||
341–607 MiB de heap utilisé ; cela ne mesure pas le pic total du processus.
|
||||
|
||||
| Ressource | Blocs de minerai, variantes pierre + deepslate |
|
||||
| --- | ---: |
|
||||
| Cuivre | 193 771 |
|
||||
| Charbon | 138 998 |
|
||||
| Fer | 5 343 |
|
||||
| Lapis | 7 124 |
|
||||
| Diamant | **336** |
|
||||
| Or | 0 |
|
||||
| Redstone | 0 |
|
||||
|
||||
Les 336 diamants sont en deepslate, sous Y=136, sans voisin d'air. L'améthyste
|
||||
comprend 5 578 blocs ordinaires, 1 422 blocs bourgeonnants et 219
|
||||
bourgeons/cristaux. Ces totaux décrivent ce monde enregistré ; les deux autres
|
||||
graines ont seulement un relevé minéral régional, pas un total d'île.
|
||||
|
||||
Sur la carte de surface échantillonnée de 42, l'automne occupe 19,4 % des points
|
||||
et les cerisiers 25,5 %. Leurs plus grandes composantes regroupent respectivement
|
||||
642/646 et 846/850 points : il s'agit bien de grandes régions, avec quelques
|
||||
points isolés aux bords. Grille de 8 blocs, connexité à quatre voisins ; ces
|
||||
proportions ne sont pas des surfaces exactes au bloc près.
|
||||
|
||||
### Durées et interprétation
|
||||
|
||||
| Passage | Initialisation serveur hors bootstrap JVM | Processus → fin des diagnostics |
|
||||
| --- | ---: | ---: |
|
||||
| 42 neuve, comptage intégral | 6,403 s | 237,523 s |
|
||||
| 42 réouverte, relevé régional | 0,922 s | 36,606 s |
|
||||
| 0 neuve, relevé régional | 4,782 s | 86,856 s |
|
||||
| 173 neuve, relevé régional | 2,792 s | 86,125 s |
|
||||
|
||||
La seconde colonne chronomètre les événements de démarrage du serveur, pas
|
||||
l'ouverture complète du client. La dernière inclut les cartes, chargements
|
||||
de chunks, coupes, minerais et 240 ticks de vérification. Ces parcours sont
|
||||
activés seulement par `--verify`/`--whole-island` et ne tournent pas pendant
|
||||
la visite solo. Aucun gain de FPS ni coût isolé de l'eau n'est déduit ici.
|
||||
|
||||
## Livraison locale et limites
|
||||
|
||||
Client solo ouvert le 29 septembre à 20:22 : backend Vulkan confirmé
|
||||
(MoltenVK 1.4.2, Apple M1), entrée de KokaLab à `(-8.5, 249, -13.5)`,
|
||||
serveur intégré et rendu passé à 32 chunks dans le journal. Les commandes
|
||||
sont autorisées dans la copie.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` : contrôles statiques et
|
||||
smokes terminés, puis **264/265 GameTests réussis**. Le seul échec est
|
||||
`familiar029game_tests_carried_pair_and_invulnerability_still_apply`, assertion
|
||||
« Carrier fixture » au tick 0. Ce code de portage n'est pas modifié par ce lot
|
||||
et cette suite ne charge pas le module écologique `sanctuary-test`.
|
||||
|
||||
Relance ciblée sans modification de code :
|
||||
`./gradlew :sanctuary:runGameTest -PsanctuaryFocusedTests=familiarhit -PsanctuaryExpansionReload=true`
|
||||
→ **7/7 réussis**, `BUILD SUCCESSFUL`. Cela indique un échec intermittent ou
|
||||
une interaction de suite à examiner ; cela ne transforme pas la première
|
||||
suite complète en passage réussi. Aucun correctif du portage n'est livré ici.
|
||||
|
||||
Assemblage local **réussi** après ces contrôles, sans les relancer :
|
||||
`./gradlew build assemblePack assembleTestPack -x :check -x :sanctuary:check -x :sanctuary-test:check -x :demeure:check -x :jei:check`.
|
||||
`BUILD SUCCESSFUL` en 12 s ; packs générés dans `build/packwiz` et
|
||||
`build/packwiz-test`. Les journaux complets sont conservés dans l'archive des relevés. Aucun canal
|
||||
packwiz, serveur personnel ni instance Prism n'est mis à jour.
|
||||
|
||||
La copie `Sanctuary-Ecology-176-Solo` provient du serveur `science176d/42`
|
||||
après arrêt et réouverture vérifiée. Commandes autorisées, mode créatif,
|
||||
réglages de visite repris de beta.175 avec rendu 32 chunks. L'ancien solo
|
||||
est conservé. Pour observer librement : `/gamemode spectator`.
|
||||
|
||||
Repères de visite, graine 42, points d'observation au-dessus du terrain :
|
||||
|
||||
| Zone | Téléportation en spectateur |
|
||||
| --- | --- |
|
||||
| Grande région automnale | `/tp @s -4 300 180` |
|
||||
| Grande région de cerisiers | `/tp @s 20 280 -180` |
|
||||
| Récif jungle | `/tp @s -116 390 160` |
|
||||
| Mare du récif mangrove | `/tp @s -105 425 -152` |
|
||||
| Récif marais | `/tp @s 219 470 22` |
|
||||
| Transition profonde près d'une mare en grotte | `/tp @s 41 166 -55` |
|
||||
|
||||
Premier essai : les mares restent petites et arrondies ; leur forme, les
|
||||
proportions de pierres et les lisières doivent encore être jugées en visite.
|
||||
Les grands lacs, rivières et cascades, les villages Sanctuary, les donjons,
|
||||
les expansions et la station ISS ne sont pas implémentés par ce lot.
|
||||
@@ -0,0 +1,96 @@
|
||||
# RENDER-150 — Terre mate et reflets solaires colorés
|
||||
|
||||
Branche `codex/matte-dirt-beta150`, socle beta.149 `1c23d0c`.
|
||||
|
||||
La terre sèche utilise un profil entièrement mat (rugosité 0,98, métal 0),
|
||||
comme le feuillage corrigé en beta.149. Cela enlève l’éclat blanc/gris diffus
|
||||
sans supprimer les normales ni le contraste de relief. Le SSR de ces matières
|
||||
est également atténué par leur rugosité, sans changer son réglage global.
|
||||
|
||||
Blocs concernés : dirt, coarse_dirt, rooted_dirt, grass_block, dirt_path,
|
||||
farmland, podzol et mycelium. Les faces terreuses suivent ainsi le même profil.
|
||||
Les autres profils de matière sont inchangés.
|
||||
|
||||
Le disque du soleil et son reflet utilisent la même couleur native
|
||||
`SkyRenderState.sunriseAndSunsetColor`, issue de l’attribut environnemental
|
||||
Minecraft `SUNRISE_SUNSET_COLOR`. Son alpha pilote la transition depuis le
|
||||
blanc : aucune palette horaire ou altitude artificielle. Le soleil reste
|
||||
neutre tant que le dégradé natif est absent et se colore avec le ciel.
|
||||
La teinte solaire est convertie en lumière linéaire pour le PBR. Le mixin
|
||||
modifie seulement la couleur du disque solaire, préserve l’alpha de pluie,
|
||||
la texture native et la lune, et respecte la désactivation globale de Vanilla Light.
|
||||
La fusion diffuse de l’eau et sa normale animée d’origine sont conservées.
|
||||
Les accents suivent les pentes des vaguelettes qui réfléchissent le soleil
|
||||
vers la caméra. Leur pulsation vient des normales animées de la texture,
|
||||
sans oscillateur indépendant ni inversion artificielle du relief.
|
||||
Une faible variation stable par texel évite des crêtes toutes identiques ;
|
||||
elle module seulement l’intensité, sans produire des points isolés aléatoires.
|
||||
Les détails s’atténuent lorsqu’ils deviennent plus petits qu’un pixel écran.
|
||||
Les accents restent limités au reflet solaire et conservent sa couleur.
|
||||
Les lampes et la lune gardent leurs
|
||||
couleurs propres.
|
||||
PBR 50 %, SSR 20 % et deux portées indépendantes de 64 blocs par défaut conservés.
|
||||
Minecraft 26.3, Java 25 et dépendances inchangés ; aucune sauvegarde modifiée.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Tests des profils de terre et scènes GPU comparant PBR OFF/ON sur terre et
|
||||
feuillage : conservation de la couleur native et présence du relief.
|
||||
- Suites PBR finales Vulkan/OpenGL réussies, dont profils des huit types de
|
||||
sol, proximité, intensités, préférences et feuillage.
|
||||
Journaux : `build/beta150-pbr-vulkan-final.log` et
|
||||
`build/beta150-pbr-opengl-final.log`.
|
||||
- Terre : 180 469 pixels contrôlés, saturation relative 0,99877 sur les deux
|
||||
moteurs ; relief visible sur 145 415 pixels. Captures conservées dans
|
||||
`build/evidence/beta150-pbr-vulkan-final/` et
|
||||
`build/evidence/beta150-pbr-opengl-final/`.
|
||||
|
||||
Les tests comparent la contribution PBR au rendu natif et capturent aussi
|
||||
le disque du soleil, avant puis pendant la transition native du ciel.
|
||||
Le contrôle serveur `./gradlew check build`, exécuté dans une copie isolée
|
||||
`build/server-validation` avant les derniers ajouts exclusivement client,
|
||||
compte 229 réussites et 23 échecs sur 252 tests. Les 23 identifiants d’échec
|
||||
sont identiques à beta.149 ; aucun nouvel identifiant. Le contrôle global
|
||||
n’est donc pas vert. Journaux : `build/beta150-check-build-isolated.log` et
|
||||
`build/beta150-server-failures.json`.
|
||||
|
||||
Les scènes solaires comparent le rendu PBR au natif, avant et pendant le
|
||||
coucher de soleil. À 2 000 ticks, la teinte native est blanche ; à 11 500 ticks
|
||||
sa transition commence légèrement (alpha 0,051) ; à 12 400 ticks l’alpha
|
||||
natif atteint 0,831 et le disque devient chaud (centre RGB 255/255/135).
|
||||
Les deux moteurs ont validé cette synchronisation native.
|
||||
|
||||
Un lancement sans sélection PBR a également rencontré l’assertion client
|
||||
Blocodex « The full snapshot must become visible together », avant les scènes
|
||||
GPU (`build/beta150-water-motion-vulkan.log`). Il ne constitue pas une
|
||||
validation de la suite client générale.
|
||||
|
||||
La suite finale Vulkan des éclats liés aux vaguelettes passe
|
||||
(`build/beta150-wave-glints-vulkan.log`) : animation native, masquage F5,
|
||||
distances indépendantes, reflets solaires et lunaires, matériaux transparents,
|
||||
rechargement des ressources et redimensionnement. Dans la scène solaire,
|
||||
150 563 pixels sont éclaircis, sans pixel blanc saturé.
|
||||
|
||||
La suite OpenGL finale passe également
|
||||
(`build/beta150-wave-glints-opengl.log`) : 148 180 pixels éclaircis, aucun
|
||||
pixel blanc saturé. Les captures finales sont conservées dans
|
||||
`build/evidence/beta150-wave-glints-vulkan/` et
|
||||
`build/evidence/beta150-wave-glints-opengl/`.
|
||||
Le ressenti des vaguelettes en mouvement reste à confirmer par le créateur
|
||||
sur sa configuration ; les tests ne constituent pas une validation esthétique.
|
||||
|
||||
## Pack local
|
||||
|
||||
Construction et assemblage réussis avec
|
||||
`./gradlew build assemblePack -x :sanctuary:check`, après le contrôle complet
|
||||
ci-dessus. L’exclusion ne transforme pas ses échecs connus en réussite.
|
||||
Export packwiz puis vérification ZIP, version Fabric, Minecraft 26.3,
|
||||
Loader 0.19.5, JAR unique identique au construit, shader final et mixin solaire.
|
||||
|
||||
- Pack : `/Users/koka/Documents/sanctuary/sanctuary-beta/build/Sanctuary-beta.150.mrpack`
|
||||
- Taille : 10522942 octets.
|
||||
- SHA-256 pack : `65a4a44948dcdcf0325b5e24c076a03f8832184f25f5c1ff16f69bd3ee641b93`.
|
||||
- SHA-256 JAR : `4962e6f8cafd6ef3e81e088bf9b0092a264441632cfa095d4fe7394b855f093d`.
|
||||
- Reçu : `build/beta150-artifacts.json`, également copié à côté du pack.
|
||||
|
||||
Aucun canal publié, aucune instance Prism ni sauvegarde personnelle modifiée.
|
||||
@@ -0,0 +1,93 @@
|
||||
# FIX-151 — Nether, PBR opaque et SSR
|
||||
|
||||
Branche `codex/nether-wood-beta151`, socle beta.150 `448e2fe`.
|
||||
|
||||
## Déconnexion Nether
|
||||
|
||||
Le rapport Windows beta.150 transmis le 18 septembre 2026 indique
|
||||
`IndexOutOfBoundsException: Index 46 out of bounds for length 46` lors de
|
||||
`ClientPacketListener.handleContainerContent`, paquet `container_set_content`.
|
||||
Le passage au Nether dans le test client local reproduit exactement cette trace
|
||||
(journal `build/beta151-client-first.log`). Ce défaut n’est pas un crash GPU.
|
||||
|
||||
Minecraft recrée le joueur client et son menu lors de `handleRespawn`, utilisé
|
||||
aussi pour les changements de dimension. La progression n’était réattachée
|
||||
qu’en fin de tick ; un paquet d’inventaire dans le même lot arrivait avant la
|
||||
création des 18 cases supplémentaires et des 3 accessoires. Le correctif lie la
|
||||
progression déjà reçue au nouveau joueur dès la fin du traitement du paquet de
|
||||
respawn, avant le paquet suivant. Aucun paquet ni objet n’est ignoré et aucun
|
||||
format de sauvegarde ne change. Sans progression Sanctuary active, le chemin
|
||||
vanilla reste inchangé.
|
||||
|
||||
## Rendu
|
||||
|
||||
La beta.146 avait porté le gain solaire des surfaces opaques de 3 à 20
|
||||
(4 × 5), soit environ 6,7 fois plus. Le renforcement prévu pour les reflets de
|
||||
l’eau avait ainsi créé un voile sur les matières sombres, dont l’obsidienne.
|
||||
Les surfaces opaques retrouvent le gain 3 et le budget spéculaire total 0,5,
|
||||
partagé entre soleil et lampes. La teinte native du coucher de soleil, les
|
||||
normales et le relief sont conservés. Eau et verre conservent leur traitement.
|
||||
Le bois utilise le profil mat 0,98 : planches, bûches et variantes de construction,
|
||||
bambou, tiges et hyphes du Nether. Ce classement ne transforme pas le nylium en bois.
|
||||
|
||||
SSR est activé par défaut à 20 %, avec portée 64 blocs. Un choix OFF déjà
|
||||
sauvegardé reste OFF. PBR reste à 50 % par défaut.
|
||||
|
||||
## Cible graphique
|
||||
|
||||
Décision du créateur pendant ce ticket : **Vulkan uniquement** pour les nouvelles
|
||||
validations. Le passage OpenGL de ce ticket s’était terminé avant cette demande ;
|
||||
il reste une trace historique, pas une cible à conserver ni à relancer.
|
||||
Cette décision est reportée dans `AGENTS.md` et `docs/testing.md`.
|
||||
|
||||
## Vérification
|
||||
|
||||
Les assertions GPU Vulkan et les quatre transferts successifs
|
||||
Overworld → Nether → Overworld → Nether → Overworld passent sur un nouveau monde
|
||||
plat de développement, graine 122. Après chaque transfert : menu de 67 cases,
|
||||
pile de 7 diamants en inventaire natif, 3 émeraudes dans la première case
|
||||
supplémentaire et 5 lingots d’or dans la dernière, toujours présents côté client.
|
||||
|
||||
Les scènes PBR conservent 99,878 % de la saturation relative du chêne et 99,968 %
|
||||
de celle de l’épicéa écorcé sur 185 776 pixels chacune. Le relief affecte
|
||||
respectivement 101 382 et 51 671 pixels. De face, l’obsidienne ne gagne en moyenne
|
||||
que 5,114 niveaux RGB sur 255 (102 480 pixels), avec relief visible sur 95 225
|
||||
pixels. Captures inspectées : bois et obsidienne PBR OFF/ON.
|
||||
|
||||
Les contrôles couvrent aussi les réglages SSR absents (ON/20 %), la persistance
|
||||
d’un choix OFF, les profils bois, les petits déplacements de caméra, les
|
||||
intensités, le rechargement des ressources et le redimensionnement.
|
||||
Le premier passage Vulkan corrigé valide toutes les assertions, mais se bloque
|
||||
ensuite à la fermeture dans les barrières de synchronisation du programme de
|
||||
test Fabric ; son processus est arrêté après capture des piles de threads.
|
||||
Premier journal : `build/beta151-client-vulkan-final.log` ; diagnostic :
|
||||
`build/beta151-client-shutdown-threads.txt`. Le second passage Vulkan réussit entièrement, fermeture comprise, en **3 min 9 s**
|
||||
(`build/beta151-client-vulkan-clean.log`). La suite OpenGL terminée avant l’abandon de
|
||||
cette cible a réussi en 2 min 42 s ; aucun nouveau test OpenGL n’est prévu.
|
||||
|
||||
Le contrôle complet `./gradlew check build` dans `build/server-validation`
|
||||
(3 068 fichiers de production identiques aux sources courantes) termine en
|
||||
11 min 13 s : **229 réussites, 23 échecs sur 252**. Les 23 identifiants sont
|
||||
strictement identiques à beta.150 : aucun nouveau, aucun résolu.
|
||||
Le contrôle global reste rouge. Journaux et liste :
|
||||
`build/beta151-check-build.log`, `build/beta151-server-failures.json`.
|
||||
Ces échecs restent ouverts et ne constituent pas la validation du correctif
|
||||
réseau ; celui-ci repose sur la reproduction puis le test client des transferts.
|
||||
|
||||
## Pack local
|
||||
|
||||
`./gradlew build assemblePack -x :sanctuary:check` réussit en 11 s,
|
||||
indépendamment du contrôle complet en échec ci-dessus. Export packwiz réussi ;
|
||||
ZIP, version Fabric, Minecraft 26.3, Loader 0.19.5, JAR Sanctuary unique,
|
||||
mixin de respawn et shader exacts, classes conformes aux sorties compilées et
|
||||
absence des tests dans le JAR vérifiés.
|
||||
|
||||
- Pack : `/Users/koka/Documents/sanctuary/sanctuary-beta/build/Sanctuary-beta.151.mrpack`.
|
||||
- SHA-256 pack : `1d87804d75edda0a95c318ea3feaf47ecadeeb29d86ca55483a42741d7a5fcab`.
|
||||
- SHA-256 JAR : `6676c67f8be4e650a9b6a8213c8b0fdf3cf7e62b030c3d1a6d4805d438a8e543`.
|
||||
- Reçu : `build/beta151-artifacts.json`.
|
||||
|
||||
Les essais sont effectués sur Apple M1 ; la validation sur le PC Windows du
|
||||
créateur reste à faire.
|
||||
|
||||
Aucune sauvegarde personnelle ouverte, aucun canal publié ni instance installée.
|
||||
@@ -0,0 +1,66 @@
|
||||
# beta.162 — scène du tableau et conversation
|
||||
|
||||
Ticket issu du retour de séance du 24 septembre : conserver Retour/Actualiser en
|
||||
haut, montrer l’auteur avec son skin orienté vers la souris et sa demande dans
|
||||
une bulle colorée, puis les réponses à droite (dessous sur GUI étroit).
|
||||
|
||||
Modification client uniquement, sans migration, écriture dans les mondes ni
|
||||
changement des contrats fichier/MariaDB. Réponses, modération et suivi gardent
|
||||
leurs validations serveur. Le portrait est un rendu GUI, pas une entité.
|
||||
|
||||
## Présentation
|
||||
|
||||
- Retour et Actualiser restent hors de la zone défilante.
|
||||
- Le portrait utilise le skin de la connexion de l’auteur s’il est présent,
|
||||
sinon le cache de profils Minecraft asynchrone. Le skin natif de repli apparaît
|
||||
pendant le chargement ou si le profil ne peut pas être résolu (notamment les
|
||||
comptes fictifs/hors ligne). Aucun téléchargement synchrone dans le rendu.
|
||||
- Une bulle persistante encadrée montre catégorie/état, titre puis texte complet.
|
||||
Information : jaune clair sur fond sombre jaune ; les autres catégories gardent
|
||||
leurs couleurs. Les cartes de liste et du menu pause suivent aussi ce jaune.
|
||||
- Le portrait tourne doucement dans des angles bornés selon la position de la
|
||||
souris, sans capturer ses mouvements et sans clic requis.
|
||||
- Demande et discussion défilent séparément si l’espace le permet ; le champ de
|
||||
réponse et son bouton restent en bas de la discussion. Sur GUI étroit/bas,
|
||||
un défilement unique garde les réponses sous la scène.
|
||||
- Matériaux, lieu, récompense, suivi, visages des réponses et modération sont
|
||||
conservés. Les détails d’une demande vide ne sont plus affichés artificiellement.
|
||||
- Aucune animation de parole ni synchronisation vocale : « parle » désigne ici
|
||||
la mise en scène visuelle avec la bulle.
|
||||
|
||||
## Vérification
|
||||
|
||||
- Compilation Java 25 / Minecraft 26.3 réussie.
|
||||
- Parcours natif Vulkan : `NOTICE162_SCENE_PASS`, puis
|
||||
`SEARCH159_CLIENT_PASS mode=file`, build réussi en 1 min 59 s.
|
||||
- FR/EN, GUI 2/3/4 : scène, passage en colonne unique, boutons de retour au-dessus,
|
||||
réponse au-dessus du footer en deux colonnes, angles du portrait bornés et
|
||||
changement d’orientation avec la souris.
|
||||
- Brouillon conservé pendant les redimensionnements ; publication d’une réponse
|
||||
et lecture de son contenu après rechargement serveur vérifiées.
|
||||
- Régressions communautaires : photos, recherche, suivi/désabonnement, marqueur
|
||||
de carte et infobulle, restrictions de l’Intendance toujours validés.
|
||||
- Captures relues : `build/qa-beta162/0042_notice162-scene-fr_fr-2.png` et
|
||||
`0043_notice162-scene-fr_fr-3.png`. Conversation : `0048_notice162-conversation.png`.
|
||||
- `./gradlew check build assemblePack` lancé : 252 GameTests terminés, 229 réussis
|
||||
et 23 échecs, exactement les mêmes qu’en beta.161 (aucun ajouté/retiré).
|
||||
Liste comparée : `build/session162-server-failures.json`.
|
||||
- Contrôles restants et assemblage avec exclusion ponctuelle de `runGameTest` :
|
||||
réussis en 3 min 16 s (`check build assemblePack -x :sanctuary:runGameTest`).
|
||||
Aucune assertion désactivée dans les sources. Log : `build/beta162-assembly.log`.
|
||||
|
||||
Aucune modification de persistance : cette passe native utilise le mode fichier ;
|
||||
le laboratoire MariaDB partagé a été relancé avec la même interface, KokaLab
|
||||
connecté sur le monde existant `duo-flat-160`, Alice/Bob recréés, shader désactivé. Les figurants
|
||||
et profils hors ligne peuvent afficher un skin Minecraft de repli.
|
||||
|
||||
## Livraison locale
|
||||
|
||||
JAR : `mods/sanctuary/build/libs/sanctuary-beta.162.jar`.
|
||||
Pack assemblé : `build/packwiz`.
|
||||
SHA-256 : `35c5d82eba110ee2150f73baf6341efc90f87971965a3b4dd2bcd04add0a59ff`.
|
||||
|
||||
Branche `codex/notice-scene-beta162`, tag local `beta.162`. Anciens JAR conservés.
|
||||
Aucune publication distante ni synchronisation de Prism dans cette livraison.
|
||||
Les 23 échecs préexistants restent ouverts ; les essais graphiques ne démontrent
|
||||
pas la résolution réseau d’un skin officiel absent dans ce laboratoire hors ligne.
|
||||
@@ -1,5 +1,13 @@
|
||||
# Distribution packwiz et Prism
|
||||
|
||||
## Rattrapage des releases du 24 septembre 2026
|
||||
|
||||
Les binaires conservés des beta.145–151 et beta.154–166 sont publiés dans Gitea.
|
||||
La beta.144 existante reste intacte ; les 152–153 demeurent des chantiers non livrés.
|
||||
[Inventaire, empreintes et limites](publications-beta144-166.md).
|
||||
Cette publication ne modifie ni le canal packwiz ni les installations Prism.
|
||||
|
||||
|
||||
Sanctuary Beta utilise une seule instance Prism, synchronisée avant chaque
|
||||
lancement par packwiz. Le canal reste à l'adresse :
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
# Palais originel, verticalité et International Sanctuary Station
|
||||
|
||||
> **Statut au 29 septembre 2026 : concept historique.** Le créateur abandonne
|
||||
> le palais souterrain, ses accès et les raccordements au palais. Le
|
||||
> [fil rouge courant](storyquest-fil-rouge.md) remplace ces choix d'implantation.
|
||||
> Le texte ci-dessous conserve la discussion du 28 septembre et ne constitue
|
||||
> plus une consigne de construction du palais. Les pistes ISS, chenil et
|
||||
> ordinateurs restent ouvertes indépendamment de ce bâtiment. Aucun code ni
|
||||
> monde de laboratoire n'est supprimé par ce changement documentaire.
|
||||
|
||||
**Discussion du 28 septembre 2026 — conception, sans code.** Précisions du
|
||||
créateur après le [cadrage Storyquest](storyquest-beta173.md), sur la même
|
||||
branche `codex/storyquest-beta173`, issue de beta.172. Les formes et intentions
|
||||
retenues sont distinguées des mécaniques encore hésitantes. Aucun monde,
|
||||
paramètre de génération, familier ou format de sauvegarde n'est modifié ici.
|
||||
|
||||
**Premier lot décidé ensuite :** un palais par seed, huit ancres extérieures
|
||||
et huit demandes de matériaux distincts. Le bloc originel garde l'illustration
|
||||
fournie ; chacune des huit gemmes se colore indépendamment. Le
|
||||
[contrat de prototype](palais-prototype-beta173.md) décrit ce qui est réellement
|
||||
implémenté dans le laboratoire et les limites de l'intégration au monde.
|
||||
|
||||
## Le secret devient un lieu commun à habiter
|
||||
|
||||
Le secret sous le spawn prend la forme d'un **palais sobre, haut sous plafond,
|
||||
à huit coins**. Après discussion du niveau zéro et du vortex, le créateur
|
||||
précise : **adapter la hauteur au terrain**. Le palais garde son centre en
|
||||
**`x=0, z=0`**, avec une **altitude déterminée par le relief**. La salle n'est
|
||||
donc plus contrainte à Y=0.
|
||||
|
||||
**Le bloc originel suit le palais**, confirmation explicite du créateur. Sa
|
||||
position devient `(x=0, y=altitude adaptée, z=0)` : l'origine horizontale reste
|
||||
fixe, l'altitude accompagne le lieu. L'idée initiale d'un bloc exactement en
|
||||
`(0, 0, 0)` est remplacée ; aucun bloc séparé à Y=0 ni liaison vers ce niveau
|
||||
n'est requis. Sa position précise dans le volume de la salle reste à dessiner.
|
||||
|
||||
Ce palais représente une **cartographie intérieure de Sanctuary Island**,
|
||||
puis l'expédition et l'aventure de ses habitants. Le complexe est léger, assez
|
||||
vide pour que les joueurs le personnalisent : une page blanche avec une
|
||||
architecture reconnaissable. Sa richesse vient progressivement de leur partie.
|
||||
La palette, les dimensions, les proportions et le mobilier initial restent à
|
||||
dessiner. « Palais » n'implique ni dorures ni décor luxueux.
|
||||
|
||||
Le lieu est souterrain par son implantation et ses accès. Si le terrain à
|
||||
l'origine est trop mince ou absent, **le palais ressort sous l'île et reste
|
||||
visible au-dessus du vide**. Le créateur souhaite un endroit lumineux,
|
||||
repérable depuis l'extérieur, notamment la nuit. Le vortex de nuages doit
|
||||
participer à la vue en dessous.
|
||||
|
||||
La fonction recherchée est une **base d'opérations pour les expéditions** :
|
||||
comprendre les destinations, voir les matériaux nécessaires, préparer le départ
|
||||
et conserver la mémoire de ce qui a été accompli. Le rôle exact des machines
|
||||
et la mécanique d'ouverture des territoires ne sont pas encore décidés.
|
||||
|
||||
### Proposition spatiale à discuter
|
||||
|
||||
Faire correspondre les huit secteurs de la salle aux huit directions autour
|
||||
de l'île, avec une orientation lisible depuis le bloc originel. Chaque secteur
|
||||
pourrait recevoir un indice découvert, l'état d'une liaison, les besoins de sa
|
||||
prochaine expédition et les souvenirs rapportés. Les joueurs complètent
|
||||
eux-mêmes l'aménagement avec cartes, cadres, bannières, trophées et constructions.
|
||||
Le palais devient une carte que l'on parcourt à pied.
|
||||
|
||||
Les huit coins de l'île désignent ici des secteurs à définir sur son contour ;
|
||||
ils n'imposent pas une île géométriquement octogonale. Le rapprochement entre
|
||||
coins du palais, lieux périphériques et liaisons est une proposition cohérente
|
||||
avec la demande, pas encore un plan d'implantation validé.
|
||||
|
||||
Il faut encore décider ce que les joueurs peuvent déplacer ou casser, et
|
||||
comment ils réparent une installation devenue inutilisable. La personnalisation
|
||||
ne vaut pas décision de protéger tout le palais. L'ancien contrat de conception
|
||||
permet aussi de reconstruire une installation d'expansion ailleurs : préciser
|
||||
ce qui relève du bloc originel unique et ce qui reste reproductible.
|
||||
|
||||
## Les accès font jouer la profondeur
|
||||
|
||||
Le créateur envisage plusieurs accès complémentaires, sans choisir encore
|
||||
leur nombre ni leur disposition :
|
||||
|
||||
| Accès évoqué | Expérience recherchée | Point à dessiner ou vérifier |
|
||||
| --- | --- | --- |
|
||||
| Ascenseur aquatique | Monter et descendre rapidement, avec eau et bulles | Les algues et la « terre des abîmes » sont les termes de la discussion ; le bloc de propulsion et le montage exact restent à préciser |
|
||||
| Puits de chute avec réception dans l'eau | Se laisser tomber vers le palais central | Continuité du bassin, visibilité de la réception, sortie et absence de fuite dans le vide |
|
||||
| Escaliers | Accès progressif qui fait découvrir les volumes souterrains | Arrivée dans la salle, paliers, raccourcis et lien avec les galeries |
|
||||
| Ouvertures verticales | Apercevoir en contrebas un sol d'eau et oser descendre | Lecture du trajet, réception réelle, retour et espace pour les compagnons |
|
||||
|
||||
Proposition : une première descente lente qui révèle le palais, puis un trajet
|
||||
rapide pour les usages quotidiens. Le choix entre escalier, chute et ascenseur
|
||||
peut exprimer cette différence sans obliger à construire les trois dès le début.
|
||||
|
||||
### Implantation retenue : une hauteur adaptée au terrain
|
||||
|
||||
Le preset actuel emploie `sanctuary:sanctuary_640`, avec **`min_y: 0`** et
|
||||
**`height: 640`**. Le vortex a **`HEIGHT=0`**, avec une épaisseur de quatre blocs
|
||||
dans son rendu. Ce sont des valeurs lues dans les sources beta.172, pas des
|
||||
mesures de terrain d'une graine particulière :
|
||||
|
||||
- [Preset Sanctuary](../mods/sanctuary/src/main/resources/data/sanctuary/worldgen/world_preset/sanctuary.json).
|
||||
- [Type de dimension](../mods/sanctuary/src/main/resources/data/sanctuary/dimension_type/sanctuary_640.json).
|
||||
- [Hauteur du vortex](../mods/sanctuary/src/main/java/fr/koka/sanctuary/sky/VortexClouds.java).
|
||||
- [Épaisseur et rendu](../mods/sanctuary/src/main/java/fr/koka/sanctuary/client/VortexCloudRenderer.java).
|
||||
|
||||
Ces contraintes ont conduit le créateur à choisir une hauteur adaptée au terrain.
|
||||
La proposition précédente d'imposer la salle juste au-dessus du bloc à Y=0
|
||||
est remplacée par cette décision. **Le vortex peut conserver sa hauteur actuelle** ;
|
||||
aucun déplacement des nuages ni changement de limite du monde n'est nécessaire
|
||||
par principe. Leur séparation visuelle devra être vérifiée sur la coupe choisie.
|
||||
|
||||
Proposition de placement : examiner le relief sous le centre de l'île et sur
|
||||
l'emprise du palais, puis choisir un niveau permettant la grande hauteur sous
|
||||
plafond et les accès. La salle peut traverser l'enveloppe inférieure du terrain
|
||||
et laisser une façade lumineuse visible dans le vide. Il ne faut ni l'enterrer
|
||||
entièrement à tout prix, ni aplatir toute l'île pour l'accueillir.
|
||||
|
||||
Avant le code, fixer la marge au-dessus du vortex, la couverture rocheuse
|
||||
souhaitée, le volume de la salle et la solution lorsque le relief ne fournit
|
||||
pas assez d'épaisseur. Prévoir planchers, bassins et fondations dans les limites
|
||||
constructibles. La même graine et la même version de génération doivent choisir
|
||||
la même implantation. Ces règles détaillées restent à dessiner puis à tester
|
||||
sur de nouveaux mondes ; aucune partie existante n'est repositionnée.
|
||||
|
||||
## Les huit objets et la Sanctuary Key restent ouverts
|
||||
|
||||
Le créateur envisage des **collectibles répartis dans les huit secteurs de
|
||||
l'île**, permettant d'activer de nouvelles expéditions, la génération de
|
||||
nouvelles îles et un nouveau point d'intérêt à découvrir. Il hésite sur le
|
||||
fait de placer ces objets derrière des défis et sur la forme d'une clé appelée
|
||||
provisoirement **Sanctuary Key**. Aucun identifiant d'objet n'est fixé.
|
||||
|
||||
| Possibilité | Ce qu'elle favorise | Limite à discuter |
|
||||
| --- | --- | --- |
|
||||
| Collectible trouvé par exploration | Curiosité, lecture du paysage et surprises | Le défi peut se résumer à trouver le bon endroit |
|
||||
| Clé obtenue après une épreuve | Accomplissement clair et aventures différentes | Huit épreuves identiques donneraient une progression répétitive |
|
||||
| Découverte puis préparation de l'expédition | Exploreurs, constructeurs et producteurs ont chacun un rôle | Il faut rendre les besoins compréhensibles et éviter les attentes sans activité |
|
||||
|
||||
**Proposition recommandée pour la discussion :** découvrir dans un secteur un
|
||||
objet ou un signe lié à une liaison, puis préparer l'expédition au palais. Le
|
||||
défi peut varier selon le lieu : combat, exploration, mécanisme, construction
|
||||
ou coopération. Cette proposition ne décide ni d'une clé pour chaque direction,
|
||||
ni d'un objet consommé, ni de l'obligation de réunir les huit avant de partir.
|
||||
|
||||
Avant le premier essai, choisir ce que l'objet autorise, qui peut l'apporter,
|
||||
où les matériaux sont réellement déposés et qui lance le départ. Prévoir le
|
||||
cas d'un objet perdu et celui d'un nouveau joueur arrivé après l'ouverture.
|
||||
La preuve durable d'ouverture appartient au serveur ; le trophée exposé au
|
||||
palais peut raconter cette ouverture sans être la seule preuve qui la conserve.
|
||||
|
||||
Les huit collectibles ne remplacent pas automatiquement les **sept boules de
|
||||
cristal** de la vision historique. Leur relation éventuelle est à discuter.
|
||||
|
||||
## La montée vers l'ISS
|
||||
|
||||
Le créateur souhaite développer la verticalité au-dessus de l'île par des
|
||||
structures en hauteur, jusqu'à l'**ISS — International Sanctuary Station**.
|
||||
Cette station métallique, inspirée visuellement de l'ISS, constitue une trace
|
||||
laissée par un ancien joueur. Son identité n'est pas encore choisie.
|
||||
|
||||
Elle comprendrait une **carte de l'île avec des cadres d'objets posés à
|
||||
l'horizontale**. La taille, l'altitude, l'accès et la mise à jour de cette carte
|
||||
restent à définir. Il faut lui donner une fonction particulière, reliée à un
|
||||
système ou une quête. Trouver la station pour obtenir un familier particulier
|
||||
est une piste du créateur, pas une récompense déjà arrêtée.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
ISS[ISS : trace d'un ancien joueur et fonction à définir]
|
||||
HAUT[Structures en hauteur : itinéraire à concevoir]
|
||||
ILE[Surface : spawn, habitants et huit secteurs]
|
||||
ACCES[Descente : escaliers, eau ou puits]
|
||||
PALAIS[Palais octogonal : préparer et raconter les expéditions]
|
||||
ORIGINE[Bloc originel : suit le palais, x=0 et z=0]
|
||||
ISS --- HAUT --- ILE --- ACCES --- PALAIS --- ORIGINE
|
||||
```
|
||||
|
||||
Schéma d'intention sans échelle : aucun ordre de déblocage ni palier d'altitude
|
||||
obligatoire n'est décidé. Le palais s'adapte au terrain ; le vortex est à Y=0.
|
||||
Le nœud du bloc indique son lien avec le palais, pas un étage nécessairement séparé.
|
||||
|
||||
Proposition : faire du palais le lieu où l'on **prépare et engage** l'expédition,
|
||||
et de l'ISS un lieu où l'on **observe et repère** des phénomènes célestes. Une
|
||||
fonction de reconnaissance donnerait une raison d'y retourner après avoir
|
||||
trouvé le familier. Elle doit être distinguée du rôle de l'observatoire déjà
|
||||
envisagé sur l'île ; ne pas créer trois lieux qui donnent la même information.
|
||||
|
||||
L'ISS pourrait suggérer une destination ou un événement aérien sans révéler
|
||||
tous les secrets. La carte pourrait être le relevé ancien du précédent joueur
|
||||
ou un outil actualisé : ce choix change ce qu'elle raconte. Prévoir l'accès des
|
||||
nouveaux arrivants et le retour au sol avant de choisir une récompense rare.
|
||||
|
||||
## Chenil et incubateurs de familiers
|
||||
|
||||
Piste du créateur : un **vrai bloc** reçoit le familier pour lui faire gagner
|
||||
de l'XP. **Deux yeux apparaissent sur le bloc lorsqu'il est occupé.** Le dispositif
|
||||
peut devenir un chenil ou un incubateur ; son nom et sa place dans le palais
|
||||
restent ouverts. Des **multiblocs de tailles différentes** pourraient accueillir
|
||||
des familiers miniatures ou colossaux. Les dimensions ne sont pas fixées.
|
||||
|
||||
Il faut distinguer cette idée de l'incubation du 25 septembre : celle-ci faisait
|
||||
fructifier une réserve avec les déplacements, poses et casses du joueur. Le
|
||||
nouvel incubateur est un lieu physique où l'on dépose le compagnon. Ils peuvent
|
||||
se remplacer ou se compléter ; aucune double croissance automatique n'est décidée.
|
||||
|
||||
Le point de conception principal est de garder un intérêt à emmener son
|
||||
familier. **Proposition à éprouver :** un entraînement limité au chenil, soutenu
|
||||
éventuellement par de la nourriture appréciée, tandis que l'aventure développe
|
||||
le lien et l'expérience autrement. Plafond, nourriture, vitesse et arrêt hors
|
||||
ligne restent des propositions, sans règle chiffrée ni rendement acquis.
|
||||
|
||||
Avant le code, préciser : XP propre du familier ou réserve récupérable par le
|
||||
joueur ; propriétaire et personnes autorisées à le retirer ; croissance selon
|
||||
temps simulé ou réel ; comportement hors ligne et hors chunks chargés ; sort
|
||||
du pensionnaire à la casse ou dissociation. Le même familier ne doit pas exister
|
||||
simultanément dans le chenil et sur la tête. Les deux yeux signalent l'occupation,
|
||||
mais l'identité du pensionnaire doit aussi pouvoir être reconnue.
|
||||
|
||||
Si la forme multibloc est retenue, la rapprocher du système d'assemblage
|
||||
volontaire existant. Ne pas imposer automatiquement une cage colossale à tout
|
||||
familier de grande apparence avant d'avoir choisi la règle de taille.
|
||||
|
||||
## Ordinateurs : commencer par la tâche à accomplir
|
||||
|
||||
Le créateur envisage **assembleur, terminal et écran**, en référence à un mod
|
||||
d'ordinateurs non identifié avec certitude. Aucun choix de dépendance n'est
|
||||
effectué. Le [cahier d'installation d'expansion](structures-conception.md#ordinateurs-et-installation-dexpansion)
|
||||
évoque déjà assembleur, contrôleur, terminal, dépôt et ancre, mais ces rôles
|
||||
restent conceptuels. Le terminal de stockage est encore une autre fonction.
|
||||
|
||||
Le premier besoin est concret : **choisir une expédition, voir les matériaux
|
||||
nécessaires, constater ce qui est déposé et comprendre ce qui manque**. On peut
|
||||
dessiner ce parcours dans une alcôve du palais avant de décider de l'ordinateur.
|
||||
|
||||
Proposition de progression : une installation locale compréhensible à la main,
|
||||
puis un écran de suivi partagé, puis éventuellement automatisation et programmation.
|
||||
Un dépôt physique possède les matériaux ; un écran les montre ; le système
|
||||
serveur valide l'expédition. Le rôle de l'assembleur doit être choisi avant
|
||||
d'ajouter un bloc dont on ne sait pas ce qu'il fabrique.
|
||||
|
||||
Cette approche conserve de la place pour les computers sans les rendre
|
||||
obligatoires par défaut pour découvrir le palais. Si un mod externe est choisi,
|
||||
son identité et sa compatibilité Minecraft 26.3 seront vérifiées dans le ticket
|
||||
d'intégration ; aucun nom n'est déduit de la description seule.
|
||||
|
||||
## Prochain résultat de conception proposé
|
||||
|
||||
Dessiner **un plan et une coupe du palais**, avec huit secteurs, bloc originel,
|
||||
altitude adaptée au terrain, hauteur de salle, partie extérieure, accès et nuages.
|
||||
Placer seulement les
|
||||
emplacements fonctionnels nécessaires pour essayer **une** expédition :
|
||||
destination, éventuel objet de déblocage et besoins matériels. Garder les
|
||||
autres espaces disponibles pour les joueurs et les évolutions suivantes.
|
||||
|
||||
L'ISS et le chenil peuvent alors avoir une fiche propre, liée à cette coupe
|
||||
verticale. Ils ne deviennent pas des prérequis techniques à la première salle.
|
||||
Les trois décisions suivantes sont : **moment de découverte du palais**,
|
||||
**premier geste d'expédition** et **ce que l'ISS permet de faire après sa visite**.
|
||||
|
||||
Validation documentaire : références locales et diff contrôlés. Aucun build
|
||||
ou essai de jeu relancé ; aucune fonctionnalité décrite ici n'est implémentée.
|
||||
@@ -0,0 +1,150 @@
|
||||
# beta.173 — palais procédural et bloc originel
|
||||
|
||||
> **Statut au 29 septembre 2026 : prototype technique conservé, architecture
|
||||
> abandonnée pour la suite.** Le palais souterrain, ses accès et les
|
||||
> raccordements au palais sortent de la conception. Le
|
||||
> [fil rouge courant](storyquest-fil-rouge.md) prépare des lieux indépendants.
|
||||
> Cette fiche décrit fidèlement le laboratoire livré et ses essais ; elle
|
||||
> n'annonce plus l'intégration du palais à Sanctuary Island.
|
||||
|
||||
Ticket d'implémentation ouvert le 28 septembre 2026, branche
|
||||
`codex/storyquest-beta173`, base beta.172. Première scène jouable issue du
|
||||
[palais originel](palais-originel-iss-beta173.md). Livraison locale vérifiée.
|
||||
|
||||
## Contrat avant implémentation
|
||||
|
||||
Le palais est construit par un générateur versionné, avec une graine explicite.
|
||||
Le volume octogonal, la hauteur, les baies, la toiture et le dessin du sol
|
||||
peuvent varier. Une même graine/version reproduit le même plan. Il conserve
|
||||
une salle haute et sobre, huit secteurs ouverts, un escalier et un puits d'eau.
|
||||
La première scène est centrée en X=0, Z=0 et surélevée dans un monde plat neuf.
|
||||
Chaque monde de laboratoire contient **un seul palais, déterminé par sa seed**.
|
||||
Pour comparer une autre architecture, créer un autre monde neuf avec une autre seed.
|
||||
|
||||
L'intégration au terrain de Sanctuary Island était envisagée lors de ce lot ;
|
||||
elle est abandonnée le 29 septembre. Ce lot n'appelle pas le générateur dans
|
||||
les mondes de production. Il ne crée ni
|
||||
expansion, ni quête, ni ISS, ni ordinateur, ni récompense.
|
||||
|
||||
Le nouveau bloc `sanctuary:origin_block` reprend les propriétés de la bedrock
|
||||
et l'image fournie par le créateur, conservée sans retouche dans
|
||||
`mods/sanctuary/src/main/art/origin-logo.png`. Son rendu est dérivé de manière
|
||||
déterministe à la construction des ressources :
|
||||
|
||||
- éteint : image sombre et désaturée, aucune lumière émise ;
|
||||
- allumé sans relais : image en niveaux de gris, centre blanc, lumière 15 ;
|
||||
- allumé avec des relais : chaque relais rend sa couleur à la gemme associée ;
|
||||
les autres restent grises et le centre reste blanc.
|
||||
|
||||
Huit bits indépendants représentent les huit relais, pas un simple compteur.
|
||||
L'ordre des activations est libre. L'extinction masque les couleurs sans effacer
|
||||
l'état des relais. Ces états sont des propriétés de bloc natives, sauvegardées
|
||||
dans les nouveaux chunks ; aucun ancien format n'est converti.
|
||||
|
||||
Huit blocs `sanctuary:expansion_anchor` sont disposés à l'extérieur, dans les
|
||||
huit directions, sur des plateformes reliées à pied au palais. Chacun accepte
|
||||
un matériau distinct, décision du créateur du 28 septembre. Clic droit avec la
|
||||
quantité en main : dépôt entier, consommé côté serveur, ancre allumée et bit
|
||||
associé du bloc originel activé. Un dépôt insuffisant ou incorrect reste intact ;
|
||||
une ancre déjà allumée ne consomme plus. Les demandes sont communes à la partie,
|
||||
sans crédit individuel ni dépôt partiel dans ce premier lot.
|
||||
|
||||
| Direction / gemme | Demande provisoire |
|
||||
| --- | --- |
|
||||
| Nord / bleu | 32 pierres |
|
||||
| Nord-est / vert clair | 16 bûches de chêne |
|
||||
| Est / vert | 16 blés |
|
||||
| Sud-est / orange | 4 lingots d'or |
|
||||
| Sud / rouge | 16 poudres de redstone |
|
||||
| Sud-ouest / rose-violet | 8 éclats d'améthyste |
|
||||
| Ouest / jaune | 8 lingots de fer |
|
||||
| Nord-ouest / menthe | 16 verres |
|
||||
|
||||
Le bloc central s'allume/s'éteint par clic droit dans ce laboratoire. Il faut
|
||||
l'allumer avant les dépôts ; l'éteindre conserve les ancres et leurs bits.
|
||||
Les coûts servent au prototype et ne constituent pas l'équilibrage final.
|
||||
Ces ancres ne déclenchent encore aucune génération d'expansion.
|
||||
|
||||
## Monde et écritures autorisées
|
||||
|
||||
Le module facultatif Sanctuary Test, le dossier dédié `palace-flat-173` et le
|
||||
commutateur `sanctuary.palaceLab=true` conditionnent le laboratoire. La préparation
|
||||
refuse toute autre partie. Une emprise doit être entièrement libre avant la
|
||||
première écriture. Un palais prêt est rechargé tel quel ; il n'existe pas de
|
||||
commande de régénération. Les écritures sont réparties sur plusieurs ticks serveur.
|
||||
|
||||
Un journal indépendant `palace-lab-173.json`, schéma 1 / génération 1, enregistre
|
||||
graine, altitude et phase du palais. Les états des blocs restent dans les chunks.
|
||||
Une génération interrompue est conservée comme incomplète ; utiliser un nouveau
|
||||
dossier de laboratoire. Pas de nettoyage ou de reprise destructive.
|
||||
Une version inconnue ou un journal invalide bloque la préparation et préserve
|
||||
le fichier. Le retrait de cette version après création de ces nouveaux blocs
|
||||
n'est pas une conversion prise en charge ; conserver le monde de développement.
|
||||
|
||||
## Visiter et comparer
|
||||
|
||||
Depuis cette branche, avec Java 25 :
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/palace_lab.py prepare --seed 173
|
||||
python3 scripts/palace_lab.py server --seed 173
|
||||
# Dans un second terminal :
|
||||
python3 scripts/palace_lab.py client --seed 173
|
||||
```
|
||||
|
||||
Le client utilise Vulkan. Le serveur écoute uniquement sur `127.0.0.1:25579`.
|
||||
Créatif pour visiter et aménager ; `/palais materiaux` donne les huit demandes,
|
||||
`/palais retour` ramène dans la salle. Les messages sont traduits FR/EN.
|
||||
Une autre seed utilise un autre dossier sous `build/palace-173/` ; arrêter le
|
||||
serveur précédent avant de lancer l'autre. Le lanceur préserve les fichiers
|
||||
existants et refuse une modification du nom de monde, de la seed ou du réseau.
|
||||
|
||||
Le palais est à Y=96 dans ce laboratoire, au-dessus du terrain plat. Le plan
|
||||
accepte une altitude arbitraire. Le placement sous Sanctuary Island à une
|
||||
altitude calculée n'a pas été intégré et n'est plus prévu ; la géographie des
|
||||
huit ancres est à reprendre indépendamment de ce palais.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- Les quatre nouveaux GameTests passent (cinq tests requis avec le témoin du
|
||||
banc). Onze graines incluent `0`, `173`, des voisins et les extrêmes 64 bits :
|
||||
plan entier reproductible, variation d'architecture, huit accès praticables
|
||||
et toutes les écritures contenues dans l'emprise. Une emprise occupée est
|
||||
refusée sans écriture. Dépôts faux, insuffisants ou répétés conservés ; les
|
||||
huit dépôts valides activent les bits attendus dans un ordre non séquentiel.
|
||||
- Les 512 combinaisons d'allumage et de relais survivent au codec natif de
|
||||
sauvegarde. Lumière 0/15 et conservation des bits à l'extinction vérifiées.
|
||||
- Client Minecraft **26.3 / Vulkan / Apple M1**, scénario
|
||||
`Palace173ClientChecks` réussi : extérieur, intérieur, éteint, monochrome,
|
||||
trois gemmes et huit gemmes. Six captures examinées dans
|
||||
`build/palace-validation/screenshots/`. Synchronisation serveur/client du
|
||||
masque de gemmes vérifiée. Le premier lancement avait omis le profil Test ;
|
||||
le scénario passe avec `-PsanctuaryQuickTests=true`.
|
||||
- Image source strictement identique au PNG fourni (SHA-256
|
||||
`33adb8f689d55b7bf8d0824b02942ea3823f128b27fab458713ab9582061a1a0`).
|
||||
Compilation Python, JSON FR/EN et préparation répétée du laboratoire : les
|
||||
cinq fichiers de configuration restent identiques.
|
||||
|
||||
Commandes ciblées :
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary:runGameTest :sanctuary-test:compileJava -PsanctuaryFocusedTests=palace173 -PsanctuaryAtlasOnly=true
|
||||
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true -PsanctuaryPalace173ClientTests=true -PsanctuaryQuickTests=true -PsanctuaryClientGraphicsBackend=vulkan
|
||||
```
|
||||
|
||||
- `./gradlew check build assemblePack assembleTestPack :sanctuary-test:exportDuoLaunch`
|
||||
**réussi**, 265/265 GameTests, contrôles du pack et autres contrôles du dépôt
|
||||
réussis (12 min 35 s). Profils packwiz normal et Test assemblés localement.
|
||||
- JAR vérifié : beta.173, générateur présent, 512 états du bloc et 257 textures.
|
||||
SHA-256 : `fa1f92f1a84b5f863e5f323ab0afe63e01acbd08c06181e5f636ab5b372886f9`.
|
||||
- Serveur dédié réellement démarré sur loopback, journal `ready`, puis reprise
|
||||
du même monde. Allumage avec masque `73` et un bloc d'aménagement témoin
|
||||
conservés après arrêt/reprise (`PALACE173_GEMS_RELOAD_PASS` et
|
||||
`PALACE173_CUSTOMIZATION_RELOAD_PASS`). Le témoin est ensuite retiré et le
|
||||
bloc éteint pour la première visite ; serveur arrêté proprement. Le lanceur
|
||||
accepte aussi le `sanctuary_test\:flat` réécrit automatiquement par Minecraft
|
||||
dans les propriétés Java.
|
||||
|
||||
Les journaux restent ignorés dans `build/palace-validation/`. Les artefacts
|
||||
restent locaux ; aucun canal public ni profil Prism n'a été mis à jour.
|
||||
@@ -0,0 +1,25 @@
|
||||
# beta.164 — calendrier compact et intendance alignée
|
||||
|
||||
Calendrier à la largeur de la navigation : jour agrandi et heure sur la première
|
||||
ligne, mois et année ensemble dessous. Le titre serveur est aligné à gauche
|
||||
et couvre la carte ainsi que les panneaux Gazette/Tableau.
|
||||
|
||||
Interface cliente uniquement. Aucun changement de stockage ou de monde.
|
||||
|
||||
Vérification client Vulkan réussie : FR/EN aux échelles GUI 2, 3 et 4.
|
||||
Le test contrôle la largeur du calendrier, l’heure dans son libellé accessible,
|
||||
les limites de l’intendance et les interactions communautaires existantes.
|
||||
Captures conservées dans `build/qa-beta164/` et journal dans
|
||||
`build/beta164-client-retry.log`. Inspection visuelle FR GUI 4 et EN GUI 2 effectuée.
|
||||
Le premier test a détecté que StringWidget redimensionnait le calendrier lors
|
||||
du changement de texte ; la largeur de navigation est maintenant réappliquée.
|
||||
|
||||
`check build assemblePack --continue` exécuté : les mêmes 23 GameTests qu’en
|
||||
beta.163 échouent, aucun nouvel échec (comparaison enregistrée dans
|
||||
`build/session164-server-failures.json`). La suite complète reste en échec.
|
||||
L’assemblage séparé `build assemblePack :sanctuary-test:exportDuoLaunch
|
||||
-x :sanctuary:check` réussit après ce contrôle.
|
||||
|
||||
Anciennes archives conservées ; aucune publication distante.
|
||||
JAR : `mods/sanctuary/build/libs/sanctuary-beta.164.jar`.
|
||||
SHA-256 : `5bdfb097004a7932a075fbce9013556266f76ddd491ca21a24e12609c8600e53`.
|
||||
@@ -0,0 +1,27 @@
|
||||
# beta.163 — cadre du menu pause
|
||||
|
||||
Retour de séance : Découverte/Progression séparés de Habitant/Factions/Combat,
|
||||
bouton Monde retiré, séparateurs natifs de Progression, reprise centrée dans le
|
||||
footer, calendrier à gauche, message serveur au centre et horloge à droite.
|
||||
|
||||
Interface cliente uniquement, sans migration ni modification de règles ou de
|
||||
mondes. « Options du monde » reste dans les réglages en bas à gauche ; seul le
|
||||
raccourci de navigation Monde disparaît. La carte centrale reste disponible.
|
||||
|
||||
Vérifications :
|
||||
- Test client natif Vulkan réussi : FR/EN aux échelles GUI 2, 3 et 4,
|
||||
calendrier, séparation des groupes, limites des widgets et reprise centrée.
|
||||
Captures dans `build/qa-beta163/` ; journal `build/beta163-client-retry.log`.
|
||||
- Le scénario communautaire vérifie aussi la scène du tableau et les échanges.
|
||||
Une première exécution a expiré pendant une publication ; le test respecte
|
||||
désormais le délai serveur entre deux écritures.
|
||||
- `check build assemblePack --continue` : 23 échecs GameTest, ensemble identique
|
||||
à beta.162, aucun nouvel échec. Les autres tâches ont terminé ; la suite
|
||||
complète reste donc en échec. Comparaison dans `build/session163-server-failures.json`.
|
||||
- Assemblage réussi avec `build assemblePack :sanctuary-test:exportDuoLaunch
|
||||
-x :sanctuary:check`, après la vérification complète.
|
||||
|
||||
JAR local : `mods/sanctuary/build/libs/sanctuary-beta.163.jar`.
|
||||
SHA-256 : `72df10885d1bcb3b623c46bddf361839c23f06dfd543119f91ac5e006a8f961e`.
|
||||
Aucune publication distante ; anciens JAR conservés.
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# PAUSE-155 — une entrée Sanctuary, trois espaces
|
||||
|
||||
Branche `codex/pause-redesign-beta155`, livraison locale `beta.155`.
|
||||
Suite de [COMMUNITY-154](community-beta154.md).
|
||||
|
||||
## Résultat attendu
|
||||
|
||||
Remplacer l'ancienne grille du menu pause par trois espaces sans fond ou
|
||||
séparateurs imposés à chaque espace :
|
||||
|
||||
- En-tête : date locale à gauche, dernier message d'intendance au centre,
|
||||
heure locale à droite. Le message ouvre directement sa publication.
|
||||
- Corps : navigation permanente Découverte, Monde, Progression, Habitant,
|
||||
Faction et Batailles à gauche, tous espacés de 4 pixels comme les boutons
|
||||
d’options. Carte personnelle au centre. Gazette au-dessus du tableau d'affichage à droite.
|
||||
- Bas de la colonne gauche : Options, Options du monde, puis Sauvegarder et
|
||||
quitter, empilés dans cet ordre et alignés avec les six boutons Sanctuary.
|
||||
Reprendre la partie se trouve seul tout en bas de la colonne centrale,
|
||||
sous la carte. Les actions natives Minecraft sont réutilisées, y compris
|
||||
Déconnexion en multijoueur. À grande échelle d'interface, les six boutons
|
||||
de navigation deviennent plus compacts pour préserver cette séparation.
|
||||
|
||||
La Gazette présente des boutons-titres avec la tête de l’auteur dans un carré
|
||||
de 20 pixels à gauche, sur la même ligne ; le nom reste disponible au survol. Les demandes prennent la forme de
|
||||
cartes encadrées avec aperçu, auteur, catégorie et état. Chaque zone possède
|
||||
son propre défilement à la molette et sa barre de défilement. Le bouton +
|
||||
ouvre la rédaction, l'en-tête ouvre la liste complète. « Afficher la suite »
|
||||
ajoute la page suivante sans quitter le menu ni remettre le défilement à zéro.
|
||||
La lecture des publications longues et des conversations utilise le
|
||||
conteneur défilant natif existant.
|
||||
|
||||
## Périmètre technique
|
||||
|
||||
La carte utilise exclusivement les relevés personnels de l'atlas existant,
|
||||
sans chargement ou génération de chunks. Elle respecte le déblocage de
|
||||
l'atlas et les restrictions de dimension. Un clic ouvre la carte détaillée.
|
||||
Le serveur solo transmet les relevés existants pendant la pause, par lots bornés,
|
||||
sans observer de nouveaux blocs ni avancer la simulation. La limite de fréquence
|
||||
accepte aussi le temps réel lorsque les ticks du monde sont arrêtés.
|
||||
La texture et le flux réseau sont libérés en quittant le menu ; les réponses
|
||||
tardives ne sont acceptées que par l'écran courant et son jeton.
|
||||
|
||||
Les publications utilisent les mêmes opérations paginées du contrat v1,
|
||||
en fichier comme en MariaDB. Aucune migration, table ou modification du site.
|
||||
L'actualisation des premières pages intervient lorsque leur défilement est en
|
||||
haut ; une liste étendue reste stable jusqu'à sa réouverture.
|
||||
Les textes d'interface sont disponibles en français et anglais.
|
||||
|
||||
La sélection d'un screenshot pour la Gazette reste l'étape suivante.
|
||||
Les visages des demandes du tableau restent différés ; ceux de la Gazette
|
||||
et des conversations sont présents. Les futures entrées
|
||||
économiques ne sont pas des boutons factices.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Environnement : macOS Apple M1, Java Temurin 25, Minecraft 26.3, Fabric Loader
|
||||
0.19.5, Fabric API 0.160.5+26.3, Vulkan/MoltenVK 1.4.2. Base de développement
|
||||
MariaDB 12.3.3, distincte des données personnelles et du site public.
|
||||
|
||||
- Essais natifs Vulkan réussis dans les deux modes : fichier et MariaDB.
|
||||
Français et anglais, GUI 2/3/4 à 1280 × 960 pixels (jusqu'à 320 × 240 unités
|
||||
d'interface), contrôles contenus dans l'écran.
|
||||
- Six vrais boutons Sanctuary ; espacement uniforme de 4 pixels en haut et
|
||||
en bas ; avatars d’articles alignés à gauche des titres.
|
||||
Défilements indépendants à la molette, ajout de la treizième publication par
|
||||
pagination sans retour en haut, lecture jusqu'au bas des longs articles et
|
||||
demandes, réponse publiée et relue.
|
||||
- Options et Options du monde natives, reprise du jeu, ouverture de la carte
|
||||
détaillée puis retour au menu. Carte personnelle reçue en plusieurs lots
|
||||
pendant la pause ; l'horloge de simulation du monde reste immobile.
|
||||
- Logs : `build/pause155-client-file.log`, `build/pause155-client-database.log`.
|
||||
Captures : `build/pause155-{file,database}-screenshots/`, aperçu
|
||||
`build/pause155-preview.png`. Les articles et le terrain de ces captures
|
||||
proviennent exclusivement d'un monde de test plat et de données fictives.
|
||||
|
||||
`./gradlew check build` a terminé ses 252 GameTests : 229 réussites et les
|
||||
23 échecs déjà recensés dans beta.154 (mêmes identifiants, aucun ajout ni
|
||||
retrait). La commande échoue donc ; ce n'est pas un contrôle global vert.
|
||||
Log : `build/pause155-check-build.log`, comparaison :
|
||||
`build/pause155-server-failures.json` / `build/community-server-failures.json`.
|
||||
`./gradlew check build assemblePack -x :sanctuary:runGameTest` réussit :
|
||||
compilation, contrôles hors GameTests et assemblage. Les derniers ajustements
|
||||
visuels de disposition sont ensuite vérifiés par les scénarios client natifs
|
||||
et un nouvel assemblage des artefacts, sans réexécuter les 252 GameTests.
|
||||
Log : `build/pause155-build-pack.log`.
|
||||
Aucun déploiement personnel ni publication du canal packwiz.
|
||||
|
||||
Commande des essais natifs :
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true \
|
||||
-PsanctuaryQuickTests=true -PsanctuaryPause155ClientTests=true \
|
||||
-PsanctuaryClientGraphicsBackend=vulkan
|
||||
```
|
||||
|
||||
Le scénario MariaDB utilise les variables de développement documentées dans
|
||||
[COMMUNITY-154](community-beta154.md). Le schéma reste le contrat v1.
|
||||
La sélection de screenshots et la validation Windows restent ouvertes.
|
||||
|
||||
|
||||
## Artefacts locaux vérifiés
|
||||
|
||||
Assemblage final réussi avec `./gradlew build assemblePack -x :sanctuary:check`,
|
||||
après réussite des essais natifs de la disposition finale dans les deux modes.
|
||||
Export packwiz réussi. ZIP, version Fabric/Minecraft, JAR unique dans le MRpack,
|
||||
pilote MariaDB embarqué et absence des classes de test vérifiés. Les 29 fichiers
|
||||
shader sont identiques octet pour octet à beta.151. Les anciens JAR et le pack
|
||||
beta.154 sont conservés, leurs empreintes vérifiées.
|
||||
|
||||
Reçu : `build/pause155-artifacts.json`. Logs : `build/pause155-final-assemble.log`
|
||||
et `build/pause155-export.log`.
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.155.jar` — SHA-256 `e8ba34ddd2726eb48fcea2b56a6f5a2e0262354553378c5bc25ca6f5061d32dc`.
|
||||
- `build/Sanctuary-beta.155.mrpack` — SHA-256 `11be47c07edbb8180b4d6911a1eff7656ba2ce4168d35b96096519a2491e899f`.
|
||||
@@ -0,0 +1,33 @@
|
||||
# PAUSE-156 — reprendre la partie sous la colonne droite
|
||||
|
||||
Branche `codex/pause-resume-right-beta156`, livraison locale `beta.156`.
|
||||
Suite de [PAUSE-155](pause-redesign-beta155.md).
|
||||
|
||||
Le bouton natif Reprendre la partie se trouve en bas à droite, sous la colonne
|
||||
Gazette / tableau, avec le même alignement et la même largeur. Il conserve son
|
||||
action native et les libellés français / anglais de Minecraft.
|
||||
|
||||
La modification concerne uniquement sa géométrie. Les options restent en bas
|
||||
à gauche ; les publications, le stockage, la carte et les shaders conservent
|
||||
leur fonctionnement de beta.155. Aucune migration ni installation personnelle.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Le scénario client Vulkan existant passe en français et anglais, GUI 2/3/4 à
|
||||
1280 × 960 : alignement du bouton avec la colonne droite, largeur identique,
|
||||
absence de chevauchement avec le tableau, reprise effective du jeu. Les
|
||||
interactions communautaires du scénario passent également en mode fichier.
|
||||
Log : `build/pause156-client.log`, aperçu `build/pause156-preview.png`.
|
||||
|
||||
`./gradlew check build assemblePack -x :sanctuary:runGameTest` réussit.
|
||||
Export packwiz, intégrité ZIP, versions du mod / pack et contenu du JAR
|
||||
embarqué vérifiés. Les shaders sont inchangés et les artefacts beta.155
|
||||
conservés avec leurs empreintes. Logs : `build/pause156-build-pack.log`,
|
||||
`build/pause156-export.log`, reçu `build/pause156-artifacts.json`.
|
||||
La suite serveur complète de beta.155 compte 229 réussites / 252, avec les
|
||||
23 échecs historiques. Elle n’est pas relancée pour ce déplacement de bouton.
|
||||
|
||||
## Artefacts locaux
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.156.jar` — SHA-256 `c325a757cff6063fe3702e755a57a3abc13ad86e071762354a3c449504d51c6f`.
|
||||
- `build/Sanctuary-beta.156.mrpack` — SHA-256 `0537273dbfecb071d481f3f22982fbbb9a8e8a75581e38128ded87926ee699d0`.
|
||||
@@ -0,0 +1,82 @@
|
||||
# RENDER-147 — PBR proche, lune et portée 256 blocs
|
||||
|
||||
Branche `codex/pbr-close-range-beta147`, socle beta.146 `cf9a670`.
|
||||
Minecraft 26.3, Java 25 ; dépendances et formats de sauvegarde inchangés.
|
||||
|
||||
## Résultat
|
||||
|
||||
- Le prépassage des récepteurs PBR comparait encore les profondeurs natives
|
||||
avec un epsilon fixe de 0,0000001. En profondeur inversée, cette marge
|
||||
devenait trop stricte au voisinage de la caméra : de nombreux fragments
|
||||
alternaient entre le rendu natif et le PBR lors de mouvements minuscules.
|
||||
- La comparaison utilise maintenant une distance reconstruite avec la marge
|
||||
bornée de 5 mm déjà employée par les passes couleur. La sélection de la
|
||||
surface PBR la plus proche et son test de profondeur EQUAL restent stricts.
|
||||
Aucun décalage du maillage ni fondu supprimant le PBR près de la caméra.
|
||||
- La lune ne reçoit plus le renforcement spéculaire du soleil (×20 pour
|
||||
l’eau, ×4 pour les autres matières). Son coefficient lunaire et ses phases
|
||||
restent appliqués ; le grand reflet solaire est conservé.
|
||||
- Distance commune PBR/SSR : 16, 32 (défaut), 64, 128 ou 256 blocs.
|
||||
À 256, le rayon SSR peut aussi rechercher jusqu’à 256 blocs. Nombre de pas
|
||||
et budgets de maillage inchangés ; les limites du terrain visible/chargé,
|
||||
des reflets hors écran et du budget de maillage subsistent.
|
||||
- PBR 80 %, SSR 20 %, mélange des eaux et préférences de beta.146 conservés.
|
||||
Libellés FR/EN mis à jour.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Mondes de test jetables, macOS Apple M1. Aucune sauvegarde personnelle utilisée.
|
||||
|
||||
- Reproduction avant correction sous Vulkan : à 12 cm du sol de briques,
|
||||
59 442 / 262 272 pixels changent de plus de 12 niveaux RVB cumulés sous une
|
||||
rotation de 0,00002 degré. Le test échoue. Journal :
|
||||
`build/beta147-close-before-vulkan.log`.
|
||||
- Même scène après correction : 1 / 262 272 pixels à 12 cm ; également testé
|
||||
à 25 cm, 50 cm et 1 m. Suite PBR Vulkan réussie :
|
||||
`build/beta147-close-after-vulkan.log`.
|
||||
- Preuves avant/après dans `build/evidence/close-before-vulkan/` et
|
||||
`build/evidence/close-after-vulkan/`.
|
||||
|
||||
- Suite PBR et préférences OpenGL finale réussie : mêmes scènes proches,
|
||||
persistance des distances dont 256 blocs, FR/EN, OFF/zéro, intensités,
|
||||
rechargement, redimensionnement et effets combinés.
|
||||
Journal : `build/beta147-close-final-opengl.log` ; preuves :
|
||||
`build/evidence/beta147-close-final-opengl/`.
|
||||
|
||||
- Suite SSR Vulkan finale réussie : F5, intersections, portées dont 256,
|
||||
eau/verre/métal, OFF/zéro, rechargement et redimensionnement.
|
||||
Journal : `build/beta147-ssr-final-vulkan.log` ; preuves :
|
||||
`build/evidence/beta147-ssr-final-vulkan/`.
|
||||
- À PBR 80 % / SSR 20 %, le soleil conserve 22 697 pixels quasi blancs sur
|
||||
la scène aquatique 854 × 480. La lune seule en compte zéro, tout en modifiant
|
||||
visiblement 40 808 pixels par rapport au PBR OFF. Capture du bloom lunaire
|
||||
inspectée : reflet doux, sans plateau blanc.
|
||||
|
||||
- Suite SSR OpenGL finale réussie également : zéro pixel quasi blanc pour
|
||||
la lune, 22 111 pour le soleil et 40 749 pixels de reflet lunaire visibles.
|
||||
Intersections : 91 / 518 400 pixels changés sous rotation minuscule.
|
||||
Journal : `build/beta147-ssr-final-opengl.log` ; captures correspondantes
|
||||
dans `build/evidence/beta147-ssr-final-opengl/`.
|
||||
- `./gradlew check build` dans un instantané isolé : 252 tests serveur,
|
||||
229 réussis, 23 échecs, avec exactement les mêmes identifiants que beta.146.
|
||||
Journal : `build/beta147-check-build-isolated.log` ; comparaison :
|
||||
`build/beta147-server-failures.json`. La vérification globale reste en échec.
|
||||
Les derniers ajouts lune/256 concernent exclusivement le rendu client,
|
||||
ses réglages et ses tests ; ils sont validés par les suites finales ci-dessus.
|
||||
- Assemblage final réussi : `./gradlew build assemblePack -x :sanctuary:check`,
|
||||
après les contrôles purs et serveur exécutés dans l’instantané, sans les
|
||||
répéter. Journal : `build/beta147-build-pack.log`. Cette exclusion ne
|
||||
transforme pas le contrôle complet en réussite.
|
||||
|
||||
## Artefact
|
||||
|
||||
[Sanctuary-beta.147.mrpack](../build/Sanctuary-beta.147.mrpack), également copié
|
||||
à côté de beta.146 dans `sanctuary-beta/build/`. Aucun ancien pack remplacé,
|
||||
aucune installation Prism ou publication effectuée.
|
||||
|
||||
Archive ZIP vérifiée, un seul JAR Sanctuary beta.147, identique au JAR construit,
|
||||
shaders identiques aux sources finales, options FR/EN 256 présentes,
|
||||
Minecraft 26.3 / Fabric Loader 0.19.5 conservés. Taille : 10 519 851 octets.
|
||||
SHA-256 : `b8044243dbe160d5171ea584052cf5732393e4d91352c29c33b727af80c0a5bc`.
|
||||
Reçu : `build/beta147-artifacts.json`.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# RENDER-148 — PBR 50 % et reflet solaire diffus
|
||||
|
||||
Branche `codex/pbr-soft-sun-beta148`, socle beta.147 `96a36d9`.
|
||||
Minecraft 26.3, Java 25, dépendances et sauvegardes inchangés.
|
||||
|
||||
## Résultat
|
||||
|
||||
- PBR à 50 % par défaut, SSR toujours à 20 % et portée commune jusqu’à 256.
|
||||
- Les configurations anciennes contenant exactement PBR 80 % passent à 50 %.
|
||||
Un marqueur de migration conserve ensuite tout choix explicite, y compris
|
||||
un nouveau choix 80 %. Les autres anciennes intensités restent conservées.
|
||||
- Le reflet solaire de l’eau avait un renforcement ×20 et une compression
|
||||
exponentielle qui effaçait les détails dans une large zone blanche.
|
||||
Le renforcement passe à ×6 et le lobe est plus diffus (rugosité minimale
|
||||
de 0,42 au lieu de 0,34 pour le lobe céleste de l’eau).
|
||||
- Pour le soleil sur l’eau, fusion par texel entre éclaircissement vers le
|
||||
blanc et lumière douce fondée sur la couleur native. La pondération emploie
|
||||
une compression rationnelle, sans atteindre brutalement un plateau blanc.
|
||||
La texture et ses animations restent natives ; aucun bruit ajouté.
|
||||
- La lune conserve son intensité atténuée, sans renforcement solaire.
|
||||
Les lampes et matières solides conservent leur fusion spéculaire précédente.
|
||||
Le bloom reste réglable et suit la lumière réellement ajoutée.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Scènes plates jetables, Apple M1, Vulkan et OpenGL. Le test solaire contrôle
|
||||
une lumière largement visible et limite les pixels quasi blancs à moins de
|
||||
0,5 % de l’image, au lieu d’imposer une grande tache blanche.
|
||||
|
||||
Premier contrôle Vulkan à PBR 50 % / SSR 20 % : 149 908 pixels éclaircis,
|
||||
zéro pixel quasi blanc sur 854 × 480. Captures vues du dessus et à soleil bas
|
||||
inspectées : la texture reste visible dans la traînée lumineuse.
|
||||
Preuves : `build/evidence/beta148-preview/`.
|
||||
Rendu de la traînée solaire approuvé par le créateur après inspection de la capture.
|
||||
|
||||
- Suites SSR complètes Vulkan et OpenGL réussies : F5, intersections, portées,
|
||||
eau/verre/métal, OFF/zéro, lune, bloom, rechargement et redimensionnement.
|
||||
Journaux : `build/beta148-ssr-vulkan.log`, `build/beta148-ssr-opengl.log`.
|
||||
Sous OpenGL, 146 680 pixels éclaircis et zéro pixel quasi blanc dans la scène
|
||||
solaire. La lune reste visible sans saturation (35 865 pixels modifiés).
|
||||
Preuves : `build/evidence/beta148-ssr-vulkan/` et
|
||||
`build/evidence/beta148-ssr-opengl/`.
|
||||
|
||||
- Suites PBR/préférences Vulkan et OpenGL réussies : défaut 50 %, migration
|
||||
de l’ancien 80 %, conservation d’un nouveau choix explicite 80 %, valeurs
|
||||
personnalisées, FR/EN, proximité, OFF/zéro, intensités et rechargement.
|
||||
Journaux : `build/beta148-pbr-vulkan.log`, `build/beta148-pbr-opengl.log`.
|
||||
- `./gradlew check build` dans l’instantané isolé : 252 tests serveur,
|
||||
229 réussis, 23 échecs. Exactement les mêmes identifiants que beta.147,
|
||||
aucun nouvel échec. Journal : `build/beta148-check-build-isolated.log` ;
|
||||
comparaison : `build/beta148-server-failures.json`. Le contrôle global
|
||||
demeure donc en échec.
|
||||
- Assemblage final réussi : `./gradlew build assemblePack -x :sanctuary:check`,
|
||||
sans répéter les contrôles purs et serveur déjà exécutés dans l’instantané.
|
||||
Journal : `build/beta148-build-pack.log`. L’exclusion ne constitue pas
|
||||
une réussite du contrôle complet.
|
||||
|
||||
## Artefact
|
||||
|
||||
[Sanctuary-beta.148.mrpack](../build/Sanctuary-beta.148.mrpack), également copié
|
||||
à côté des autres versions dans `sanctuary-beta/build/`. Archive ZIP vérifiée,
|
||||
un seul JAR Sanctuary beta.148 identique au JAR construit, shaders finaux
|
||||
identiques aux sources, textes FR/EN 50 % et dépendances exactes vérifiés.
|
||||
Taille : 10 520 050 octets. SHA-256 :
|
||||
`597cc2a96eabc3cf0ebf8ac59b4f94cf7cabf0c773e1dce13c10736ca6fcba38`.
|
||||
Reçu : `build/beta148-artifacts.json`.
|
||||
Aucune publication ni installation Prism effectuée.
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Publications beta.144 à beta.166
|
||||
|
||||
Rattrapage des releases Gitea du 24 septembre 2026. La beta.144 était déjà publiée ; ses tags et fichiers restent inchangés. Les archives suivantes sont publiées avec leurs binaires conservés, sans reconstruire ni renuméroter les anciens mods.
|
||||
|
||||
| Version | Commit source | Fichiers (hors SHA-256) |
|
||||
| --- | --- | --- |
|
||||
| [beta.145](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.145) | `ed2d08827260` | sanctuary-beta.145.jar, Sanctuary-Test-beta.145.mrpack, Sanctuary-beta.145.mrpack, Sanctuary-Template-beta.145.zip |
|
||||
| [beta.146](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.146) | `cf9a670f4415` | sanctuary-beta.146.jar, Sanctuary-beta.146.mrpack, Sanctuary-Template-beta.146.zip |
|
||||
| [beta.147](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.147) | `96a36d99bea0` | sanctuary-beta.147.jar, Sanctuary-beta.147.mrpack, Sanctuary-Template-beta.147.zip |
|
||||
| [beta.148](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.148) | `6f871446bbbf` | sanctuary-beta.148.jar, Sanctuary-beta.148.mrpack, Sanctuary-Template-beta.148.zip |
|
||||
| [beta.149](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.149) | `1c23d0c17ec9` | sanctuary-beta.149.jar, Sanctuary-beta.149.mrpack, Sanctuary-Template-beta.149.zip |
|
||||
| [beta.150](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.150) | `448e2febff2a` | sanctuary-beta.150.jar, Sanctuary-beta.150.mrpack, Sanctuary-Template-beta.150.zip |
|
||||
| [beta.151](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.151) | `b97ec6c9e820` | sanctuary-beta.151.jar, Sanctuary-beta.151.mrpack, Sanctuary-Template-beta.151.zip |
|
||||
| [beta.154](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.154) | `0eb1a381d4d3` | sanctuary-beta.154.jar, Sanctuary-beta.154.mrpack, Sanctuary-Template-beta.154.zip |
|
||||
| [beta.155](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.155) | `3bfb3cb251a1` | sanctuary-beta.155.jar, Sanctuary-beta.155.mrpack, Sanctuary-Template-beta.155.zip |
|
||||
| [beta.156](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.156) | `32158e2e0513` | sanctuary-beta.156.jar, Sanctuary-beta.156.mrpack, Sanctuary-Template-beta.156.zip |
|
||||
| [beta.157](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.157) | `9791d178ab5e` | sanctuary-beta.157.jar, Sanctuary-beta.157.mrpack, Sanctuary-Template-beta.157.zip |
|
||||
| [beta.158](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.158) | `19aa8054a52f` | sanctuary-beta.158.jar, Sanctuary-beta.158.mrpack, Sanctuary-Template-beta.158.zip |
|
||||
| [beta.159](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.159) | `bdaad0c94032` | sanctuary-beta.159.jar, Sanctuary-beta.159.mrpack, Sanctuary-Template-beta.159.zip |
|
||||
| [beta.160](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.160) | `c1d22786745d` | sanctuary-beta.160.jar, Sanctuary-beta.160.mrpack, Sanctuary-Template-beta.160.zip |
|
||||
| [beta.161](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.161) | `8deba7cfb47d` | sanctuary-beta.161.jar, Sanctuary-Template-beta.161.zip |
|
||||
| [beta.162](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.162) | `0c55a375623b` | sanctuary-beta.162.jar, Sanctuary-Template-beta.162.zip |
|
||||
| [beta.163](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.163) | `905b3bcfa125` | sanctuary-beta.163.jar, Sanctuary-Template-beta.163.zip |
|
||||
| [beta.164](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.164) | `9a429c40a15b` | sanctuary-beta.164.jar, Sanctuary-Template-beta.164.zip |
|
||||
| [beta.165](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.165) | `dbb9e3428da2` | sanctuary-beta.165.jar, Sanctuary-Template-beta.165.zip |
|
||||
| [beta.166](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.166) | `460c4706d37a` | sanctuary-beta.166.jar, Sanctuary-Template-beta.166.zip |
|
||||
|
||||
## Historique et intégration
|
||||
|
||||
La PR [#1 de Chris](https://git.botsu.net/koka/sanctuary-beta/pulls/1), intitulée « beta.144 — Code d’accès », est intégrée par le merge `ad960ef`, puis corrigée et livrée dans `460c470` (beta.166). Son numéro de version historique ne remplace pas la release beta.144 dédiée à l’eau. `main` a été avancé en fast-forward sur beta.166. La PR est clôturée : Gitea refuse le mode « manually-merged » dans ce dépôt, elle reste donc affichée comme fermée plutôt que fusionnée. Sa branche et ses commits sont conservés.
|
||||
|
||||
Les beta.152 et beta.153 sont des chantiers shaders non finalisés : aucun commit de livraison ni JAR Sanctuary vérifiable retrouvé. Aucun tag ou binaire artificiel n’est créé pour remplir ces numéros. Les fichiers de travail locaux sont conservés.
|
||||
|
||||
## Contrôles et limites
|
||||
|
||||
- Intégrité ZIP, version Fabric et version source contrôlées pour chaque JAR ; sources Java archivées comparées aux commits de livraison.
|
||||
- Le ZIP de sources beta.145 est antérieur à une correction : il n’est pas publié. Le JAR correspond exactement au SHA-256 documenté dans le ticket, et la classe concernée correspond au binaire beta.146 dont les sources sont vérifiées.
|
||||
- Les MRpack embarquent le JAR de leur release, vérifié par SHA-256. Le MRpack beta.150 conservé contenait un binaire intermédiaire : une copie a été réassemblée avec le JAR final ; l’original local reste intact.
|
||||
- Chaque pièce jointe est retéléchargée anonymement et comparée à son SHA-256 local. Chaque release comporte son fichier SHA256SUMS.
|
||||
- Les anciens échecs de tests restent documentés dans les tickets liés aux releases historiques. Cette publication rétrospective ne prétend pas les avoir corrigés dans les anciens binaires.
|
||||
- La beta.166 a passé `check build assemblePack`, avec 252/252 GameTests ; voir [le contrat et les vérifications](inscription-web-beta166.md). Le parcours OAuth Discord et l’inscription sous le pseudo du dossier ont ensuite été testés localement.
|
||||
- Site compagnon : branche `codex/inscription-web-beta166`, commit `89973c1` publié. Les 12 tests ciblés accès/parrainage passent (162 assertions), y compris le refus d’un autre pseudo et la génération d’invitation refusée à un candidat en attente même administrateur.
|
||||
|
||||
Le canal packwiz et les installations personnelles ne sont pas modifiés par ce rattrapage. Aucun monde, ancien JAR ou secret n’est ajouté à Git. Les fichiers `.env`, sauvegardes SQL et configurations privées du labo restent locaux.
|
||||
@@ -0,0 +1,112 @@
|
||||
# RENDER-146 — Reflets, portée commune et transitions de l’eau
|
||||
|
||||
Branche `codex/reflections-water-beta146`, socle beta.145 `ed2d088`.
|
||||
Minecraft 26.3, Java 25 et dépendances inchangés. Préparation locale uniquement.
|
||||
|
||||
## Contrat
|
||||
|
||||
- Les passes PBR et SSR vérifient la profondeur finale du rendu natif pour
|
||||
masquer les personnages, notamment en F5. La profondeur du paysage capturée
|
||||
avant les transparences reste celle utilisée pour rechercher le reflet.
|
||||
- Le PBR utilise le test matériel EQUAL contre la profondeur du récepteur
|
||||
sélectionné. L’ancienne tolérance de 3,5 cm pouvait accepter des faces
|
||||
concurrentes près des coins ; elle est supprimée. La comparaison avec la
|
||||
profondeur native conserve seulement une marge de 5 mm pour les différences
|
||||
d’ordre de calcul des matrices, sans relâcher la sélection stricte du récepteur.
|
||||
La position du vertex est déclarée invariante entre les passes.
|
||||
- PBR à 80 % et SSR à 20 % par défaut, pour toutes les matières, eau comprise.
|
||||
Deux intensités d’effets distinctes, sans réglage séparé solide/liquide.
|
||||
Les préférences explicites déjà enregistrées restent conservées.
|
||||
- Distance PBR/SSR commune : 16, 32 (défaut), 64, 128 blocs. Maillages,
|
||||
sélection des récepteurs et fondu final emploient ce réglage. Le rayon SSR
|
||||
conserve sa recherche maximale de 128 blocs dans le paysage visible.
|
||||
Budgets de maillages inchangés : solides 24 Mio, transparences 8 Mio.
|
||||
Une forte densité à grande portée peut donc limiter les surfaces disponibles.
|
||||
- Le reflet du soleil adopte un lobe plus large sur l’eau, avec une irradiance
|
||||
renforcée : grande empreinte blanche vue du dessus et traînée étendue aux
|
||||
angles rasants. Les lampes conservent leur lobe local.
|
||||
Les éclats spéculaires ne sont plus plafonnés au gris moyen. Leur compression
|
||||
conserve une valeur finie pouvant approcher le blanc. Une contribution douce
|
||||
des seules passes PBR/SSR alimente le bloom existant, sans faire briller tous
|
||||
les blocs blancs. Le réglage Bloom et son intensité gardent autorité.
|
||||
- Eau naturelle OFF par défaut dans une dimension ordinaire, ON pour une
|
||||
dimension dont le générateur est Sanctuary / ExpansionChunkGenerator.
|
||||
Le serveur transmet ce contexte à la connexion, au changement de dimension
|
||||
et à la réapparition, y compris en multijoueur. Le défaut automatique nécessite
|
||||
ce contexte envoyé par un serveur beta.146 ; sans lui, le client utilise le
|
||||
défaut ordinaire OFF, toujours réglable. Aucune donnée de monde modifiée.
|
||||
Les choix ON/OFF sont mémorisés séparément pour ces deux catégories.
|
||||
Un ancien OFF explicite est conservé dans les deux ; un ancien ON global
|
||||
adopte les nouveaux défauts par contexte.
|
||||
- Mélange des profils sur 1×1, 3×3, 5×5, 9×9 (défaut) ou 17×17 blocs, puis
|
||||
interpolation aux sommets. Océans toujours clairs. La texture demeure native.
|
||||
Surface et immersion utilisent la même étendue. Les changements du fond
|
||||
invalident toute la largeur dépendante ; changer le réglage reconstruit les
|
||||
sections chargées contenant de l’eau.
|
||||
|
||||
Le filtre de surface est calculé dans la copie de région native, avec sommes
|
||||
préfixées par hauteur pour borner le coût des sommets. Aucun accès au monde
|
||||
vivant depuis le worker, chargement de chunk ou modification de sauvegarde.
|
||||
Les textes des nouvelles options sont fournis en français et en anglais.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Machine : Apple M1, macOS, Java 25, Minecraft 26.3. Mondes plats jetables,
|
||||
graine 122 ; aucune sauvegarde personnelle utilisée.
|
||||
|
||||
- Suite SSR/PBR Vulkan avec transparence améliorée : réussie
|
||||
(`build/beta146-final-settings-vulkan.log`). F5 avec personnage réel en créatif,
|
||||
portée 16/32/64/128, couleur du paysage, eau/verre/métal, OFF/zéro,
|
||||
immersion, rechargement de ressources et redimensionnement.
|
||||
- Suite SSR/PBR OpenGL finale réussie également
|
||||
(`build/beta146-final-settings-opengl.log`), avec 22 450 pixels quasi blancs
|
||||
dans la scène solaire et 99 / 518 400 pixels modifiés dans le contrôle
|
||||
des intersections. Captures dans `build/evidence/final-settings-opengl/`.
|
||||
- F5 : les 1 900 pixels du torse contrôlés sont strictement identiques entre
|
||||
SSR OFF et ON, tandis que le reflet reste présent autour du personnage.
|
||||
- Soleil : 22 111 pixels quasi blancs sur 854 × 480 à PBR 80 % / SSR 20 %,
|
||||
sans bloom, sur la scène de contrôle Vulkan. Les captures vues du dessus et
|
||||
à soleil bas montrent une empreinte large et une traînée allongée.
|
||||
Bloom OFF libère sa cible.
|
||||
- Mur, sol et escaliers en contact : 86 pixels sur 518 400 changent de plus
|
||||
de 12 niveaux cumulés lors d’une rotation de 0,00002 degré ; seuil du test
|
||||
0,1 %. Le cadre exclut l’eau animée, qui ne constitue pas une référence
|
||||
de stabilité pour les blocs statiques.
|
||||
|
||||
- Eau : suites fonctionnelles Vulkan et OpenGL réussies
|
||||
(`build/beta146-water-vulkan.log`, `build/beta146-water-opengl.log`).
|
||||
Profils, mélange 5/9/17, changement de contexte transmis par le serveur,
|
||||
préférences séparées, immersion, fond modifié, océans, eau courante,
|
||||
waterlogging et rechargement. OpenGL contrôle aussi l’égalité des couleurs
|
||||
aux frontières des copies de région et avec l’échantillonnage de brume.
|
||||
- `./gradlew check build` exécuté dans un instantané isolé des sources :
|
||||
252 tests serveur, 229 réussis et 23 échecs. Les identifiants des échecs
|
||||
correspondent exactement au reçu archivé beta.145 ; aucun nouvel identifiant.
|
||||
Il ne s’agit pas d’une nouvelle exécution comparative de beta.145.
|
||||
Journal : `build/beta146-check-build-isolated.log` ; comparaison :
|
||||
`build/beta146-server-failures.json`. Les derniers changements après cet
|
||||
instantané concernent le rendu client, ses réglages et ses tests.
|
||||
|
||||
Les preuves Vulkan finales sont dans `build/evidence/final-settings-vulkan/`.
|
||||
La suite de régression PBR et de préférences finale réussit
|
||||
(`build/beta146-final-pbr-preferences.log`) : PBR 80 %, SSR 20 %, persistance,
|
||||
migration des anciennes préférences, FR/EN, OFF/zéro et rechargement.
|
||||
La commande finale `./gradlew check build assemblePack -x :sanctuary:runGameTest`
|
||||
réussit (125 tâches), sans répéter la suite serveur isolée déjà exécutée.
|
||||
Journal : `build/beta146-final-build-pack.log`. Cette exclusion ne transforme
|
||||
pas les 23 échecs serveur en validation globale réussie.
|
||||
|
||||
## Artefact local
|
||||
|
||||
Le pack [Sanctuary-beta.146.mrpack](../build/Sanctuary-beta.146.mrpack) est exporté
|
||||
et copié dans le dossier `sanctuary-beta/build/`, à côté des autres versions.
|
||||
Intégrité ZIP, version beta.146, Minecraft 26.3 et identité du JAR embarqué
|
||||
avec le JAR construit contrôlées ; shaders finaux présents et identiques aux sources.
|
||||
Taille : 10 519 634 octets. SHA-256 :
|
||||
`9f3f7a840652d401741cbe3c85bbb5626326542179754cb599479b01a6b4c955`.
|
||||
Reçu : `build/beta146-artifacts.json`.
|
||||
|
||||
Aucun benchmark isolé de performance de beta.146 n’est revendiqué : des
|
||||
contrôles serveur peuvent tourner simultanément. Les limites SSR hors écran,
|
||||
de maillage à grande portée et de compatibilité avec d’autres GPU subsistent.
|
||||
Aucune publication ou synchronisation Prism.
|
||||
@@ -0,0 +1,196 @@
|
||||
# WG-ECO-177 — écologie liée au relief
|
||||
|
||||
Branche `codex/relief-ecology-beta177`, base beta.176 `96433d3` conservée.
|
||||
Demande : nouveau test solo à 32 chunks intégrant les retours R019–R021.
|
||||
|
||||
## Contrat
|
||||
|
||||
Nouveau preset de laboratoire `sanctuary_test:relief_ecology_v1`, graine 42.
|
||||
Aucune migration ni réouverture des anciens laboratoires avec de nouveaux
|
||||
chunks. Les sauvegardes beta.175 et beta.176 restent conservées.
|
||||
|
||||
- Plateau : forêts normales et plaines fleuries, sans bandes géographiques
|
||||
automne/cerisiers. Classification par hauteur de surface et couverture.
|
||||
- Creux à ciel ouvert : automne. Hauteurs : cerisiers. Une élévation locale
|
||||
autour d'un sommet existant crée un relief arboré, sous le plafond 320 ;
|
||||
la roche profonde et les cinq récifs conservent le champ précédent.
|
||||
- Sous une couverture de roche : cavités humides, mousse, chêne noir et
|
||||
champignons géants. Ne pas confondre sous-sol et vallée basse extérieure.
|
||||
- Marais sur un récif uniquement, aucune mangrove. Petits fragments hauts
|
||||
sobres, sans tapis fleuri. Températures chaudes et aucune décoration de gel.
|
||||
- Géologie : conserver des strates lisibles, les interrompre avec des
|
||||
gisements cohérents de pierre/andésite/granite/diorite ; deepslate profonde,
|
||||
ressources conservées, aucun ajout d'or ni de redstone.
|
||||
- Mares locales, pas d'hydrologie régionale, pas de structures natives.
|
||||
Villages et structures aériennes restent à concevoir, pas livrés ici.
|
||||
|
||||
Le signalement « map cassée » reste sans symptôme précis : le créateur ne se
|
||||
rappelle plus s'il parlait de la carte opérateur ou du monde. Vérifier le
|
||||
chemin de lecture de l'atlas et les données du nouveau monde ; ne pas
|
||||
inventer de diagnostic ni modifier une interface sans défaut identifié.
|
||||
|
||||
## Vérifications prévues
|
||||
|
||||
Relevés natifs du relief, biomes à la surface et sous roche, végétation des
|
||||
cavités, eau après ticks, absence de neige/glace/mangrove, ciel 512–639 libre,
|
||||
répartition minérale sur toute l'île 42 et carte lisible depuis les chunks.
|
||||
Compilation, `check build`, assemblages, puis ouverture du solo Vulkan à
|
||||
32 chunks avec commandes. La visite peut commencer dès les contrôles du
|
||||
laboratoire terminés pendant que la suite générale continue, conformément
|
||||
à la préférence exprimée pendant la visite précédente.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le profil CLI `terrain` sélectionne ce nouveau preset. Les anciens profils
|
||||
gardent leur générateur. Tout le code de génération nouveau est dans le
|
||||
module facultatif `sanctuary-test` ; le preset Sanctuary normal est conservé.
|
||||
|
||||
Le sélecteur de biomes partage une hauteur de surface par colonne de quatre
|
||||
blocs, mise en cache dans les limites de l'île. Une couverture de roche de
|
||||
plus de douze blocs prend priorité sur la tranche d'altitude : les grottes
|
||||
restent des cavités humides. À l'extérieur, les seuils initiaux sont Y≈237
|
||||
et Y≈264, avec une variation lente de trois blocs. Forêt et prairie fleurie
|
||||
se partagent la tranche intermédiaire par un bruit de grande échelle.
|
||||
Ces seuils sont des réglages de prototype, à juger pendant la visite.
|
||||
|
||||
Un sommet existant parmi douze candidats proches du centre reçoit une
|
||||
élévation locale douce, de rayon 96 blocs, uniquement entre Y=216 et 319.
|
||||
Le relief profond et le champ des récifs beta.175 sont conservés. Les trois
|
||||
récifs bas reçoivent jungle, forêt et marais ; les deux fragments supérieurs
|
||||
restent sobres. Le ciel 512–639 reste réservé à l'ISS.
|
||||
|
||||
Les biomes ont une température empêchant la neige aux altitudes du monde ;
|
||||
la décoration native `freeze_top_layer` est aussi retirée. Sous roche, les
|
||||
décorations natives de lush caves sont complétées par des tentatives bornées
|
||||
de chêne noir et de champignons géants dans les cavités existantes. Un arbre
|
||||
demande de l'air, un plafond de roche et un socle solide ; aucune salle n'est
|
||||
excavée. Le sol autour reçoit de la mousse, puis les features natives 26.3
|
||||
contrôlent et placent le végétal.
|
||||
|
||||
Sous la transition profonde, les lits rocheux précédents sont conservés.
|
||||
Plus haut, deux bruits tridimensionnels forment des gisements d'andésite,
|
||||
granite et diorite, avec des régions de pierre ordinaire et d'autres où les
|
||||
strates restent visibles. Les minerais et mares utilisent les composants
|
||||
précédents ; leurs quantités réelles sont remesurées après décoration.
|
||||
|
||||
## Reproduction
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/worldgen_lab.py prepare --profile terrain --seed 42 --run nouvel-essai
|
||||
python3 scripts/worldgen_lab.py server --profile terrain --seed 42 --run nouvel-essai --verify
|
||||
```
|
||||
|
||||
Ajouter `--whole-island` à la préparation et au serveur pour le recensement
|
||||
intégral. Le lanceur refuse de réutiliser un monde dont l'empreinte des
|
||||
sources a changé. Les mesures sortent dans `relief-ecology-v1-cold` et
|
||||
`relief-ecology-v1-warm`. `scripts/ecology_atlas.py` rend cartes, coupes et
|
||||
recensement minéral à partir de ces données natives.
|
||||
|
||||
## Mesures natives — graine 42
|
||||
|
||||
Série `science177b`, Java 25 / Minecraft 26.3, heap serveur 1 536 MiB.
|
||||
[Atlas et coupes](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/releves-beta177/graine-42/atlas.png)
|
||||
et [recensement minéral](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/releves-beta177/graine-42/minerals.png).
|
||||
L'archive conserve données, coupes binaires, identité et journaux.
|
||||
|
||||
| Contrôle | Résultat |
|
||||
| --- | ---: |
|
||||
| Densités profondes et aériennes identiques à beta.175 | 42 500 / 42 500 |
|
||||
| Témoins de biome sous couverture rocheuse | 358 / 358 en cavités humides |
|
||||
| Climats vérifiés contre la neige, neuf biomes × quatre altitudes | 36 / 36 |
|
||||
| Neige, glace et blocs de mangrove dans l'enveloppe recensée | 0 |
|
||||
| Végétaux natifs sous roche retrouvés dans les blocs | 8 chênes noirs, 3 champignons bruns, 6 rouges |
|
||||
| Mares générées / mares témoins stables pendant 240 ticks | 60 / 8 |
|
||||
| Ensembles de structures natives admissibles | 0 |
|
||||
| Plans hydrologiques régionaux calculés | 0 |
|
||||
| Air contrôlé en Y=512–639, autour des cinq récifs | 1 474 560 blocs |
|
||||
| Tuile d'atlas du spawn | 256 pixels lisibles, aucun chargement de chunk ajouté |
|
||||
|
||||
La première tentative `science177a` avait été rejetée par le test des
|
||||
chênes noirs. Les critères d'espace et de socle ont été assouplis : quatre
|
||||
cases sous le tronc au lieu d'une fondation plane de six blocs de large,
|
||||
et précontrôle d'air moins large. Le test passe dans le nouveau monde,
|
||||
avec les vérifications natives des collisions conservées.
|
||||
|
||||
La carte de surface échantillonnée tous les huit blocs contient 32,86 % de
|
||||
forêt, 24,87 % de prairie fleurie, 33,25 % d'automne et 8,36 % de cerisiers.
|
||||
Le reste, 0,66 %, correspond à de petits reliefs périphériques classés en
|
||||
vide par le sélecteur de surface. Ce ne sont pas des surfaces exactes au
|
||||
bloc près. Le sommet local atteint Y≈302 ; son centre est `(38, 296, 88)`.
|
||||
La proportion des vallées, les lisières et la silhouette restent à juger en jeu.
|
||||
|
||||
### Ressources sur toute l'île
|
||||
|
||||
Recensement réel de **2 000 chunks**, Y=0–639, après décoration, sans
|
||||
extrapolation des chunks du spawn. Variantes pierre et deepslate additionnées :
|
||||
|
||||
| Ressource | Blocs |
|
||||
| --- | ---: |
|
||||
| Cuivre | 202 008 |
|
||||
| Charbon | 144 464 |
|
||||
| Fer | 5 592 |
|
||||
| Lapis | 7 139 |
|
||||
| Diamant | **304** |
|
||||
| Or | 0 |
|
||||
| Redstone | 0 |
|
||||
|
||||
Tous les diamants sont en deepslate, sous Y=136, sans voisin d'air.
|
||||
Améthyste : 6 513 blocs ordinaires, 1 630 bourgeonnants et 269
|
||||
bourgeons/cristaux. La décoration et les nouvelles surfaces changent certains
|
||||
totaux par rapport à beta.176 ; aucune augmentation des paramètres de
|
||||
placement des minerais n'est faite dans ce lot.
|
||||
|
||||
Réouverture : mêmes cartes de surface/récifs, mêmes palettes et coupes de
|
||||
blocs octet par octet, mêmes sources d'eau après 240 ticks supplémentaires.
|
||||
Les 17 végétaux témoins sous roche sont retrouvés. Ces résultats concernent
|
||||
la graine 42 ; aucune généralisation à toutes les graines n'est revendiquée.
|
||||
|
||||
### Durées et portée
|
||||
|
||||
| Passage | Initialisation serveur hors bootstrap JVM | Processus → fin du diagnostic |
|
||||
| --- | ---: | ---: |
|
||||
| Création et recensement intégral | 6,126 s | 317,799 s |
|
||||
| Réouverture et recensement intégral | 0,961 s | 41,350 s |
|
||||
|
||||
Le recensement à froid prend 207,176 s. Ce coût de diagnostic ne tourne pas
|
||||
pendant la visite ; ces durées ont aussi été mesurées pendant les contrôles
|
||||
généraux du pack et ne constituent pas un benchmark isolé ni une mesure de FPS.
|
||||
|
||||
## Solo et livraison
|
||||
|
||||
Le client est entré dans `Sanctuary-Relief-177-Solo` le 29 septembre à
|
||||
22:07 CEST : serveur intégré, KokaLab à `(-9.5, 249, -4.5)`, backend Vulkan
|
||||
(MoltenVK 1.4.2 / Apple M1), distance de vue passée à **32 chunks** dans le
|
||||
journal. Commandes autorisées et mode créatif dans la copie neuve ;
|
||||
`/gamemode spectator` permet la visite libre. Les réglages de la visite
|
||||
beta.176 sont repris, shader de laboratoire désactivé. Les diagnostics ne
|
||||
sont pas activés dans le client.
|
||||
|
||||
Le monde provient de la copie du serveur scientifique arrêté proprement
|
||||
après réouverture. Les anciennes sauvegardes sont conservées. Aucun canal,
|
||||
serveur personnel ni instance Prism n'est mis à jour.
|
||||
|
||||
Repères d'observation en spectateur :
|
||||
|
||||
| Lieu | Commande |
|
||||
| --- | --- |
|
||||
| Sommet à cerisiers | `/tp @s 38 318 88` |
|
||||
| Chêne noir de cavité | `/tp @s -89 168 119` |
|
||||
| Champignon rouge de cavité | `/tp @s 23 169 -57` |
|
||||
| Récif forêt, ancienne place de la mangrove | `/tp @s -102 430 -172` |
|
||||
| Récif marais | `/tp @s 219 470 22` |
|
||||
|
||||
Compilation et diagnostics du laboratoire réussis.
|
||||
`./gradlew check build assemblePack assembleTestPack` : **BUILD SUCCESSFUL**
|
||||
en 25 min 50 s, **265/265 GameTests réussis**, contrôles statiques et smokes
|
||||
réussis. Le test de portage intermittent de beta.176 passe dans cette suite,
|
||||
sans modification du code des familiers ; son ancien échec n'est pas effacé.
|
||||
Versions, icône et hashes d'index du pack vérifiés ; distributions locales
|
||||
dans `build/packwiz` et `build/packwiz-test`. Journal complet archivé dans
|
||||
`releves-beta177/check-build.log`. Aucune nouvelle dépendance ajoutée.
|
||||
|
||||
L'esthétique attend les retours de visite. Les grands plans d'eau, cascades,
|
||||
structures aériennes, villages Sanctuary et station ISS restent hors de ce lot.
|
||||
Le contrôle d'atlas vérifie la lecture des données ; il ne diagnostique pas
|
||||
le signalement imprécis « map cassée » ni l'ensemble de son interface.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Revue PR #1 de Chris — intégration sur beta.165
|
||||
|
||||
PR : [beta.144 — Code d’accès à la création Hello World](https://git.botsu.net/koka/sanctuary-beta/pulls/1).
|
||||
Révision examinée : `8de569bbf188f7b76ede9235d3a2e5449907bcd5`.
|
||||
Socle local : beta.165 + audit documentaire ; les corrections de GameTests restent
|
||||
un chantier distinct. Dépôt web local examiné : `sanctuary-web-community`, commit
|
||||
`1feab73`. Vérification du distant web après fetch : `6640966b7306b36735c622cc4e75f31467e6f249`, aucun écart sur `AccessCodeService.php` et `routes/api.php`. Inventaire API : une PR mod ouverte, aucune PR web ouverte.
|
||||
|
||||
## Verdict
|
||||
|
||||
**Intégrable, mais ne pas fusionner telle quelle.** Le principal blocage est la
|
||||
reprise après consommation du code côté site. Aucun merge distant, publication,
|
||||
activation du contrôle d’accès ou commentaire à Chris n’a été effectué.
|
||||
|
||||
## Constats
|
||||
|
||||
### P1 — code consommé sans possibilité de reprendre l’accueil
|
||||
|
||||
Dans `ProgressionService.java` de la PR, lignes 375–382, le serveur appelle
|
||||
`redeem` puis ignore le résultat si la session s’est déconnectée avant le retour.
|
||||
Le site peut donc avoir consommé le code alors que le ledger local n’a jamais
|
||||
été écrit. Une coupure réseau après consommation, avant réception de la réponse,
|
||||
produit la même fenêtre.
|
||||
|
||||
Le site actuel (`AccessCodeService.php:71–72`) répond `already_used` sans
|
||||
`minecraft_username` ni `discord_id`. `AccessCodes.redeem` exige ces champs pour
|
||||
la reprise et refuse donc. De plus, même si le site est corrigé pour les renvoyer,
|
||||
`AccessCodes.verifyKey:50` transforme encore tout `already_used` en `code_used` ;
|
||||
`HelloWorldScreen.updateSelection` ne permet de confirmer que `code_recognized`
|
||||
ou un pending local. La réparation doit donc couvrir **le site, le serveur et
|
||||
le parcours client**, pas seulement ajouter deux champs à la réponse API.
|
||||
|
||||
Correction recommandée : un contrat de consommation rejouable pour le même compte
|
||||
et la même admission, avec identité confirmée par l’API serveur authentifiée ;
|
||||
réconciliation du résultat après déconnexion et reprise au prochain accueil.
|
||||
Un code révoqué, un autre compte et une requête non autorisée doivent rester
|
||||
refusés. Vérifier l’identité complète et la portée du reçu avant de l’accepter.
|
||||
Le ledger ne doit jamais conserver le code brut ni le jeton API.
|
||||
|
||||
La documentation actuelle de la PR dit qu’une déconnexion avant `redeemed`
|
||||
ne consomme pas le code : ce n’est pas garanti dès que le POST est parti.
|
||||
Le smoke teste un ledger après `remember`, pas la fenêtre avant cette écriture.
|
||||
|
||||
### P2 — compatibilité des clients existants à expliciter
|
||||
|
||||
Le nouveau contrôle de capacité `AccessPayloads.Gate` dans CONFIGURE est placé
|
||||
avant la recherche de l’habitant. Activer l’accès impose donc aussi la mise à
|
||||
jour des clients des habitants déjà enregistrés. La promesse « habitant existant
|
||||
entre sans écran » ne signifie pas compatibilité avec un ancien client.
|
||||
Décider et tester ce contrat avant activation : mise à jour coordonnée ou
|
||||
capacité exigée uniquement pour les accueils qui utilisent le code.
|
||||
|
||||
## Compatibilité avec nos changements
|
||||
|
||||
`git merge-tree --write-tree HEAD origin/pr-1` a été exécuté sans changer le
|
||||
checkout. Trace ignorée : `build/pr1-merge-tree.txt`.
|
||||
|
||||
- Conflits textuels uniquement dans `gradle.properties` et `packwiz/pack.toml`.
|
||||
**Ne pas reprendre beta.144** : réserver le prochain numéro libre au moment
|
||||
de la livraison, synchroniser les manifestes et garder les archives existantes.
|
||||
- Les changements de progression et Hello World s’appliquent sans conflit textuel
|
||||
au socle examiné. Cela ne constitue pas une preuve de compatibilité fonctionnelle.
|
||||
- Pas de changement proposé aux tables communautaires ou aux pages Gazette/Tableau.
|
||||
L’admission appelle une API HTTP du site ; elle n’utilise pas directement le
|
||||
stockage des publications. Tester quand même fichier et MariaDB en régression.
|
||||
- Sans URL/jeton, la fonctionnalité se désactive. Le laboratoire actuel ne doit pas
|
||||
être activé implicitement : ses comptes locaux doivent rester accessibles.
|
||||
- Le ledger ajoute `data/sanctuary-access.json` sans réécrire le registre des
|
||||
habitants. Prévoir sauvegarde et comportement de retour arrière avant activation.
|
||||
|
||||
## Plan d’intégration concret
|
||||
|
||||
1. Finaliser et enregistrer les corrections de tests séparément.
|
||||
2. Préparer une branche d’intégration depuis ce socle et importer le changement
|
||||
de Chris, en résolvant uniquement les numéros de version puis les adaptations
|
||||
réellement nécessaires. Conserver son attribution.
|
||||
3. Corriger et documenter ensemble le contrat de reprise site/mod ; écrire les
|
||||
tests du POST consommé avec réponse perdue et déconnexion concurrente.
|
||||
4. Valider l’accueil sans configuration, l’accueil configuré neuf, un habitant
|
||||
existant, client ancien, code invalide, mauvais pseudo, code révoqué, panne
|
||||
du site, jeton rejeté, deux confirmations et redémarrage entre les écritures.
|
||||
Vérifier qu’un seul habitant et un seul familier sont créés et qu’aucun coût
|
||||
n’est dupliqué.
|
||||
5. Jouer le parcours réel sur un serveur jetable avec API de test, puis rejouer
|
||||
les échanges Gazette/Tableau en fichier et MariaDB. Garder le site public
|
||||
communautaire en lecture seule.
|
||||
6. Livrer et taguer le nouveau compteur ; activer seulement sur le serveur
|
||||
explicitement visé, après configuration et sauvegarde.
|
||||
|
||||
Pas de compilation ni d’essai de handshake de cette PR revendiqués ici.
|
||||
La suite en cours concerne les tests actualisés de beta.165, pas le code de Chris.
|
||||
@@ -0,0 +1,30 @@
|
||||
# WG-ECO-178 — vallées bornées et grottes minérales
|
||||
|
||||
Retour de visite beta.177 : automne trop envahissant sur le plateau et les
|
||||
bords, cavités trop uniformément lush. Le créateur précise : roche dominante,
|
||||
petites eaux et mousse localisée ; priorité au solo, sans nouveaux atlas.
|
||||
|
||||
Branche `codex/rocky-ecology-beta178`, base `9613322`. Nouveau preset de labo
|
||||
`sanctuary_test:rocky_ecology_v1`, profil CLI `rocky`, graine 42. Aucune
|
||||
migration : le solo beta.177 ouvert reste sur son ancien générateur.
|
||||
|
||||
Automne limité aux sols Y=200–232, entourés de relief plus haut et sans
|
||||
chute périphérique. Sous Y=200 et sur les flancs raides : géologie exposée.
|
||||
Les grottes gardent surtout leur roche ; mousse en plaques, poches végétales
|
||||
rares, petits bassins retenus et décorations de stalactites. Les bosquets de
|
||||
chênes noirs/champignons restent ponctuels. Relief, récifs, minerais et
|
||||
absence d'hydrologie régionale conservent leurs composants précédents.
|
||||
|
||||
Contrôles ciblés de chargement et du comportement modifié avant le nouveau
|
||||
solo ; `check build assemblePack assembleTestPack` exigé pour la livraison.
|
||||
Pas de recensement complet ni de cartes scientifiques dans cette passe.
|
||||
Contrôle natif `solo178d`, graine 42 : réussi, 567 comparaisons de densité
|
||||
identiques au socle profond, aucun calcul d’hydrologie régionale. Dans les
|
||||
neuf chunks centraux contrôlés, 380 sols rocheux sur 405, 23 en mousse et
|
||||
cinq sources d’eau observées ; ce petit échantillon ne décrit pas toute l’île.
|
||||
|
||||
Solo neuf `visite178/rocky/42`, sauvegarde `Sanctuary-Rocky-178-Solo`,
|
||||
vue 32, simulation 12, commandes activées. Compilation et `check build assemblePack assembleTestPack` réussis
|
||||
(23 min 35 s, résultat du 29 septembre). Aucun ancien monde ni canal de distribution modifié.
|
||||
Ouverture confirmée le 29 septembre à 22:47 : Vulkan sur Apple M1,
|
||||
KokaLab connecté en solo et distance serveur passée à 32 chunks.
|
||||
@@ -0,0 +1,78 @@
|
||||
# beta.161 — correctifs de la séance du 23 septembre
|
||||
|
||||
Ticket : R002–R009 du carnet de séance. Branches `codex/session-fixes-beta161`.
|
||||
|
||||
## Contrat de données et migration additive
|
||||
|
||||
Aucun monde ni chunk n’est régénéré, aucun format existant n’est modifié.
|
||||
Le nouveau fichier `data/sanctuary-server-metrics.json` version 1 conserve le temps
|
||||
réel cumulé depuis l’activation du compteur, les morts connues par UUID et les
|
||||
UUID des figurants exclus. Au premier démarrage, les morts sont importées des
|
||||
statistiques Minecraft déjà présentes ; le temps antérieur n’est pas inventé.
|
||||
Les valeurs vivantes sont relevées côté serveur, sauvegardées toutes les 30 secondes
|
||||
et à l’arrêt. Les arrêts ne comptent pas dans la durée. Un crash peut perdre au
|
||||
plus la période depuis le dernier relevé enregistré. Un fichier corrompu ou
|
||||
modifié extérieurement arrête la collecte sans le remplacer.
|
||||
|
||||
En mode base, appliquer la migration Web additive `create_sanctuary_server_metrics_table`
|
||||
avant de lancer le mod. Elle crée une table indépendante, une ligne par server_id ;
|
||||
le schéma communautaire v3 et ses données restent inchangés. Le serveur publie un
|
||||
instantané toutes les 30 secondes et un état arrêté à l’arrêt propre. En cas de
|
||||
panne SQL le compteur local continue ; le site marque la dernière mesure périmée
|
||||
après 90 secondes. Une table absente ne bloque pas les publications communautaires.
|
||||
La suppression de cette nouvelle table au rollback perd uniquement les instantanés,
|
||||
recréés depuis le fichier local à la prochaine publication. Ne pas supprimer le fichier
|
||||
local pour revenir en arrière ; les anciennes versions l’ignorent.
|
||||
Un seul serveur actif par server_id, comme pour les publications. Les morts de tous
|
||||
les joueurs ayant des statistiques sont cumulées, y compris les joueurs déconnectés.
|
||||
Les figurants du laboratoire sont exclus des morts et des joueurs en ligne.
|
||||
|
||||
## Résultat livré
|
||||
|
||||
Liste des joueurs sur U (migration unique de Tab ; autres choix conservés), suivi
|
||||
et place unique expliqués dans les demandes. Site : retour au dessin d’origine,
|
||||
intendance limitée au titre, page officielle avec retour, pas de navbar de rubriques
|
||||
ni de sélecteur de langue ni d’incitation à se connecter dans les publications.
|
||||
Gazette en accordéon exclusif : le dernier article visible est ouvert au chargement,
|
||||
ouvrir un autre referme le précédent. Photo entière à gauche, extrait court à droite,
|
||||
visage/pseudo/date et lien de lecture alignés en bas. Calendrier jour/mois encadré,
|
||||
message officiel à droite, sans heure ; année dans le footer.
|
||||
Les pages publiques du site sont en lecture seule, y compris leurs anciennes
|
||||
routes d’écriture pour les comptes connectés et le staff. L’administration
|
||||
Intendance réservée au staff reste disponible. Les joueurs publient, répondent
|
||||
et suivent les demandes dans Minecraft. Compteurs réels et état périmé explicite.
|
||||
|
||||
## Vérifications
|
||||
|
||||
| Vérification | Résultat |
|
||||
| --- | --- |
|
||||
| `./gradlew check build assemblePack` | 252 GameTests : 229 réussis, les mêmes 23 échecs que beta.160, aucun nouvel échec dans cette passe. Comparaison dans `build/session161-server-failures.json`. |
|
||||
| Contrôles JVM et persistance fichier/SQL | Réussis, dont `SERVER_METRICS161_PASS` et `COMMUNITY154_SQL_PASS` dans `build/beta161-release.log`. |
|
||||
| Démarrage et raccourci U | `SESSION161_KEYS_PASS` : défaut U, migration unique depuis Tab, choix personnalisé conservé. |
|
||||
| Parcours natif communautaire | `SEARCH159_CLIENT_PASS mode=file` : photos/galerie, FR/EN, GUI 2/3/4, recherche, bulletin en lecture seule, suivi et infobulle du repère. Captures dans `mods/sanctuary/build/run/clientGameTest/screenshots/`. |
|
||||
| Web SQLite et MariaDB isolée | 29 tests, 252 assertions réussies ; lecture seule visiteurs/joueurs/staff, contrats de persistance, statistiques absentes/périmées/arrêtées. Pint et `git diff --check` réussis. |
|
||||
| Navigateur | Accordéon exclusif, lien vers l’article, calendrier sans heure, visage/auteur/date et lecture en bas ; vérification desktop et 390 × 844. |
|
||||
| Assemblage final | `./gradlew build assemblePack :sanctuary-test:exportDuoLaunch -x :sanctuary:check` réussi, après les vérifications ci-dessus. Exclusion ponctuelle des tests connus en échec, pas de suppression d’assertion. |
|
||||
| Laboratoire réel | Monde existant `duo-flat-160` relancé, serveur 768 MiB/client 2048 MiB, shaders désactivés. KokaLab non OP, 26 niveaux conservés. Deux figurants, compteur SQL et Web : 1 humain. Journal de durée persisté et exclusions vérifiées. |
|
||||
|
||||
Le premier démarrage de test a révélé une mauvaise déclaration du nouveau mixin :
|
||||
`PlayerListKeyMixin` au lieu de `client.PlayerListKeyMixin`. Elle est corrigée ;
|
||||
la classe et sa déclaration sont présentes dans le JAR final. Le test natif a
|
||||
ensuite réussi. L’assemblage avait redéclenché une seconde passe de GameTests
|
||||
serveur ; cette répétition a été interrompue, la première passe complète faisant
|
||||
foi. Logs distincts : `beta161-check-build.log`, `beta161-release.log`,
|
||||
`beta161-client.log`, `beta161-assembly.log`, sous `build/`.
|
||||
|
||||
Livraison locale uniquement : `mods/sanctuary/build/libs/sanctuary-beta.161.jar`
|
||||
et pack assemblé dans `build/packwiz`. SHA-256 du JAR :
|
||||
`c6e0424bf091c41827730bc260dad3b95b7c949fb0dcc41811f0ae3d94bce79c`.
|
||||
Les 29 ressources shader sont identiques à beta.160. Les anciens JAR beta.154–160
|
||||
sont conservés. Aucun canal public ni instance Prism personnelle n’a été modifié.
|
||||
Le téléchargement public du site reste l’ancien lien beta.151, hors de cette
|
||||
livraison locale. Les 23 échecs préexistants restent ouverts.
|
||||
|
||||
Le compteur de jours débute à l’activation de cette version : le fonctionnement
|
||||
antérieur n’était pas mesuré. Le site reçoit les nouveaux compteurs au rechargement
|
||||
de la page. La mesure d’une mort réelle supplémentaire n’a pas été provoquée sur
|
||||
le personnage de la séance ; l’import et l’incrément sont couverts par le test du
|
||||
journal et les tests de lecture Web.
|
||||
@@ -0,0 +1,87 @@
|
||||
# WG-SKY-175 — récifs rares au-dessus de Sanctuary Island
|
||||
|
||||
Branche `codex/sky-fragments-beta175`, issue du labo beta.174.
|
||||
|
||||
## Contrat de génération
|
||||
|
||||
Demande du créateur : conserver l'île et sa hauteur actuelle ; ajouter des
|
||||
fragments rares jusqu'à Y=512, puis laisser **Y=512–639 vide pour l'ISS**.
|
||||
Le plafond de construction reste 640. L'ISS elle-même n'est pas générée par
|
||||
ce lot. Les nouveaux blocs sont une idée ouverte, pas une palette décidée.
|
||||
Les relevés scientifiques remplacent les captures en jeu pour cet essai.
|
||||
|
||||
**Correction suivante du créateur : retirer les anciennes îles flottantes**
|
||||
dans cette variante. Le nouveau preset optionnel `sanctuary_test:sky_v1`
|
||||
emploie son propre réglage `sanctuary_test:sky_v1_10`. Il conserve le champ
|
||||
de l'île principale, sans sa couche `FloatingTerrain306`. Au-dessus,
|
||||
une poignée de volumes bornés, déterminés par la graine, reçoit une érosion
|
||||
3D à deux échelles. Ils deviennent plus petits et moins nombreux en altitude.
|
||||
Pas de recherche de sites, de chargement de chunks ni de plan hydrologique
|
||||
pour placer ces formes. Les matériaux existants restent provisoires.
|
||||
|
||||
Réservé aux **nouveaux mondes de laboratoire**, diamètre 724. Les presets
|
||||
`relief_v1` et Sanctuary normal restent stables. Aucun ancien monde n'est
|
||||
modifié ni complété ; pas de migration. Le lanceur exige un nouveau `--run`
|
||||
après changement de génération et conserve les expériences antérieures.
|
||||
Le module Sanctuary Test est nécessaire pour relire ce profil.
|
||||
|
||||
La conservation porte sur le relief : une nouvelle roche au-dessus d'une
|
||||
colonne peut modifier sa lumière ou le placement des décorations fondées sur
|
||||
la hauteur de surface. Ce profil ne représente pas encore l'écologie finale.
|
||||
|
||||
## Relevés prévus
|
||||
|
||||
**Suite de demande — minerais :** cuivre et charbon abondants, peu de fer,
|
||||
aucun or ni redstone, lapis et améthyste présents. Le diamant reste rare,
|
||||
uniquement enfoui dans la deepslate profonde ; contrôle d'enveloppe solide
|
||||
de deux blocs avant placement. Après un premier relevé de trois diamants sur
|
||||
81 chunks, le créateur précise : **des centaines sur l'île entière, pas sur
|
||||
81 chunks**. Les petits filons restent enfouis ; le comptage exhaustif doit
|
||||
servir à régler leur quantité totale. Les strates sont conservées, avec
|
||||
deepslate, basalte et roche noire. Les quantités sont à régler sur les mesures.
|
||||
Cette première répartition appartient au nouveau labo, pas aux mondes normaux.
|
||||
|
||||
- Graines 0, 42 et 173 ; comparaison bit à bit du champ inférieur avec le
|
||||
profil relief, bornes d'altitude, répétabilité et absence d'hydrologie.
|
||||
- Échantillonnage du champ natif compilé par Minecraft : coupes verticales,
|
||||
empreinte horizontale, épaisseur cumulée et fraction de roche par altitude.
|
||||
Indiquer le pas, le domaine, les limites et les empreintes des données.
|
||||
- Témoins dans de vrais chunks : roche aux fragments et ciel vide au-dessus
|
||||
de 512. Réouverture du monde ; conserver les données et les temps à froid.
|
||||
- Mesurer séparément arrivée serveur, demande de chunks et diagnostic.
|
||||
Comparer au labo relief avec le même protocole ; aucun chiffre anticipé.
|
||||
- `./gradlew check build assemblePack assembleTestPack`. Pas de déploiement
|
||||
dans Prism ni sur le canal public.
|
||||
|
||||
## État
|
||||
|
||||
Prototype local compilé et visité sur la graine 42. Les relevés de géométrie
|
||||
sur 0, 42 et 173 ont été produits ; la quantité de diamants a ensuite été
|
||||
ajustée et son total sur l'île entière reste à mesurer. La première tentative
|
||||
exhaustive a manqué de mémoire : elle ne valide pas la cible globale.
|
||||
La validation complète `check build` et les assemblages de livraison beta.175
|
||||
restent à terminer. Ne pas confondre cette visite avec une livraison validée.
|
||||
|
||||
Le [retour de visite](worldgen-retours-beta175.md) conserve les captures et
|
||||
précise les prochains essais : géologie, biomes, eau locale et retrait des
|
||||
mineshafts. Ces changements ne sont pas appliqués à cette sauvegarde.
|
||||
|
||||
## Contrat de copie pour la visite solo
|
||||
|
||||
Le créateur demande ensuite une vue à **32 chunks en solo**. Le serveur de
|
||||
visite `visite175` est sauvegardé et arrêté proprement, puis son monde est
|
||||
**copié** dans un dossier solo encore absent du client de ce même labo.
|
||||
Le monde serveur original est conservé. La copie garde le même code, les
|
||||
chunks, la graine, les identifiants de génération et les données du joueur.
|
||||
Aucun chunk n'est régénéré ni format converti. Seuls le nom de la copie et
|
||||
son autorisation native de commandes sont ajustés, après copie et avant
|
||||
ouverture. Le client conserve explicitement l'UUID de KokaLab.
|
||||
|
||||
Le rendu à 32 chunks a été confirmé dans les logs du serveur intégré. La
|
||||
simulation effective est à 12 à l'ouverture, malgré le réglage initial à 4 ;
|
||||
ne pas annoncer 4 comme valeur vérifiée du solo. Le shader reste désactivé.
|
||||
Un manifeste `solo.json` nomme cette copie. Relancement par
|
||||
`python3 scripts/worldgen_lab.py solo --profile sky --seed 42 --run visite175`.
|
||||
Le lanceur contrôle toujours l'empreinte de génération ; le serveur de visite
|
||||
sur 25581 n'est plus nécessaire pour cette copie locale. Le recensement
|
||||
scientifique utilise un autre monde, sur le port local 25582.
|
||||
@@ -0,0 +1,400 @@
|
||||
# Sanctuary — refonte Storyquest et priorités pour la 173
|
||||
|
||||
**Vision générale ouverte le 27 septembre, actualisée le 29 septembre 2026.**
|
||||
Le [fil rouge courant](storyquest-fil-rouge.md) situe le chantier et remplace
|
||||
les anciennes priorités par des parcours à éprouver. Le présent document
|
||||
conserve les intentions de la refonte et les sujets encore en réserve.
|
||||
Branche `codex/storyquest-beta173`, créée
|
||||
depuis **beta.172**, commit `55c745a`. Ce document décrit la refonte globale.
|
||||
Le premier lot de code réalisé ensuite est le [prototype du palais et de ses
|
||||
huit ancres](palais-prototype-beta173.md), en beta.173 ; les autres intentions
|
||||
restent à implémenter.
|
||||
|
||||
**Décision du 29 septembre :** le palais souterrain, ses accès et les
|
||||
raccordements au palais sont abandonnés. La refonte de Sanctuary Island part
|
||||
de plans géographiques indépendants. Le [concept du 28 septembre](palais-originel-iss-beta173.md)
|
||||
reste une archive ; les pistes ISS, chenil et ordinateurs ne sont pas annulées
|
||||
par l'abandon de ce bâtiment. Le rôle et la position du bloc originel restent
|
||||
à reprendre, sans nouvelle contrainte de hauteur décidée.
|
||||
|
||||
[État du socle et provenance](storyquest-socle-beta172.md) ·
|
||||
[Vision](vision.md) · [Discussion communauté et économie](ecosysteme-communaute-economie.md).
|
||||
|
||||
## La direction demandée
|
||||
|
||||
Sanctuary doit devenir une expérience commune, étrange et réconfortante —
|
||||
« Minecraft cursed & healed ». L'histoire se découvre en habitant ce monde :
|
||||
cuisiner, s'occuper d'un familier, explorer, construire, combattre, commercer
|
||||
et se retrouver font avancer des histoires qui se croisent.
|
||||
|
||||
Le **Storyquest** est le fil qui relie ces actions. Il commence dès le code de
|
||||
connexion et l'introduction en SGA, se poursuit dans les hordes, puis dans
|
||||
l'île, ses secrets et ses expansions. Le SGA permet de raconter indirectement.
|
||||
L'objectif est une expérience soignée et cohérente ; « premium » ne définit
|
||||
ici aucun modèle payant.
|
||||
|
||||
### Intentions confirmées par le créateur
|
||||
|
||||
- Refaire le menu pause et les pages **Habitant, Combat, Factions** ; retirer
|
||||
**Friends / Amis** du menu principal.
|
||||
- Relier des quêtes **individuelles, collectives, de groupes spontanés et de
|
||||
factions** dans le Storyquest.
|
||||
- Reprendre Sanctuary Island comme départ, son worldgen, une liste de bâtiments
|
||||
générés, ses lieux manquants et le spawn ; la forme du secret est à reprendre
|
||||
après l'abandon du palais souterrain le 29 septembre.
|
||||
- Organiser les **huit liaisons** de l'île, puis les **relais des expansions**.
|
||||
- Reprendre les nouveaux zombies de 26.2, les Mooblooms, les fantômes et la
|
||||
baleine fantôme ; retirer les phantoms.
|
||||
- Concevoir les navets, la loterie et le shop spécial autour de PNJ de
|
||||
l'ancienne partie qui circulent à la surface de l'île.
|
||||
- Faire des donjons des étapes de progression en réinterprétant les anciennes
|
||||
fins du jeu. **Cavernes / diamants, Nether, Océan et End** étaient les repères
|
||||
initiaux ; la redistribution du Nether et de l'End dans de nouvelles
|
||||
dimensions est depuis une piste à explorer, pas une géographie arrêtée.
|
||||
- Concevoir des apparitions de structures dans le ciel : bateaux pirates,
|
||||
diligences, arènes et donjons volants thématiques.
|
||||
- Optimiser le shader d'ambiance ; ajouter du vent en altitude et une ambiance
|
||||
sourde dans les profondeurs.
|
||||
- Relier le jeu, sanctuary-minecraft.net et Discord : connexion, événements,
|
||||
rappels, forum, messages et activités communautaires.
|
||||
- Réintroduire **It's Alive !**, sa cuisine combinatoire et ses aliments ;
|
||||
découvrir les produits préférés des familiers et leur donner un passif qui
|
||||
s'active ou s'intensifie lorsqu'ils sont portés sur la tête.
|
||||
- Fournir une véritable bibliothèque de constructions à assembler comme des
|
||||
LEGO avec les schematics et les outils de construction déjà présents.
|
||||
|
||||
Tout ce qui suit est une **proposition de conception**, sauf rappel explicite
|
||||
d'une décision. Les noms de quêtes, prix, puissances, lieux et conditions de
|
||||
déblocage ne sont pas encore arrêtés.
|
||||
|
||||
## Une histoire qui se découvre par ses effets
|
||||
|
||||
Proposition : construire un réseau de découvertes. Un joueur peut comprendre
|
||||
un indice par une ruine, un plat, une rencontre ou une épreuve ; ces chemins
|
||||
convergent vers des transformations communes. Les activités gardent leur
|
||||
intérêt même quand leur contribution à l'histoire n'est pas encore comprise.
|
||||
|
||||
Le SGA doit conserver des associations reconnaissables : un même motif rencontré
|
||||
dans l'intro, une carte de horde et une ancienne machine suggère un lien. Définir
|
||||
quelques motifs, leurs usages et les indices de compréhension avant de multiplier
|
||||
les textes chiffrés. Éviter de donner à chaque glyphe aléatoire une signification
|
||||
canonique après coup. Le code d'accès réel reste une donnée d'authentification :
|
||||
les indices de fiction utilisent des signes distincts, jamais le secret du joueur.
|
||||
|
||||
Le carnet Storyquest mémorise **ce que l'habitant a observé**, l'action qu'il peut
|
||||
tenter et les conséquences qu'il connaît. Il ne révèle pas l'arbre entier des
|
||||
secrets. Instructions de jeu, erreurs et narration d'accessibilité restent
|
||||
compréhensibles en FR/EN ; le SGA porte le mystère.
|
||||
|
||||
| Portée d'une quête | Exemple proposé | Règle à concevoir |
|
||||
| --- | --- | --- |
|
||||
| Individuelle | Reconnaître un motif, retrouver une recette, comprendre un familier | Découverte et récompense personnelles ; un serveur avancé ne saute pas toute l'histoire du nouvel arrivant |
|
||||
| Groupe spontané | Rejoindre une horde ou une expédition rencontrée en chemin | Contribution utile, participation tardive, départ et déconnexion ; le dernier coup ne définit pas seul la participation |
|
||||
| Faction | Préparer un relais, une cuisine ou un chantier d'expédition | Projet et ressources de faction ; adhésion/départ sans double attribution ; solo encore viable |
|
||||
| Collective au serveur | Rendre une liaison utilisable, stabiliser un lieu | Une transformation du monde, historique des contributions, accueil des joueurs absents lors de l'ouverture |
|
||||
|
||||
Une quête peut articuler plusieurs portées : observation personnelle, action en
|
||||
groupe, conséquence collective. Les preuves serveur et les récompenses sont
|
||||
distinctes. Une horde déjà généreuse en butin ne doit pas recevoir implicitement
|
||||
une deuxième distribution complète parce qu'elle sert une quête.
|
||||
|
||||
### Première boucle proposée
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Arrivée et motif SGA] --> B[Rencontre ou lieu sur l'île]
|
||||
B --> C[Préparer : cuisine, familier, construction]
|
||||
C --> D[Explorer ou rejoindre une épreuve]
|
||||
D --> E[Découverte et conséquence visible]
|
||||
E --> F[Gazette, tableau, rendez-vous]
|
||||
F --> C
|
||||
E --> G[Liaison et nouvelle exploration]
|
||||
G --> B
|
||||
```
|
||||
|
||||
Exemple à discuter : un motif de l'arrivée réapparaît dans une ruine proche ;
|
||||
une rencontre donne une raison d'y retourner préparé. Une épreuve permet de
|
||||
comprendre un fragment du lieu. Le joueur garde sa découverte, le groupe garde
|
||||
son résultat et le serveur voit un changement. Un article, une recette ou un
|
||||
plan donne une raison à d'autres joueurs de venir. Le secret n'est plus lié
|
||||
au palais ; l'épreuve, la révélation narrative et le premier changement restent
|
||||
à choisir. Le fil rouge courant propose de commencer par un trajet extérieur
|
||||
avec une halte et une ancre indépendante.
|
||||
|
||||
## Le menu pause accompagne la vie de l'île
|
||||
|
||||
Conserver la carte et les nouvelles du serveur comme repères. Le Storyquest
|
||||
doit montrer ce qui relie les activités, tandis que chaque page conserve une
|
||||
responsabilité claire. La disposition graphique vient après ces parcours.
|
||||
|
||||
| Page / entrée proposée | Ce que le joueur vient y faire |
|
||||
| --- | --- |
|
||||
| Storyquest / Storyquest | Retrouver une découverte, une piste suivie, une contribution et une conséquence connue |
|
||||
| Habitant / Inhabitant | Voir son identité, son parcours, son familier, les goûts découverts et l'état réel du passif |
|
||||
| Combat / Combat | Comprendre son familier au combat, consulter les épreuves et accéder aux duels/arènes existants |
|
||||
| Factions / Factions | Voir membres, rôles et projets partagés ; rejoindre un effort de faction |
|
||||
| Découvertes et progression | Consulter Blocodex, connaissances et capacités existantes sans dupliquer le carnet |
|
||||
| Gazette, tableau, calendrier | Lire les récits, proposer une rencontre et retrouver les rendez-vous |
|
||||
|
||||
Le menu ne doit pas activer une carte à distance ou effectuer une transaction
|
||||
auprès d'un PNJ absent sans règle de jeu décidée. Il donne une direction et
|
||||
permet de retrouver l'action dans le monde. Le bouton Friends est à retirer
|
||||
du titre, avec réagencement et navigation clavier ; cela ne décide pas du sort
|
||||
des autres fonctions sociales de Minecraft.
|
||||
|
||||
## Sanctuary Island : dessiner les destinations avant de régler le relief
|
||||
|
||||
La proposition est de décrire un **graphe de lieux** : ce que l'on aperçoit au
|
||||
spawn, ce que l'on cherche, où l'on revient, ce qui se trouve en dessous et ce
|
||||
qui ouvre l'horizon. La reprise du worldgen sert ce parcours. Elle rouvre la
|
||||
conception de l'île clôturée historiquement en alpha.30.7.
|
||||
|
||||
Le nouveau départ sur l'île doit être concilié avec les **quatre expéditions
|
||||
déjà ouvertes à la création** en beta.172. Préparer un contrat de nouveaux
|
||||
mondes ; conserver les anciennes parties et leurs régions. Aucune suppression
|
||||
d'île, réécriture de chunk ou activation n'est décidée par cette note.
|
||||
|
||||
### Liste initiale de lieux à travailler
|
||||
|
||||
| Lieu / famille | Rôle proposé | Décision encore nécessaire |
|
||||
| --- | --- | --- |
|
||||
| Spawn commun | Arrivée sûre, identité visuelle, place pour les premières installations | Vue d'arrivée, niveau d'aménagement et lien avec l'ancien choix d'un départ naturel |
|
||||
| Secret et bloc originel | Trace de l'histoire et lecture de l'état du monde, sans palais imposé | Nouvelle forme, implantation et première découverte ; aucun emplacement de remplacement décidé |
|
||||
| Huit liaisons | Huit horizons et usages distincts de l'île | Forme, répartition, coûts et ordre ; aucune règle « huit sur huit » imposée |
|
||||
| Observatoire | Regarder le ciel et reconnaître des signes | Maintien du lieu retenu dans la conception précédente, contenu observable |
|
||||
| Salle d'expansion | Trace d'une ancienne infrastructure réappropriable | Articulation avec les huit liaisons et les installations construites par les joueurs |
|
||||
| Atelier caché | Plans, fabrication et mémoire de l'ancienne partie | Quel premier plan et quelle transformation utile |
|
||||
| Grande traversée souterraine | Circulation et découverte du dessous de l'île | Entrées, passages et connexion au secret, sans confondre grottes et dimension Cavernes |
|
||||
| Haltes de PNJ | Rencontres et services économiques vivants | Personnages, itinéraires, horaires et solution lorsqu'un PNJ est inaccessible |
|
||||
| Donjon de départ | Première épreuve liée à une conséquence de progression | Objectif, préparation, boss éventuel, récompense et reprise après échec |
|
||||
| Relais d'expansion | Prolongement du réseau sur les nouveaux territoires | Différence avec liaison, ancre d'aventure et salle d'expansion |
|
||||
| ISS et structures en hauteur | International Sanctuary Station métallique, trace d'un ancien joueur, carte horizontale de l'île | Parcours vertical, fonction propre et éventuelle quête de familier ; éviter la duplication de l'observatoire |
|
||||
|
||||
Cette liste prépare la sélection ; elle ne réactive pas automatiquement les
|
||||
villes, clusters et bâtiments mis en réserve dans [WG-26](structures-conception.md).
|
||||
Chaque bâtiment devra avoir une fiche : ancien usage, histoire visible,
|
||||
emprise, accès, contraintes de terrain, ressources récupérables, contenu,
|
||||
statut obligatoire/facultatif, nombre et espacement, rotations, rôle narratif,
|
||||
plan éventuellement constructible et critères d'essai.
|
||||
|
||||
Versionner ensemble sélection des lieux et règles de placement. Pour chaque
|
||||
taille d'île, éprouver une même série de graines consignées, avec coordonnées,
|
||||
captures et temps de génération. Un lieu indispensable sans emplacement valide
|
||||
doit avoir une solution explicite ; le générateur ne peut simplement l'oublier.
|
||||
|
||||
## Donjons et anciennes fins du jeu
|
||||
|
||||
Le tableau conserve les **repères de progression du 27 septembre**, à
|
||||
réexaminer. La discussion « Concevoir la salle secrète » envisage des cavernes
|
||||
habitées et un Haut accessible par portail, avec redistribution de contenus du
|
||||
Nether/End. Quatre dimensions distinctes ne sont donc plus une hypothèse de
|
||||
travail acquise. Le [fil rouge courant](storyquest-fil-rouge.md) distingue ces
|
||||
intentions des propositions de l'assistant ; aucun remplacement de dimension
|
||||
n'est implémenté ni autorisé dans les anciennes parties.
|
||||
|
||||
| Branche | Rôle envisagé | Point à trancher avant génération |
|
||||
| --- | --- | --- |
|
||||
| Cavernes | Accès au diamant et approfondissement de l'exploration | Gisement, filière ou accès exclusif ; contrôler aussi coffres, échanges et autres sources si l'exclusivité est voulue |
|
||||
| Nether | Production, chaleur et préparation aux épreuves exigeantes | Place des portails vanilla, conditions d'accès, ressource ou capacité débloquée |
|
||||
| Océan | Exploration aquatique et infrastructures associées | Région océanique existante ou autre espace ; navigation, respiration, sortie et récupération |
|
||||
| End | Grand accomplissement et ouverture de possibilités nouvelles | Rôle du Dragon et du dénouement, lien avec les cycles personnels et la suite collective |
|
||||
|
||||
Le donjon doit définir ce qui change après sa réussite, qui en bénéficie et ce
|
||||
que fait un nouvel arrivant après cette réussite. Prévoir exploration, préparation,
|
||||
combat et métiers ; une faction de constructeurs ou de cuisiniers doit pouvoir
|
||||
contribuer. Ne pas assimiler automatiquement une fin de donjon à un New Game+.
|
||||
|
||||
### Structures célestes à la demande
|
||||
|
||||
Garder les **cartes de découverte** pour révéler un lieu existant, les **cartes
|
||||
d'épreuve** pour lancer une activité, et les **clés/reliques** pour un accès
|
||||
précis. Une horde actuelle fait apparaître des entités sans écrire de blocs :
|
||||
elle ne constitue pas encore un générateur de donjon.
|
||||
|
||||
Proposition pour une nouvelle structure : choisir un modèle autorisé → réserver
|
||||
une emprise 3D et un accès → enregistrer graine et version → préparer progressivement
|
||||
→ vérifier l'arrivée et la sortie → ouvrir l'aventure. Une interruption reprend
|
||||
la même réservation. Deux groupes ne créent ni structures superposées ni butins
|
||||
dupliqués. La visibilité d'un vide à l'écran ne prouve pas qu'il est libre.
|
||||
|
||||
Décliner ensuite bateau pirate, diligence, arène et donjon thématique. Commencer
|
||||
par un seul modèle. L'apparition d'une structure immobile et le déplacement
|
||||
réel d'un véhicule constituent deux problèmes distincts à décider. Son devenir
|
||||
après l'épreuve doit permettre retour, récupération et éventuelle appropriation ;
|
||||
aucun effacement automatique de constructions de joueurs n'est présumé.
|
||||
|
||||
## PNJ, économie et vie communautaire
|
||||
|
||||
Les PNJ sont des habitants de l'ancienne partie avec des occupations et des
|
||||
trajets, pas seulement des boutons de boutique. Leur présence doit produire
|
||||
des rencontres, des habitudes, des indices et parfois des rendez-vous.
|
||||
|
||||
Il faut concilier cette direction avec la cosmologie actuelle, qui décrit les
|
||||
anciens personnages comme disparus. Revenants, survivants, manifestations ou
|
||||
révision du canon sont des possibilités à discuter ; aucun choix n'est déduit
|
||||
du seul souhait de les voir marcher sur l'île. Ne pas attribuer d'office un
|
||||
métier ou le secret du spawn à un personnage nommé.
|
||||
|
||||
Pour une première économie, éprouver une semaine cohérente : PNJ des navets le
|
||||
dimanche → conservation et revente → péremption → rendez-vous de loterie → shop
|
||||
spécial et demandes de production. L'achat dominical, la revente en semaine et
|
||||
le pourrissement après une semaine viennent du cadrage du 25 septembre. Définir
|
||||
encore échéance exacte, fuseau serveur, cours communs, stocks, monnaie, financement,
|
||||
machines de loterie et devenir des pertes. L'approvisionnement du shop ne doit
|
||||
pas annuler les étapes des donjons. Le lien au ballast reste à concevoir.
|
||||
|
||||
| Surface | Fonction proposée dans la même boucle |
|
||||
| --- | --- |
|
||||
| Minecraft | Action, transactions, preuves de quête, apparition des lieux et attribution des récompenses, sous autorité serveur |
|
||||
| Site | Identité liée, candidature, mémoire consultable, calendrier, projets et préparation d'une prochaine session |
|
||||
| Discord | Discussion, forum, rendez-vous et rappels choisis, avec liens vers le bon objet du site ou du jeu |
|
||||
|
||||
Un événement ou projet possède une identité commune entre les surfaces. Prévoir
|
||||
changements d'horaire, annulations, messages déjà envoyés et reprise après panne.
|
||||
Les préférences règlent les rappels ; chaque action mineure en jeu ne devient
|
||||
pas une notification. Les fonctions de participation web devront disposer de
|
||||
leurs droits explicites : le contrat actuel Gazette/tableau côté site est en
|
||||
lecture seule. Préserver les découvertes secrètes lors de toute publication.
|
||||
|
||||
La participation web peut préparer un événement ou prolonger sa mémoire ; une
|
||||
discussion Discord ne constitue pas à elle seule une preuve de combat. Le jeu
|
||||
continue lorsque les services communautaires sont indisponibles, selon les
|
||||
règles d'accès déjà configurées. La synchronisation complète reste à construire.
|
||||
|
||||
## It's Alive ! et les familiers : découvrir, soigner, préparer
|
||||
|
||||
La reprise historique réunit les cultures, les postes culinaires, les recettes
|
||||
et les créatures. **Porter le module vers Minecraft 26.3** demande d'examiner
|
||||
ses dépendances réelles ; le JAR 26.2 ne peut être ajouté sur supposition.
|
||||
It's Alive conserve la responsabilité de ses systèmes autonomes. Sanctuary
|
||||
raccorde quêtes, économie et familiers par des contrats explicites.
|
||||
|
||||
Pour la cuisine, partir des combinaisons et substitutions existantes. Le livre
|
||||
révèle une recette sans empêcher de la deviner. La combinatoire souhaitée doit
|
||||
préciser jusqu'où elle va : catalogue de recettes par familles ou plats dynamiques
|
||||
aux propriétés calculées. Le second modèle n'est pas établi par l'ancien catalogue.
|
||||
|
||||
Proposition pour un familier : essayer un produit → observer une réaction →
|
||||
mémoriser un goût découvert → préparer un plat apprécié → constater l'effet du
|
||||
lien en exploration ou en combat. Définir goûts d'espèce et éventuelle préférence
|
||||
individuelle, sans attribuer dès maintenant un aliment favori à chaque créature.
|
||||
Une préférence doit survivre aux changements de forme et à la reconnexion.
|
||||
|
||||
| État proposé | Utilité recherchée | Vérification de jeu |
|
||||
| --- | --- | --- |
|
||||
| À côté du joueur | Personnalité, présence et rôle autonome perceptible | Observer une contribution pendant une horde et une activité calme |
|
||||
| Porté sur la tête | Passif activé ou renforcé, clairement indiqué | Porter/retirer change réellement l'effet ; aucune accumulation par changement rapide |
|
||||
| Nourri avec un produit apprécié | Relation et préparation valorisées | Réaction compréhensible, goût mémorisé, effet mesurable et borné |
|
||||
|
||||
Le choix **passif seulement porté** ou **passif de base renforcé sur la tête**
|
||||
reste ouvert. Réexaminer la désactivation actuelle des passifs en combat,
|
||||
les chapeaux, le slot familier, les piles d'entités et l'état monté. Commencer
|
||||
avec quelques espèces contrastées. L'incubation d'XP évoquée le 25 septembre
|
||||
reste une piste distincte, à articuler sans ajouter un rendement implicite.
|
||||
|
||||
Le 28 septembre, le créateur propose aussi un **chenil physique** : déposer
|
||||
un familier pour lui faire gagner de l'XP, avec deux yeux visibles sur le bloc
|
||||
occupé et de possibles multiblocs adaptés aux tailles. Cette piste est détaillée
|
||||
dans la [fiche palais / ISS / incubateurs](palais-originel-iss-beta173.md) ; elle
|
||||
ne remplace pas encore l'incubation d'XP liée aux actions évoquée précédemment.
|
||||
|
||||
Pour les créatures, préparer les cinq rôles d'Infectés, les seize variantes de
|
||||
Moobloom, les fantômes et la baleine ; traiter Bracken comme candidat historique
|
||||
à discuter. Le retrait des phantoms doit couvrir leurs apparitions, les cartes
|
||||
de horde, les œufs, les familiers et les contenus sauvegardés. Prévoir une règle
|
||||
explicite pour les anciens exemplaires ; ne pas supprimer silencieusement les
|
||||
compagnons ou les cartes déjà possédés. La baleine garde une place de respiration
|
||||
et de surprise dans une île qui peut aussi devenir inquiétante.
|
||||
|
||||
## Construire et ressentir ce monde
|
||||
|
||||
La bibliothèque de plans doit proposer des ensembles cohérents : abris,
|
||||
passerelles, cuisine, ateliers, haltes, modules de faction et grandes constructions.
|
||||
Un kit comporte aperçu, dimensions, liste des matériaux, étapes, variantes,
|
||||
points de raccord et fonction réelle. Commencer par un petit kit complet,
|
||||
combinable et réalisable en survie, puis élargir. Les schematics personnels et
|
||||
le Métabli restent les bases ; le chantier partagé persistant est encore à concevoir.
|
||||
|
||||
Les plans peuvent devenir des trouvailles ou des récompenses, et les ruines
|
||||
montrer une ancienne version de ce que les joueurs sauront construire. Tous
|
||||
les plans utilitaires de départ n'ont pas besoin d'être verrouillés par le récit.
|
||||
|
||||
L'ambiance accompagne les mêmes lieux : vent en altitude, poids sourd des
|
||||
profondeurs, calme de surface et signes SGA ponctuels. Définir altitude absolue,
|
||||
distance au sol et abri pour éviter du vent permanent dans une maison élevée.
|
||||
Fondus, réglages de volume et indices visuels évitent les ruptures et rendent
|
||||
les indices utilisables sans le son. Mesurer le shader sous **Vulkan**, avec
|
||||
scènes, résolution, machine et réglages consignés ; séparer coût des effets,
|
||||
chargement du terrain et densité des entités avant de changer les qualités.
|
||||
|
||||
### Ce que les inspirations doivent apporter
|
||||
|
||||
Ces références expriment des sensations recherchées, pas des fonctionnalités
|
||||
déjà choisies ni un import de personnages ou de ressources.
|
||||
|
||||
| Inspiration citée | Traduction proposée pour Sanctuary |
|
||||
| --- | --- |
|
||||
| Nintendogs | S'attacher à un compagnon par des soins et réactions reconnaissables |
|
||||
| Animal Crossing | Habitudes, habitants itinérants, dimanche, économie et souvenirs |
|
||||
| DOOM | Combats lisibles, mouvement, rythme et rôles ennemis distincts |
|
||||
| Undertale | Étrangeté, humour, tendresse et relecture de ce que l'on croyait comprendre |
|
||||
| Pokémon | Collection, découverte des préférences et complémentarité des familiers |
|
||||
| Fire Emblem | Liens entre personnages, rôles tactiques et projets de groupe |
|
||||
| Final Fantasy | Monde habité, donjons marquants, progression et grands moments partagés |
|
||||
| LEGO | Kits compréhensibles, assemblage modulaire et liberté de recombinaison |
|
||||
|
||||
## Réserve de conception et priorités courantes
|
||||
|
||||
La beta.173 a livré le prototype du palais, pas toute la refonte. Depuis le
|
||||
29 septembre, l'ordre de travail par systèmes est remplacé par les
|
||||
[parcours jouables du fil rouge](storyquest-fil-rouge.md#avancer-par-parcours-jouables).
|
||||
Les anciens noms de lots ci-dessous restent des repères de conception,
|
||||
pas des tickets externes créés ni des développements en cours.
|
||||
|
||||
| Sujets en réserve | Contribution à choisir lorsqu'une scène en a besoin |
|
||||
| --- | --- |
|
||||
| SQ-01 / SQ-02 — Storyquest | Une découverte et une conséquence persistante, sans doublons de récompense et avec accueil des nouveaux arrivants |
|
||||
| WG-173 / EXP-173 — île et liaisons | Une destination et un accès réellement jouables ; génération versionnée et reprise sous contrat de nouveaux mondes |
|
||||
| UI-173 — interfaces | Les informations nécessaires au trajet ; retrait de Friends ; refonte FR/EN, clavier et petits GUI à éprouver |
|
||||
| PET-173 / FOOD-01 — familier et cuisine | Une préparation qui change réellement l'exploration ; comportement et sauvegarde vérifiés, compatibilité 26.3 examinée |
|
||||
| LIFE-01 — créatures | Un rôle utile à une épreuve ou un lieu ; port vérifié et devenir explicite des phantoms existants |
|
||||
| NPC-01 / ECO-01 — habitants et économie | Une rencontre récurrente et un service éprouvé avant de généraliser les navets, la loterie et le shop |
|
||||
| BUILD-173 — plans | Un premier kit constructible et utile sur le terrain retenu |
|
||||
| DUN-01 / SKY-01 — donjons et ciel | Une entrée, une épreuve et un retour sûrs ; chaque lieu sert une conséquence choisie |
|
||||
| AMB-173 — ambiance | Les sons et le rendu de la scène courante, comparés sous Vulkan |
|
||||
| WEB-173 — communauté | Prolonger un rendez-vous ou un projet réel, avec identité, droits, préférences, annulation et dédoublonnage |
|
||||
|
||||
## Les choix à prendre ensemble
|
||||
|
||||
1. Quel premier trajet extérieur fait comprendre l'île et une ancre ? Quelle
|
||||
esthétique lui convient, et où l'état du bloc originel devient-il lisible ?
|
||||
2. Qui sont les anciens PNJ présents à la surface, compte tenu de leur
|
||||
disparition dans le récit actuel ?
|
||||
3. Quel premier fragment voulons-nous faire jouer, et quelle trace visible
|
||||
laisse-t-il pour la personne, le groupe et le serveur ?
|
||||
4. Les huit liaisons offrent-elles des choix dès le départ, des branches ou
|
||||
un ordre partiel ? Que deviennent les quatre anciennes expéditions dans
|
||||
les nouveaux mondes ?
|
||||
5. Le passif du familier apparaît-il uniquement sur la tête, ou y devient-il
|
||||
plus fort ? Quels premiers compagnons doivent faire ressentir la différence ?
|
||||
6. Quelle fonction donne envie de retourner à l'ISS ? Comment le chenil
|
||||
complète-t-il l'aventure avec son familier ? Le choix d'un mod d'ordinateurs
|
||||
reste ouvert après définition du parcours de préparation des expéditions.
|
||||
|
||||
Les montants de l'économie et l'ordre des quatre grands donjons viennent après
|
||||
ces décisions. On peut dessiner les interfaces avec des exemples explicitement
|
||||
fictifs, sans figer leurs règles ni les présenter comme des données du serveur.
|
||||
|
||||
## Réalisation et validation de ce cadrage
|
||||
|
||||
Le cadrage initial a livré la branche, l'inventaire sourcé et des propositions.
|
||||
Le prototype beta.173 réalisé depuis a sa propre fiche de validation. La
|
||||
révision du 29 septembre actualise la direction, les priorités et les liens
|
||||
depuis README, vision et backlog. Code, versions, sauvegardes et distribution
|
||||
inchangés par cette révision ; aucun test de jeu relancé pour la documentation.
|
||||
|
||||
Chaque futur lot de code devra exécuter `./gradlew check build`, puis
|
||||
`./gradlew assemblePack` si la distribution change, et ses essais comportementaux
|
||||
ciblés. Une évolution des sauvegardes ou de la génération exige son contrat
|
||||
avant implémentation. beta.173 est déjà utilisée ; la prochaine livraison de
|
||||
code consommera le prochain numéro disponible selon le compteur du dépôt.
|
||||
@@ -0,0 +1,255 @@
|
||||
# Sanctuary Island — fil rouge de la refonte
|
||||
|
||||
**État au 29 septembre 2026.** Point d'entrée pour la conception en cours sur
|
||||
`codex/storyquest-beta173`. Ce document remplace les anciennes priorités de
|
||||
[Storyquest](storyquest-beta173.md) et le projet d'intégration du palais.
|
||||
Il sépare les décisions du créateur, le socle vérifié et les propositions de
|
||||
travail. Les étapes ci-dessous sont un ordre de développement à discuter,
|
||||
pas un ordre de déblocage imposé aux joueurs.
|
||||
|
||||
**Priorité révisée pendant la séance : rendre le worldgen rapide à essayer.**
|
||||
Le créateur veut commencer par la génération de Sanctuary Island et couper
|
||||
temporairement les calculs lourds dans **un labo uniquement**. Le
|
||||
[lot WG-LAB-174](worldgen-lab-beta174.md), sur `codex/worldgen-lab-beta174`,
|
||||
passe avant les maquettes de halte : relief → hydrologie mesurée → lieux
|
||||
indépendants → détails réunis. Les parcours Storyquest restent la destination
|
||||
de cette reprise ; ils ne bloquent pas le travail sur le terrain.
|
||||
|
||||
**Étape 0 livrée localement en beta.174 :** labo de relief, comparaison complète,
|
||||
mesures, trois graines contrôlées et validation Vulkan. La prochaine itération
|
||||
peut travailler les formes et les parcours de l'île sur cette base ; le
|
||||
nouvel algorithme d'hydrologie et la nouvelle géographie ne sont pas encore faits.
|
||||
|
||||
**Suite décidée : [WG-SKY-175](sky-fragments-beta175.md).** Garder l’île
|
||||
principale et sa hauteur ; remplacer les anciennes îles flottantes par des
|
||||
récifs rares jusqu’à 512, puis réserver 512–640 à l’ISS. Le créateur préfère
|
||||
les relevés scientifiques aux vues en jeu. Répartition demandée : beaucoup
|
||||
de cuivre et charbon, peu de fer, pas d’or ni de redstone ; lapis, améthyste
|
||||
et deepslate, diamant rare et enfoui. Ces changements sont éprouvés dans
|
||||
un profil de labo neuf ; l’ISS et les éventuels nouveaux blocs restent à concevoir.
|
||||
|
||||
**Retour de visite du 29 septembre :** le plateau est conservé. La
|
||||
[fiche de reprise beta.175](worldgen-retours-beta175.md) relie les captures
|
||||
aux prochains essais : strates rocheuses plus continues, grands secteurs
|
||||
automnaux et cerisiers, identité écologique des récifs et petites eaux
|
||||
locales. Le créateur retire les mineshafts de l'île de départ et réserve
|
||||
leurs variantes aux expansions ; les villages doivent être propres à
|
||||
Sanctuary. Les constats de code et les propositions y sont séparés des
|
||||
changements effectivement livrés. La sauvegarde de visite reste le témoin.
|
||||
|
||||
**Essai suivant réalisé : [WG-ECO-176](island-ecology-beta176.md).** Nouveau
|
||||
labo avec strates ondulées, larges régions automnales et cerisiers, récifs
|
||||
végétalisés et mares locales. Trois graines et une réouverture passent les
|
||||
contrôles natifs ; aucune hydrologie régionale ni structure native admise.
|
||||
Le recensement de toute l'île 42 donne 336 diamants enfouis. La visite sert
|
||||
maintenant à régler les proportions et les transitions ; ce profil ne remplace
|
||||
pas encore la génération normale ni les anciens mondes.
|
||||
|
||||
## Le changement de cap
|
||||
|
||||
**Correction demandée après cette visite : [WG-ECO-177](relief-ecology-beta177.md).**
|
||||
Le plateau doit redevenir forestier et fleuri. L'automne suit les vallées
|
||||
extérieures, les cerisiers les hauteurs ; les cavités couvertes restent
|
||||
humides, moussues, avec chênes noirs et champignons. Retirer la neige et la
|
||||
mangrove, réserver le marais aux récifs, laisser les fragments supérieurs
|
||||
plus sobres. Conserver les strates appréciées et les mêler à des gisements
|
||||
de roche. Le créateur demande un nouveau solo à 32 chunks ; état des
|
||||
vérifications et limites dans la fiche du lot. Ce choix remplace les bandes
|
||||
de surface beta.176 pour le nouvel essai uniquement.
|
||||
|
||||
**Décidé le 29 septembre :** abandonner le palais souterrain, ses accès par
|
||||
chute dans l'eau et escaliers, et les raccordements des lieux au palais.
|
||||
Concevoir des plans géographiques indépendants. Refaire **Sanctuary Island,
|
||||
l'île de départ** : « Nether Island » dans la dictée désignait cette île,
|
||||
comme le créateur l'a précisé ensuite.
|
||||
|
||||
Les huit ancres, leurs matériaux distincts et les couleurs du bloc originel
|
||||
ne sont pas annulés par cette décision. Leur implantation et leur relation
|
||||
de jeu sont à reprendre sans dépendance architecturale au palais. La règle
|
||||
« le bloc suit le palais » n'a donc plus de lieu auquel s'appliquer ; aucune
|
||||
nouvelle position du bloc, à Y=0 ou ailleurs, n'est décidée.
|
||||
|
||||
Le palais beta.173 reste un prototype technique documenté. Son abandon dans
|
||||
la conception ne supprime ni son code ni sa sauvegarde de laboratoire. Son
|
||||
intégration sous l'île sort du plan de travail.
|
||||
|
||||
| Statut | Ce que l'on garde en tête |
|
||||
| --- | --- |
|
||||
| Direction confirmée | Entrée SGA, histoire découverte par les activités, Sanctuary Island comme départ, huit ancres à demandes de matériaux distincts, ambivalence « cursed & healed » |
|
||||
| Architecture abandonnée | Palais souterrain central, accès associés, disposition des lieux dictée par les raccordements au palais |
|
||||
| Encore ouvert | Esthétique de l'île, emplacement et rôle du bloc originel, géographie des ancres, ordre des découvertes, devenir des dimensions |
|
||||
| Idées conservées à explorer | ISS, cuisine, familiers utiles, chenil, plans constructibles, habitants, économie et vie communautaire ; elles n'exigent pas de palais |
|
||||
|
||||
## Où nous en sommes réellement
|
||||
|
||||
| Socle | État documenté | Conséquence pour la suite |
|
||||
| --- | --- | --- |
|
||||
| beta.172 | Arrivée et code SGA, hordes par cartes, interfaces communautaires, familiers et outils de construction existants | Partir de ces gestes ; le Storyquest qui les relie reste à développer |
|
||||
| Île et expansions | Quatre régions d'expédition déjà ouvertes à la création dans le socle audité | Un départ limité à Sanctuary Island demande un contrat explicite de nouveaux mondes |
|
||||
| Prototype beta.173 | Palais variant par seed ; huit dépôts ; bloc éteint, monochrome lumineux, puis huit gemmes indépendantes ; sauvegarde vérifiée | Réutiliser les mécaniques utiles après examen de leur dépendance au laboratoire ; aucune expansion n'est déclenchée |
|
||||
| Validation beta.173 | `check build assemblePack`, 265/265 GameTests, essai client Vulkan et reprise du monde consignés | Preuves du prototype existant, pas validation de la nouvelle géographie |
|
||||
| Refonte globale | Menus, récit persistant, nouvelles dimensions, économie, reprise d'It's Alive et nouvelle bibliothèque de plans restent des chantiers | Choisir une contribution utile par scène plutôt que lancer tous les systèmes ensemble |
|
||||
|
||||
Sources : [audit du socle beta.172](storyquest-socle-beta172.md) et
|
||||
[livraison locale beta.173](palais-prototype-beta173.md). Cette remise à plat
|
||||
est documentaire ; elle ne constitue pas un nouvel audit du code ni un essai
|
||||
de jeu. beta.173 est déjà utilisée, sans publication du canal ou mise à jour
|
||||
de Prism par ce chantier.
|
||||
|
||||
## Un fil rouge proposé : remettre l'île en relation
|
||||
|
||||
Le joueur arrive dans un monde qui a déjà servi. Il reconnaît des signes,
|
||||
comprend un usage ancien, prépare une sortie et rend un lieu de nouveau utile.
|
||||
Chaque progrès lui donne une raison d'explorer, de construire ou de revenir
|
||||
avec quelqu'un. Le SGA relie les traces ; les conséquences racontent l'histoire.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Arriver par le SGA] --> B[Repérer une trace dans un lieu]
|
||||
B --> C[Préparer une sortie]
|
||||
C --> D[Explorer et contribuer]
|
||||
D --> E[Transformer un lieu]
|
||||
E --> F[Revenir, habiter et partager]
|
||||
F --> C
|
||||
E --> G[Découvrir un nouvel horizon]
|
||||
G --> B
|
||||
```
|
||||
|
||||
**Première scène proposée :** depuis une arrivée sûre, apercevoir une ancienne
|
||||
halte, y reconnaître un motif SGA et découvrir une ancre avec une demande
|
||||
claire. Rapporter son matériau produit un changement visible et persistant.
|
||||
Dans le premier essai, ce changement peut se limiter à l'allumage déjà
|
||||
prototypé. L'ouverture d'un territoire viendra dans un lot distinct, annoncé
|
||||
comme tel : une lampe allumée ne prouve pas qu'une expansion fonctionne.
|
||||
|
||||
Cette scène fait avancer ensemble le spawn, un bâtiment, une ancre, la lecture
|
||||
du paysage, l'ambiance et le retour d'information. Un familier, une recette,
|
||||
un PNJ ou une horde s'y ajoute ensuite seulement s'il change réellement ce que
|
||||
le joueur prépare ou découvre. L'histoire ne dépend pas d'avoir tout fini.
|
||||
|
||||
## Trois plans qui répondent à trois questions
|
||||
|
||||
Les lieux ont une implantation propre. Un lien logique entre une ancre et le
|
||||
bloc originel n'impose ni pont, ni couloir, ni alignement avec un palais.
|
||||
|
||||
| Plan | Ce qu'il explique | Première version à produire |
|
||||
| --- | --- | --- |
|
||||
| Vue du dessus de Sanctuary Island | Relief, température et humidité, arrivée, silhouettes, chemins et huit secteurs d'exploration | Une carte schématique avec un seul parcours détaillé ; les autres secteurs restent réservés sans bâtiment obligatoire |
|
||||
| Coupe et accès aux autres espaces | Haut/bas, clair/sombre, visible/caché ; surface habitée, infrastructures et seuils | Une coupe de l'île et des schémas séparés pour les espaces accessibles par portail ; distinguer altitude et changement de dimension |
|
||||
| Graphe de progression | Ce qu'une découverte permet, qui contribue et ce qui change | Arrivée → lieu → demande → conséquence → nouvel horizon, avec états personnels et collectifs distingués |
|
||||
|
||||
La conversation [« Concevoir la salle secrète »](chatgpt-conversation://6abb9cdf-9a10-83eb-ac7e-8d481ce3352c)
|
||||
apporte la température et l'humidité, l'axe visible/caché, des cavernes déjà
|
||||
habitées et dominées par les villageois souterrains, ainsi que l'idée d'un
|
||||
portail horizontal pour le Haut. Ce sont des intentions à développer, pas des
|
||||
dimensions livrées.
|
||||
|
||||
La redistribution du contenu du Nether et de l'End dans de nouvelles dimensions
|
||||
est une **piste radicale en réflexion**, pas une suppression autorisée des
|
||||
mondes actuels. Le placement cardinal des climats, la formule
|
||||
« surface = territoire / cavernes = matière / Haut = information », la
|
||||
symétrie observatoire/Core et les conditions de prestige venaient des réponses
|
||||
de l'assistant : ce sont des propositions, sans adoption implicite.
|
||||
|
||||
L'ISS peut rester une trace dans le ciel de l'île. Son éventuel rôle dans
|
||||
l'accès au Haut est à décider ; station en altitude et dimension du Haut ne
|
||||
sont pas automatiquement le même espace. L'abandon du palais ne tranche pas
|
||||
le sort de tous les souterrains ni des autres lieux existants.
|
||||
|
||||
## Chercher l'esthétique sur une scène comparable
|
||||
|
||||
**Recommandation de départ, à éprouver : une infrastructure ancienne rendue
|
||||
habitable.** Elle exprime à la fois la trace inquiétante du SGA, la douceur
|
||||
des usages quotidiens et la place laissée aux constructions des joueurs.
|
||||
Éviter de transformer toute l'île en décor fini : le terrain doit encore
|
||||
donner envie d'y construire.
|
||||
|
||||
Comparer deux variantes de la **même halte extérieure**, avec la même fonction,
|
||||
le même gabarit et le même trajet. Les palettes ci-dessous sont des propositions
|
||||
de matériaux déjà présents dans Minecraft, sans ajout de mod esthétique.
|
||||
|
||||
| Variante | Silhouette et matière | Sensation à éprouver |
|
||||
| --- | --- | --- |
|
||||
| A — Halte réhabitée, recommandée | Base en pierre, réparations en bois, touches de cuivre, végétation, abri ouvert ; repère SGA discret | Une ancienne installation que l'on comprend et que l'on a envie de prolonger |
|
||||
| B — Vestige minéral | Pierre claire et ardoise sombre, métal ponctuel, volumes plus géométriques et ajourés ; quelques aménagements chaleureux | Une infrastructure étrange dont la fonction se révèle par l'usage |
|
||||
|
||||
Un même langage peut ensuite relier les lieux : formes et signes récurrents
|
||||
pour les traces anciennes, ajouts pratiques pour l'habitation, espaces libres
|
||||
pour les joueurs. Les couleurs des huit gemmes servent d'abord à lire un état ;
|
||||
elles ne fixent pas huit palettes de biomes ni huit matériaux de construction.
|
||||
|
||||
**Essai proposé :** mêmes points de vue de jour et de nuit, trajet depuis le
|
||||
spawn, lecture de l'ancre éteinte/allumée, place pour construire et perception
|
||||
des sons. Retenir ce qui rend la destination visible, le geste compréhensible
|
||||
et le retour agréable. Comparer le coût du rendu à réglages Vulkan identiques.
|
||||
Les maquettes et cette comparaison restent à réaliser.
|
||||
|
||||
## Avancer par parcours jouables
|
||||
|
||||
Chaque étape croise quelques systèmes et se termine par un essai. On ne
|
||||
développe pas toute la géographie, puis tous les menus, puis toute la cuisine.
|
||||
Une seule nouvelle scène sert de référence à la fois ; les autres idées restent
|
||||
en réserve. L'essai peut conduire à simplifier, déplacer ou abandonner une idée.
|
||||
|
||||
| Étape proposée | Résultat concret | Ce qui avance ensemble | Vérification avant d'élargir |
|
||||
| --- | --- | --- | --- |
|
||||
| 0. Itérer sur le worldgen — priorité actuelle | Un labo de relief rapide et un profil complet de comparaison, dans des mondes séparés | Temps de démarrage, forme de l'île, biomes et outils d'observation | Mesures à froid/réouverture, relief comparé à la référence, aucun plan hydrologique caché ; voir WG-LAB-174 |
|
||||
| 1. Arriver et comprendre | Un trajet extérieur spawn → halte → ancre indépendante, deux variantes visuelles | Relief local, bâtiment, motif SGA, matériau demandé, feedback et ambiance | Trouver le lieu, comprendre le dépôt, voir son effet, revenir ; comparer les variantes et reprendre l'état après reconnexion |
|
||||
| 2. Donner une conséquence | Une ancre ouvre réellement une première destination dans un nouveau monde de test | Génération, progression commune, trajet aller/retour et mémoire d'une découverte | Contrat de génération avant code ; dépôt répété, duo, interruption/reprise, emplacement libre et retour sûr ; tester plusieurs graines consignées |
|
||||
| 3. Donner envie de préparer et revenir | Une préparation utile au parcours : un familier et un produit, ou un plan constructible selon le besoin révélé par l'essai | Vie quotidienne, exploration, Habitant et construction ; première rencontre si elle sert cette boucle | Effet observable avant/après, ressources compréhensibles, sauvegarde ; dépendances 26.3 vérifiées avant tout port d'It's Alive |
|
||||
| 4. Ouvrir la verticalité | Un premier seuil vers les cavernes ou le Haut, choix encore ouvert | Accès, ambiance, ressource ou capacité, première épreuve et récit | Entrée/sortie, échec/récupération, intérêt après réussite et accueil d'un joueur arrivé plus tard |
|
||||
| 5. Décliner ce qui fonctionne | Autres secteurs, ancres et lieux ; plusieurs usages et contributions | Variété géographique, groupes spontanés, factions et mémoire communautaire | Chaque variante apporte une raison de partir ; absence de huit répétitions du même trajet |
|
||||
|
||||
La lecture minimale d'une demande ou d'une découverte accompagne sa scène dans
|
||||
les interfaces existantes. La refonte complète Habitant/Combat/Factions reste
|
||||
dans la réserve ; elle se précisera avec les gestes réellement testés. Le
|
||||
retrait demandé de Friends reste une petite tâche explicite à réaliser, sans
|
||||
en faire une dépendance de la carte de l'île.
|
||||
|
||||
Les navets, la loterie, le shop, l'ensemble des créatures, le chenil, les
|
||||
ordinateurs et les grandes structures volantes ne sont pas des prérequis de
|
||||
la première scène. Leur priorité remonte lorsqu'un parcours en a besoin.
|
||||
Le site et Discord prolongeront une activité commune éprouvée ; les droits,
|
||||
les préférences de rappel et les règles de synchronisation restent à concevoir.
|
||||
|
||||
## Préparer la prochaine séance de développement
|
||||
|
||||
Le labo rapide de l'étape 0 est disponible. Il permet de travailler les
|
||||
volumes, les cavités, les climats et les parcours sans
|
||||
attendre toutes les recherches de structures et d'hydrologie. Les mesures
|
||||
et le protocole sont dans [WG-LAB-174](worldgen-lab-beta174.md).
|
||||
|
||||
La scène extérieure de l'étape 1 suivra ce travail sur le terrain. Pour la
|
||||
préparer, compléter une fiche courte :
|
||||
|
||||
- une carte du trajet et une coupe du terrain, avec l'emprise des deux variantes ;
|
||||
- ce que le joueur voit, comprend, apporte et observe après son dépôt ;
|
||||
- où se lit l'état de l'ancre et quel rôle expérimental garde le bloc originel ;
|
||||
- une fiche de génération du lieu : relief accepté, accès, variantes par seed,
|
||||
contenu nécessaire et solution si son placement est impossible ;
|
||||
- la graine, la version de génération et les points de capture de l'essai.
|
||||
|
||||
Après la visite, consigner **garder / ajuster / abandonner** avec une observation
|
||||
de jeu. Mettre à jour cette page et la fiche du lot, puis choisir la prochaine
|
||||
conséquence à rendre jouable. Une intention n'est cochée que lorsque son
|
||||
résultat a été essayé ou vérifié.
|
||||
|
||||
Questions encore ouvertes : fonction et emplacement du bloc originel ; carte
|
||||
des huit secteurs et correspondance climatique ; statut narratif des anciens
|
||||
PNJ ; première dimension à explorer ; redistribution du Nether/End et rôle
|
||||
de l'ISS. Aucune de ces questions n'impose de dessiner le monde entier avant
|
||||
la première halte.
|
||||
|
||||
## Cadre de réalisation
|
||||
|
||||
La première remise à plat était documentaire. La demande suivante ouvre le
|
||||
lot de code WG-LAB-174, dont la fiche distingue contrat, réalisation et mesures.
|
||||
Les anciens documents du palais portent un avertissement de statut et gardent
|
||||
les preuves des essais déjà effectués ; son laboratoire est préservé.
|
||||
|
||||
Chaque évolution future de génération devra préciser nouveaux mondes ou
|
||||
migration, seed, version, reprises et autorité serveur avant toute écriture.
|
||||
Les identifiants `sanctuary:*` restent stables ; aucun retrait de blocs ou
|
||||
remplacement de dimension n'est déduit de cette note. Le prochain lot de code
|
||||
utilisera le prochain numéro beta disponible, exécutera `./gradlew check build`,
|
||||
et `./gradlew assemblePack` si la distribution change ; rendu validé sous Vulkan.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Storyquest — état du socle beta.172
|
||||
|
||||
Relevé du **27 septembre 2026**, préparatoire à la
|
||||
[refonte Storyquest](storyquest-beta173.md). Inspection de Git, des contrats et
|
||||
des sources ; ce relevé n'est ni un nouveau playtest ni un audit exhaustif de
|
||||
tous les comportements du pack.
|
||||
|
||||
## Le bon point de départ
|
||||
|
||||
| Élément vérifié | État |
|
||||
| --- | --- |
|
||||
| Checkout initial `sanctuary-beta` | Branche `codex/pixel-shadows-beta122`, HEAD `9b0104d`, changements suivis et non suivis en cours ; conservés |
|
||||
| Dernier socle local de jeu | Branche `codex/horde-runtime-beta172`, commit `55c745a`, checkout `sanctuary-community-beta154` propre lors du relevé |
|
||||
| `main` local | `8ab5a45`, étape beta.168 |
|
||||
| `origin/main` après `git fetch origin` | `7979f33`, étape beta.166 ; ancêtre du socle 172, avec dix commits d'avance locale sur cette référence |
|
||||
| Nouvelle branche de cadrage | `codex/storyquest-beta173`, depuis `55c745a`, worktree géré `storyquest-beta173/sanctuary-beta` |
|
||||
| Versions du nouveau checkout | Mod et pack beta.172 ; resource pack beta.121 ; cible Minecraft 26.3, Java 25, Fabric Loader 0.19.5, API 0.160.5+26.3 |
|
||||
| Ancienne branche `codex/rework-pingpong-173` | Pointe sur `9b0104d`, sans note Storyquest trouvée ; laissée intacte |
|
||||
|
||||
Le numéro du resource pack et le nom historique d'un checkout ne déterminent
|
||||
pas la version du jeu. Aucune conclusion sur un bug de beta.122 n'est tirée
|
||||
de sa simple présence dans le checkout initial. Ce chantier repart du code 172.
|
||||
La publication et le canal public ne sont pas déduits des branches locales.
|
||||
|
||||
## Ce qui existe et ce qu'il faut relier
|
||||
|
||||
Les chemins Java ci-dessous sont relatifs à
|
||||
`mods/sanctuary/src/main/java/fr/koka/sanctuary/` dans le socle 172.
|
||||
|
||||
| Sujet | Preuve locale / état observé | Conséquence pour la refonte |
|
||||
| --- | --- | --- |
|
||||
| Code et intro SGA | `client/HelloWorldScreen.java` formate la saisie avec `minecraft:alt` ; `client/IntroScreen.java` et `intro/IntroSequence.java` composent l'introduction | Une continuité visuelle existe ; une grammaire narrative commune reste à concevoir |
|
||||
| Inscription web | [beta.166](inscription-web-beta166.md) : candidature, code, reçu lié à l'admission et reprise après coupure | Réutiliser le contrat ; l'OAuth authentifié complet et le déploiement public ne sont pas établis par ce relevé |
|
||||
| Pause | `client/CommunityPausePanels.java` : Blocodex, Progression, Habitant, Faction, Combat, carte, Gazette, tableau, date et intendance | Refaire les parcours en préservant les fonctions déjà disponibles |
|
||||
| Friends | `mixin/client/TitleMenuMixin.java` remplace la rangée Realms par Friends et retire le bouton social natif | Retrait explicite de la rangée et réagencement nécessaires ; pas encore réalisés |
|
||||
| Habitant / factions / combat | `client/ProgressionScreen.java`, `client/CycleFactionScreen.java`, `client/ArenaScreen.java` ; contrats [factions](cycle-factions-beta011.md) et [combat](combat-beta078.md) | Réorganiser des systèmes existants ; ne pas présenter factions et combats comme absents |
|
||||
| Hordes | `horde/HordeCards.java`, `HordeTrials.java`, `HordeRunes.java` ; contrats [170](horde-map-native-beta170.md), [171](horde-families-beta171.md), [172](horde-runtime-beta172.md) | Cartes identifiées, familles, runes et activation de groupe disponibles ; pas un moteur Storyquest complet |
|
||||
| Expansions | [Quatre anciennes expéditions](expeditions-beta001.md), registre et placement dans `expansion/` | Les quatre régions existent dès la création ; l'île seule au départ change le contrat des nouveaux mondes |
|
||||
| Lieux de l'île | [WG-26](structures-conception.md) retient observatoire, salle d'expansion, atelier caché, traversée et donjon à définir | Réconcilier cette sélection avec spawn, secret et huit liaisons avant nouvelle génération |
|
||||
| Familiers | `companion/CompanionService.java` et `CompanionPassives.java` : passifs existants ; sélection par œuf du slot familier, délai d'équipement ; retour nul en combat actif hors mode utilitaire | Le besoin porte sur l'utilité, le port sur la tête et les conditions d'activation ; éviter d'empiler un second système contradictoire |
|
||||
| Préférences alimentaires | `familiar/FamiliarSupplies.java` réserve notamment des aliments selon leurs propriétés ; le port culinaire n'est pas présent dans les modules de la bêta | Aucun système complet de goûts culinaires individuels n'a été établi par l'inspection ; à concevoir et vérifier |
|
||||
| Phantoms | `mixin/RealtimePhantomMixin.java` bloque le spawner d'insomnie quand le temps réel est actif ; `horde/HordeCards.java` contient encore PHANTOM ; `CompanionPassives.java` le référence | Suppression partielle seulement : régler aussi hordes, familiers, objets et anciens contenus |
|
||||
| Bibliothèque | [Métabli](metabli-beta096.md), [catalogue](catalogue-progression-beta099.md), `plans/ConstructionCatalog.java`, `client/PlansScreen.java` | Catalogue, aperçu, matériaux et imports existent ; il manque la bibliothèque éditoriale complète demandée |
|
||||
| Communauté | [Contrat communauté](community-contract-v1.md), [beta.158](community-cards-beta158.md), [beta.161](session-fixes-beta161.md) | Gazette, tableau, réponses, suivis et stockage fichier/MariaDB sont un socle ; économie et notifications complètes ne sont pas acquises |
|
||||
| Ambiance | Historique shader et règle Vulkan de `AGENTS.md` | Mesurer le rendu courant ; ne pas réappliquer les anciennes recettes OpenGL de beta.122 |
|
||||
|
||||
Le [cadrage du 25 septembre](ecosysteme-communaute-economie.md) conserve déjà
|
||||
les cartes de découverte/épreuve, les clés, les huit structures, le bloc originel,
|
||||
les navets du dimanche, les machines de loterie, le ballast et l'incubation d'XP.
|
||||
Ces intentions ne sont pas remplacées silencieusement par de nouvelles règles.
|
||||
|
||||
## Ce que contient réellement la référence 26.2
|
||||
|
||||
Inspection en lecture seule du dépôt historique `/Users/koka/Documents/sanctuary/26.2`.
|
||||
Les sources suivantes établissent une piste de port ; elles ne prouvent pas
|
||||
leur compatibilité avec Minecraft 26.3.
|
||||
|
||||
| Contenu retrouvé | Source historique | Limite / travail de port |
|
||||
| --- | --- | --- |
|
||||
| Cinq Infectés : infecté, vomiteur, cracheur, hunter, chargeur | `itsalive/README.md`, `itsalive/src/main/java/fr/koka99cab/sanctuary26/itsalive/entity/InfectedVariant.java` | Une entité et cinq variantes ; revoir attaques, rendu, sauvegarde et raccord aux hordes |
|
||||
| Seize Mooblooms | `itsalive/README.md`, `entity/MoobloomVariant.java` | Une entité et variantes ; fleurs hibiscus/narcisse rattachées à Another World dans la référence |
|
||||
| Ghost et Bracken | `itsalive/README.md`, `entity/GhostEntity.java` | Distinguer le retour demandé des fantômes et le choix encore ouvert de reprendre Bracken |
|
||||
| Baleine volante | `entity/FlyingWhaleEntity.java`, `world/FlyingWhaleSpawner.java` | Règles d'altitude, fréquence, persistance et rendu à éprouver sur les îles actuelles |
|
||||
| Suppression historique des phantoms | `itsalive/README.md`, section Real Ghost | L'ancienne suppression de toute entité chargée ne constitue pas un contrat de migration acceptable pour les possessions actuelles |
|
||||
| Cuisine et cultures | `itsalive/FOOD_WIKI.md`, `CULINARY_CATALOG.md`, paquet `culinary/` | Postes, familles d'ingrédients, cuisson, fermentation et connaissance ; réconcilier documents historiques et sources lors du port |
|
||||
|
||||
Le catalogue culinaire décrit une poêle à un à quatre ingrédients, des recettes
|
||||
strictes et des substitutions par famille. Une recette devinée fonctionne sans
|
||||
sa page. Le wiki documente le livre et ses pages comme disponibles à son étape,
|
||||
alors que le catalogue conserve aussi des formulations historiques « prochain
|
||||
système » : les notes n'ont pas toutes le même âge. Aucune préférence alimentaire
|
||||
des familiers ne doit être inventée à partir d'une simple liste de recettes.
|
||||
|
||||
Le manifeste historique `itsalive/src/main/resources/fabric.mod.json` exige
|
||||
`minecraft: ~26.2`, Fabric API `>=0.154.2+26.2`, **anotherworld**, **ambiance** et
|
||||
**sanctuary**. Il n'est donc pas un JAR autonome prêt à ajouter à la bêta actuelle.
|
||||
`settings.gradle` de beta.172 inclut Sanctuary, Demeure, JEI et Sanctuary Test,
|
||||
sans module It's Alive. Le port doit établir les responsabilités et dépendances
|
||||
pour 26.3 ; pas de copie globale de l'ancien pack ni de renommage improvisé
|
||||
des identifiants historiques.
|
||||
|
||||
## Vérifications et limites de ce relevé
|
||||
|
||||
- Références Git inspectées et `origin` récupéré ; base 172 propre, branche
|
||||
de conception isolée, modifications initiales préservées.
|
||||
- Versions lues dans la configuration ; sources des parcours clés examinées.
|
||||
- Le contrat beta.172 consigne **261/261 GameTests**, `check build` réussi et
|
||||
les empreintes des archives. Ce sont les preuves de cette livraison : la
|
||||
présente passe documentaire ne relance pas ces tests et ne revalide pas les
|
||||
archives générées.
|
||||
- L'ouverture en lecture seule de `https://sanctuary-minecraft.net/` par l'outil
|
||||
de recherche n'a pas abouti. Cela ne prouve pas une panne du site. État public,
|
||||
forum, permissions Discord et notifications non vérifiés ici ; le cadrage
|
||||
s'appuie sur les contrats locaux, pas sur un déploiement supposé.
|
||||
- Aucun code, dépendance, monde, format, version binaire, canal packwiz ou
|
||||
instance Prism modifié ; aucun message externe envoyé.
|
||||
@@ -1,5 +1,13 @@
|
||||
# Vérifier Sanctuary
|
||||
|
||||
## Cible graphique — décision du 18 septembre 2026
|
||||
|
||||
Les nouvelles validations graphiques Sanctuary utilisent **Vulkan uniquement**.
|
||||
OpenGL est abandonné comme cible de validation à la demande du créateur.
|
||||
Les anciens résultats OpenGL ci-dessous sont historiques et ne constituent
|
||||
pas un engagement de compatibilité. Ne plus programmer de suite OpenGL.
|
||||
|
||||
|
||||
Utiliser Java 25 et les dépendances épinglées dans `gradle.properties`.
|
||||
|
||||
```sh
|
||||
|
||||
+54
-2
@@ -1,5 +1,33 @@
|
||||
# Sanctuary — vision du projet
|
||||
|
||||
**Direction courante — 29 septembre 2026 :** le
|
||||
[fil rouge de la refonte](storyquest-fil-rouge.md) reprend **Sanctuary Island,
|
||||
l'île de départ**, autour de plans géographiques indépendants et de parcours
|
||||
jouables qui croisent lieu, récit, préparation et conséquence. Le créateur
|
||||
abandonne le palais souterrain, ses accès et les raccordements au palais.
|
||||
L'esthétique, la nouvelle position du bloc originel et le plan des huit ancres
|
||||
restent à concevoir. Les demandes de matériaux et les gemmes ne sont pas annulées.
|
||||
|
||||
Le [cadrage Storyquest du 27 septembre](storyquest-beta173.md) conserve la vision
|
||||
large : SGA, quêtes, habitants, familiers, cuisine, économie et vie communautaire.
|
||||
Les priorités actives sont désormais dans le fil rouge. Le
|
||||
[concept du palais du 28 septembre](palais-originel-iss-beta173.md) est historique ;
|
||||
ses pistes ISS, chenil et ordinateurs restent ouvertes sans dépendance au palais.
|
||||
Le [laboratoire beta.173](palais-prototype-beta173.md) conserve les mécaniques
|
||||
et essais réalisés ; son architecture n'est plus une cible d'intégration à l'île.
|
||||
L'[inventaire beta.172](storyquest-socle-beta172.md) décrit le socle précédent.
|
||||
|
||||
**Priorité ajoutée pendant la séance :** reprendre d'abord le worldgen grâce
|
||||
à un [labo rapide mesuré](worldgen-lab-beta174.md), sans hydrologie ni plans
|
||||
de structures, puis réintroduire les couches utiles. Le créateur choisit ce
|
||||
profil pour les nouveaux mondes de laboratoire uniquement ; la génération
|
||||
normale conserve son comportement.
|
||||
|
||||
La clôture worldgen alpha.30.7 et les ordres de travail rappelés dans la suite
|
||||
sont historiques. Les rôles du Nether et de l'End sont à réexaminer dans la
|
||||
nouvelle conception ; aucune suppression de dimension, régénération ou migration
|
||||
de partie n'est décidée par ce cadrage.
|
||||
|
||||
Ce document conserve les intentions exprimées au démarrage de Sanctuary Beta. Il décrit une **destination de conception**, pas une liste de fonctionnalités déjà livrées. Le code, les tests et les notes de version font foi pour l'état réel du mod. Les valeurs d'équilibrage ci-dessous sont des propositions initiales à éprouver en jeu.
|
||||
|
||||
La cible demandée est **Minecraft 26.3 avec Fabric**. Au démarrage du dépôt, le 8 septembre 2026, la base disponible retenue est **26.3-pre-2** ; les versions effectivement utilisées restent indiquées dans la configuration de construction. Le socle Fabric et la reprise du générateur de l'ancienne version 26.2 ont constitué la première étape.
|
||||
@@ -399,6 +427,12 @@ ses plans orientés et ses poses natives limitées par les rangs.
|
||||
|
||||
## Monnaies, propriétés et échanges
|
||||
|
||||
Le [cadrage du 25 septembre 2026](ecosysteme-communaute-economie.md) précise
|
||||
l'État régulateur, le tableau à message général, les bounties collectionnables,
|
||||
les huit structures d'expansion et leurs liens avec les statistiques et le
|
||||
ballast. Il distingue les décisions du créateur, les propositions et le socle
|
||||
réellement livré ; ces systèmes futurs ne sont pas activés par cette vision.
|
||||
|
||||
### Les trois gemmes
|
||||
|
||||
Les trois gemmes forment la palette et la symbolique triangulaire de Sanctuary.
|
||||
@@ -419,7 +453,7 @@ Le shop pourrait devenir une infrastructure coûteuse à construire, accessible
|
||||
|
||||
### Bourse du navet
|
||||
|
||||
Les **navets** s'achètent **uniquement le dimanche, lors de la loterie**. Pendant la semaine, les joueurs peuvent les revendre au shop au cours variable de la **bourse du navet**. Aucun achat de navets n'est proposé les autres jours.
|
||||
Les **navets** s'achètent **auprès d'un PNJ, uniquement le dimanche**. Pendant la semaine, les joueurs peuvent les revendre au shop au cours variable de la **bourse du navet**. Ils **pourrissent après une semaine s'ils ne sont pas vendus**. Aucun achat de navets n'est proposé les autres jours. L'échéance exacte et les règles du cours restent à définir ; voir le [cadrage du 25 septembre](ecosysteme-communaute-economie.md).
|
||||
|
||||
### Coffre-fort, équipes et braquage
|
||||
|
||||
@@ -537,7 +571,12 @@ Un calendrier expose des nombres de jours simples : âge du serveur depuis sa cr
|
||||
|
||||
Des panneaux d'événements permettent de proposer une activité et de s'inscrire, en lien avec les factions. Des anniversaires issus d'Only Fun peuvent y être reliés. Des événements suivent un cycle évoqué comme six jours actifs et un septième jour de repos ou de fête ; la durée et l'ancrage hebdomadaire doivent être confirmés.
|
||||
|
||||
Une **loterie du dimanche** permet de collecter des tickets pendant la semaine. Le nombre de tickets augmente les chances de récompense : lootboxes anomaly, emplacements du catalogue ou extensions du shop. Les probabilités et le financement des récompenses doivent préserver la rareté des ressources.
|
||||
Une **loterie du dimanche** utilise des tickets trouvés ou achetés pendant la
|
||||
semaine. La précision du 25 septembre 2026 prévoit de **vraies machines pouvant
|
||||
dupliquer ou diviser des stocks d'objets**, avec des conséquences à définir pour
|
||||
le ballast des Backrooms. Probabilités, objets admissibles et financement restent
|
||||
à concevoir. Les lootboxes, emplacements et privilèges temporaires évoqués
|
||||
auparavant sont des possibilités, pas un catalogue de gains arrêté.
|
||||
|
||||
La loterie du dimanche accueille aussi l'unique occasion hebdomadaire d'acheter les navets de la [bourse du navet](#bourse-du-navet), revendables ensuite au shop pendant la semaine.
|
||||
|
||||
@@ -555,6 +594,19 @@ Le joueur pourrait tracer des constellations depuis son point de vue, directemen
|
||||
|
||||
## Quêtes, récompenses et cosmologie
|
||||
|
||||
Le [cadrage du 25 septembre](ecosysteme-communaute-economie.md) ajoute les
|
||||
bounties sous forme d'objets collectionnables, activables quand le joueur est
|
||||
prêt, et l'utilisation de cartes créées à la volée. Le tableau accueille un
|
||||
message général pouvant porter une quête ou une découverte à explorer ensemble.
|
||||
Les règles d'XP, de groupe, d'activation et de récompense restent à définir.
|
||||
|
||||
La même discussion ajoute des quêtes canoniques à quotas et paliers, avec une
|
||||
progression personnelle et une coopération à concevoir, ainsi que des capes et
|
||||
familiers exclusifs liés aux épreuves difficiles. Les familiers sont aussi
|
||||
envisagés comme **incubateurs d'XP** : leur réserve fructifie avec les blocs
|
||||
parcourus à pied, posés et cassés, selon un rendement modéré encore à définir.
|
||||
Ces intentions ne décrivent pas des fonctionnalités déjà livrées.
|
||||
|
||||
### Panneaux et lootboxes
|
||||
|
||||
Les panneaux de quêtes utilisent les trois gemmes et leurs couleurs : vert émeraude, rouge rubis, bleu saphir, pour trois paliers de difficulté. Le nombre de quêtes réalisables est limité par heure. Le joueur voit les récompenses avant de choisir ; les quêtes donnent notamment de l'XP et des lootboxes.
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
# WG-LAB-174 — itérer rapidement sur Sanctuary Island
|
||||
|
||||
Branche `codex/worldgen-lab-beta174`, issue de beta.173 et du cadrage Storyquest
|
||||
du 29 septembre. Le créateur choisit explicitement **un labo rapide uniquement**.
|
||||
Ce lot prépare l'outil de travail avant la nouvelle géographie de l'île.
|
||||
|
||||
## Contrat avant implémentation
|
||||
|
||||
Le module optionnel Sanctuary Test fournit un preset distinct
|
||||
`sanctuary_test:relief_v1`, réservé à de nouveaux mondes. Il reprend le champ
|
||||
de densité, les règles de surface et les biomes de l'origine Sanctuary de
|
||||
diamètre 724, à graine identique. Il retire les plans d'eau/lave, les structures
|
||||
planifiées, les îlots aériens planifiés et les quatre expéditions initiales.
|
||||
Les décorations qui requièrent les protections hydrologiques sont aussi absentes.
|
||||
Les cavités et masses qui appartiennent au champ de densité restent présentes.
|
||||
Ce profil sert à lire et modifier le relief ; il ne représente pas le monde fini.
|
||||
|
||||
Le preset et son réglage de bruit ont une identité propre, enregistrée avec
|
||||
le monde. Aucun interrupteur global ne permet de changer sa génération en
|
||||
rouvrant une sauvegarde. Le profil complet de référence utilise le preset
|
||||
Sanctuary normal dans un **autre dossier neuf**. Changer de profil, de graine
|
||||
ou de révision demande un autre dossier de labo ; aucune régénération ni
|
||||
migration de chunks existants n'est livrée.
|
||||
|
||||
La génération normale et les anciens identifiants restent inchangés. La
|
||||
présence de Sanctuary Test est nécessaire pour relire un monde de ce labo.
|
||||
Les fichiers vivent sous `build/worldgen-lab/`, ignoré ; aucun profil Prism ni
|
||||
canal packwiz n'est mis à jour. Le lanceur doit refuser une identité de labo
|
||||
ou des propriétés de monde modifiées, et conserver les fichiers existants.
|
||||
|
||||
## Pourquoi couper plusieurs couches
|
||||
|
||||
Le [profilage beta.063](loading-gameplay-beta063.md) mesurait environ 13 s
|
||||
d'hydrologie, imbriquées dans 24 s de traversées ; patrimoine, sanctuaire minier
|
||||
et village ajoutaient d'autres recherches. Ces chiffres sont historiques,
|
||||
pas une mesure beta.174. Couper seulement l'écriture de l'eau ne suffit pas :
|
||||
les recherches de structures et les protections de décorations demandent
|
||||
elles aussi le plan hydrologique.
|
||||
|
||||
Le [cache beta.064](loading-cache-beta064.md) accélère les réouvertures, mais
|
||||
inclut les versions de tous les mods dans sa clé. Un rebuild avec nouveau
|
||||
numéro peut donc refaire la planification. Le labo de relief contourne les
|
||||
planificateurs ; ce gain n'est pas une accélération de leur algorithme.
|
||||
|
||||
## Vérifications prévues
|
||||
|
||||
- Comparer les paramètres de bruit et les échantillons de densité avec
|
||||
`sanctuary:unified_10`, sans solliciter son hydrologie.
|
||||
- Demander de vrais chunks centraux et éloignés, puis constater zéro plan
|
||||
hydrologique dans le profil rapide et l'absence de journal d'expansion.
|
||||
- Comparer création à froid et réouverture des deux profils, même graine,
|
||||
mémoire et zone de chunks. Séparer lancement JVM, serveur prêt et exploration.
|
||||
- Reprendre un monde de labo sans changement de son identité ; vérifier les
|
||||
refus du lanceur et les libellés FR/EN. Inspection du terrain sous Vulkan.
|
||||
- Exécuter `./gradlew check build assemblePack assembleTestPack` ; publier ici
|
||||
les résultats effectivement obtenus et leurs limites.
|
||||
|
||||
## Fil rouge proposé pour le worldgen
|
||||
|
||||
1. **Itérer vite** : ce labo, une graine de référence et des mesures séparées.
|
||||
2. **Lire l'île** : forme, épaisseur, silhouettes, cavités, arrivée et chemins ;
|
||||
comparer plusieurs graines et des vues fixes avant les détails coûteux.
|
||||
3. **Retrouver l'eau** : mesurer les sondages, partager les données de terrain,
|
||||
puis comparer une planification simplifiée et la référence complète.
|
||||
4. **Habiter le terrain** : un lieu et une ancre indépendants ; placement borné,
|
||||
accès et fonction, puis les familles de bâtiments retenues.
|
||||
5. **Réunir les couches** : écologie, ressources et structures dans un nouveau
|
||||
profil complet versionné, avec contrôles d'eau, de progression et de reprise.
|
||||
|
||||
L'objectif est de garder les détails utiles tout en réduisant le travail fait
|
||||
avant la première arrivée. Plan de relief grossier, sondages partagés, cache
|
||||
par couche et calcul local sont des pistes à mesurer, pas des optimisations
|
||||
déjà validées. Les passages entre couches se font dans de nouveaux mondes de
|
||||
comparaison ; les régions déjà générées ne sont jamais « complétées » en place.
|
||||
|
||||
## Résultats
|
||||
|
||||
Première comparaison mesurée le 29 septembre 2026 : **Apple M1, 8 Gio de RAM,
|
||||
Java 25, Minecraft 26.3**, serveur dédié local, heap 1 536 Mio, graine **42**,
|
||||
diamètre **724**, vue 6 chunks et simulation 4. Aucun autre serveur ou client
|
||||
Minecraft pendant ces mesures. Un seul passage par condition ; ces valeurs
|
||||
ne constituent pas une garantie pour toute machine ou toute graine.
|
||||
|
||||
| Mesure | Relief neuf | Référence neuve | Relief rouvert | Référence rouverte |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| Phase serveur jusqu'à `SERVER_STARTED`, arrivée sûre comprise | 5,566 s | 114,564 s | 1,054 s | 1,331 s |
|
||||
| Demande du lot fixe de dix chunks | 9,660 s | 17,347 s | 0,322 s | 0,456 s |
|
||||
| Processus lancé → zone de contrôle prête, bootstrap Java/mods compris | **29,197 s** | **144,569 s** | **14,563 s** | **14,594 s** |
|
||||
|
||||
La dernière ligne est le temps mural du processus jusqu'au marqueur
|
||||
`WORLDGEN_LAB_READY`, pas le temps jusqu'à un client rendu et jouable. Elle
|
||||
inclut les diagnostics de relief sur le profil rapide. Le lot de chunks est
|
||||
`(0,0), (1,0), (-1,0), (0,1), (0,-1), (12,0), (-12,0), (0,12), (0,-12), (24,0)` ;
|
||||
ses dépendances de génération peuvent demander des chunks voisins.
|
||||
|
||||
Sur cette création, le labo réduit cette attente d'environ **80 %** en retirant
|
||||
des couches. Il ne prouve pas une accélération du monde complet à résultat
|
||||
identique. Les réouvertures sont proches : le cache du profil complet remplit
|
||||
déjà son rôle. Référence froide : région hydrologique centrale **20,168 s /
|
||||
5 541 443 sondages**, traversées **38,431 s**, sanctuaire minier **12,550 s**,
|
||||
patrimoine **23,138 s**, village **5,350 s**. Ces durées peuvent être imbriquées,
|
||||
notamment l'hydrologie dans les traversées ; ne pas les additionner.
|
||||
|
||||
Vérifié à ce stade :
|
||||
|
||||
- Paramètres de bruit JSON identiques à `unified_10` ; **1 620 échantillons de
|
||||
densité identiques bit à bit** à la référence, à froid et après réouverture.
|
||||
- **Zéro région hydrologique calculée** après le parcours rapide ; pas de
|
||||
journal d'expansion créé. Les deux profils ont été sauvegardés puis rouverts.
|
||||
- Lanceur : préparation répétée sans écrasement, refus d'une graine/profil ou
|
||||
de propriétés modifiées, acceptation des deux-points échappés par Minecraft.
|
||||
- Sources et données de génération du mod normal inchangées.
|
||||
|
||||
Contrôles supplémentaires sur les graines **0 et 173** : arrivée sûre,
|
||||
dix chunks demandés, 1 620 échantillons identiques par graine et zéro région
|
||||
hydrologique. Rapports dans `build/worldgen-lab/seed-checks174/`. Ces passages
|
||||
confirment trois graines de labo au total, pas une garantie sur toutes les
|
||||
graines ni les formats Petit/Grand, absents de ce premier preset.
|
||||
|
||||
Livraison : `./gradlew check build assemblePack assembleTestPack --max-workers=1
|
||||
-Dorg.gradle.jvmargs=-Xmx1G` **réussi en 13 min 13 s**, avec **265/265 GameTests**.
|
||||
Les profils packwiz normal et Test sont assemblés localement. Les **2 265 entrées
|
||||
de classes et de worldgen** du JAR normal sont identiques à beta.173 ; le preset
|
||||
rapide appartient uniquement au JAR optionnel Sanctuary Test. Libellés FR/EN
|
||||
vérifiés dans cet artefact. Compteurs mod/pack et manifeste alignés sur beta.174.
|
||||
|
||||
Les journaux et le rapport complet restent dans
|
||||
`build/worldgen-lab/audit174a/`, avec `comparison.json` et les quatre logs.
|
||||
La suite de livraison est dans `build/worldgen174-check-build.log`. Aucun
|
||||
profil Prism, canal public ni serveur personnel n'a été modifié.
|
||||
|
||||
Client **Vulkan / Apple M1** : `Worldgen174ClientChecks` réussi, arrivée sur
|
||||
un sol réel, survol et exploration, toujours zéro région hydrologique. La
|
||||
première passe fonctionnelle prenait ses captures avant la fin du chargement ;
|
||||
le banc attend désormais les chunks observés et la fin du rendu. La dernière
|
||||
passe réussit en 1 min 35 s. Captures examinées dans
|
||||
`build/worldgen174-visual/final/` : `relief-seed42.png` et `sous-sol-seed42.png`.
|
||||
La seconde caméra est sous le terrain à cette position ; elle ne constitue
|
||||
pas une vue d'ensemble du bord de l'île. La distance de vue 6 limite les
|
||||
captures à une portion du monde, sans valider toute l'île.
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary:runClientGameTest -PsanctuaryClientTests=true \
|
||||
-PsanctuaryWorldgen174ClientTests=true -PsanctuaryQuickTests=true \
|
||||
-PsanctuaryClientGraphicsBackend=vulkan
|
||||
```
|
||||
|
||||
Journal : `build/worldgen174-client-final.log`. L'optimisation des algorithmes
|
||||
du monde complet et la nouvelle carte de Sanctuary Island restent à développer.
|
||||
|
||||
## Utiliser le labo
|
||||
|
||||
Depuis cette branche, avec Java 25 :
|
||||
|
||||
```sh
|
||||
./gradlew :sanctuary-test:exportDuoLaunch
|
||||
python3 scripts/worldgen_lab.py prepare --seed 42 --run visit174
|
||||
python3 scripts/worldgen_lab.py server --seed 42 --run visit174
|
||||
# Dans un second terminal :
|
||||
python3 scripts/worldgen_lab.py client --seed 42 --run visit174
|
||||
```
|
||||
|
||||
Le serveur écoute sur `127.0.0.1:25581`. Le client rejoint le labo en Vulkan,
|
||||
avec shader d'ambiance désactivé dans sa configuration dédiée. Il reste en
|
||||
créatif, avec choix initial d'habitant/familier et introduction courte. Le
|
||||
profil est aussi sélectionnable sous **Sanctuary — Labo relief v1** dans la
|
||||
création de monde lorsque Sanctuary Test est installé ; le monde plat reste
|
||||
le choix par défaut du module.
|
||||
|
||||
`--profile reference` choisit le monde complet dans un dossier séparé. Arrêter
|
||||
le serveur avant de changer de profil, les deux utilisant le même port local.
|
||||
`--run NOM` choisit une expérience ; après modification des sources, relancer
|
||||
`exportDuoLaunch` pour compiler, puis utiliser un nom neuf. L'empreinte
|
||||
conservatrice des sources/données est vérifiée par le
|
||||
lanceur : aucune conversion silencieuse d'un ancien laboratoire. Les archives
|
||||
de labo demandent la version du code et du module qui les a générées.
|
||||
|
||||
Comparaison automatisée dans un dossier encore absent :
|
||||
|
||||
```sh
|
||||
python3 scripts/worldgen_lab.py benchmark --seed 42 --run comparaison174 --memory 1536
|
||||
```
|
||||
|
||||
Cette commande crée, mesure, arrête proprement puis rouvre chaque profil. Elle
|
||||
conserve les logs et produit `comparison.json`. `server --verify` réalise les
|
||||
mêmes contrôles de chunks et de densité ; la visite normale évite ce travail
|
||||
diagnostique supplémentaire.
|
||||
@@ -0,0 +1,178 @@
|
||||
# Sanctuary Island — reprise après la visite beta.175
|
||||
|
||||
29 septembre 2026, retour R017. Profil `sanctuary_test:sky_v1`, graine 42,
|
||||
diamètre nominal 724, visite solo avec rendu à 32 chunks. Branche de travail
|
||||
`codex/sky-fragments-beta175`. **Audit et conception, sans nouvelle livraison
|
||||
binaire.** Les changements décrits ci-dessous ne sont pas encore appliqués.
|
||||
|
||||
**Suite R018 :** le créateur a ensuite autorisé l'essai. La réalisation et
|
||||
les mesures du nouveau profil sont dans [WG-ECO-176](island-ecology-beta176.md).
|
||||
Le présent document conserve l'audit initial du monde beta.175.
|
||||
|
||||
Le plateau et le relief principal sont jugés satisfaisants par le créateur.
|
||||
La prochaine passe porte sur ce qui habille et rend utilisable ce relief :
|
||||
roches, biomes, eau et sélection des structures. Récifs rares jusqu'à 512 ;
|
||||
ciel 512–639 réservé à l'ISS. Le [contrat beta.175](sky-fragments-beta175.md)
|
||||
reste la référence du monde actuellement visité.
|
||||
|
||||
## Observations et décisions du créateur
|
||||
|
||||
| Sujet | Observation ou direction | Statut |
|
||||
| --- | --- | --- |
|
||||
| Géologie | La transition stone/deepslate forme une bande trop nette. Plusieurs pierres doivent former des couches et des groupements cohérents | Transition visible sur les captures ; nouvelle palette à concevoir |
|
||||
| Île principale | Carte jugée très verte ; introduire de grandes zones automnales et de cerisiers, avec des transitions douces | Direction décidée ; proportions non fixées |
|
||||
| Récifs | Plusieurs îlots herbeux sont nus ; un récif photographié possède une forêt | Vérifié visuellement ; jungle, mangrove, marais, cerisiers et automne proposés par le créateur comme identités possibles |
|
||||
| Structures | Supprimer la génération de mineshafts sur Sanctuary Island | Décidé ; variantes modifiées réservées aux expansions futures |
|
||||
| Villages | Employer des villages Sanctuary, propres au projet | Direction décidée ; modèles, habitants et placement encore à définir |
|
||||
| Progression | Chaque expansion doit apporter un défi et une nouvelle découverte | Décidé ; partage des contenus avec les petits récifs à préciser |
|
||||
| Eau | Étudier une génération locale inspirée des lush caves | Question technique ; aucune durée cible mesurée ou adoptée |
|
||||
|
||||
Les neuf captures du créateur ont été ouvertes et conservées avec SHA-256
|
||||
dans le [carnet de séance](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/retours.md).
|
||||
Les témoins principaux sont la [bande rocheuse](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/captures-R017/2026-09-29_16.51.43.png),
|
||||
un [récif nu](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/captures-R017/2026-09-29_16.52.57.png)
|
||||
et les [mineshafts sous le terrain](/Users/koka/Documents/sanctuary/retours-sessions/2026-09-29_1506-storyquest/captures-R017/2026-09-29_16.54.09.png).
|
||||
Aucune capture de carte opérateur dans ce lot : la dominante verte à
|
||||
l'échelle de l'île reste un retour du créateur, pas un relevé de surface.
|
||||
|
||||
## Ce que l'audit explique
|
||||
|
||||
**Roches.** Le preset emploie `sanctuary:unified_island`, qui délègue largement
|
||||
à [population_island.json](../mods/sanctuary/src/main/resources/data/sanctuary/worldgen/material_rule/population_island.json).
|
||||
Cette règle passe aux roches profondes sous Y=136, puis applique deux bandes
|
||||
conditionnelles sous 152 et 168. Le bruit varie les taches, mais les limites
|
||||
verticales restent fixes. La [palette profonde](../mods/sanctuary/src/main/resources/data/sanctuary/worldgen/material_rule/population_deep_strata.json)
|
||||
associe deepslate, basalte et blackstone. Certains biomes de cavernes suivent
|
||||
une autre règle. Ces chemins expliquent des ruptures de matière possibles ;
|
||||
les captures ne donnent pas l'altitude exacte de chaque contact observé.
|
||||
|
||||
**Récifs nus.** Le [sélecteur de biomes](../mods/sanctuary/src/main/java/fr/koka/sanctuary/worldgen/SanctuaryBiomeSource.java)
|
||||
conserve une limite historique à Y=384. Au-dessus, sa délégation à
|
||||
`ExpansionBiomeSource` renvoie `minecraft:the_void`, alors que les nouveaux
|
||||
volumes atteignent presque 512. Ce décalage est confirmé dans le code ;
|
||||
remplacer uniquement les textures du sol ne le corrigerait pas.
|
||||
|
||||
**Automne et cerisiers.** `minecraft:dappled_forest` et
|
||||
`minecraft:cherry_grove` figurent déjà parmi les biomes du preset. Leur
|
||||
présence dans cette liste ne signifie pas qu'ils sont sélectionnés sur
|
||||
l'île de départ : celle-ci utilise encore la palette forestière Population.
|
||||
Les profils `autumn` et `cherry` appartiennent au sélecteur des expansions.
|
||||
Les ressources natives de Minecraft **26.3 installé** ont été inspectées,
|
||||
notamment les arbres du Dappled Forest ; aucun nouveau mod n'est nécessaire
|
||||
pour essayer ces deux biomes.
|
||||
|
||||
**Mineshafts.** Le labo remplace le générateur Sanctuary par le générateur
|
||||
natif `minecraft:noise`. Il coupe les planificateurs Sanctuary, mais ne
|
||||
supprime pas pour autant toutes les structures natives admissibles par les
|
||||
biomes. Les mineshafts sont visibles dans la visite. Le correctif doit
|
||||
porter sur l'admission de leurs starts, avant placement. Modifier le
|
||||
catalogue des expansions seul ne traiterait pas ce chemin du labo.
|
||||
À terme, la génération normale a aussi son propre chemin
|
||||
`starterMines306` à retirer dans une nouvelle version de départ ; il ne
|
||||
doit pas être modifié implicitement dans les anciennes sauvegardes.
|
||||
|
||||
**Eau.** Dans les ressources natives 26.3, `lush_caves_clay` choisit entre
|
||||
une plage d'argile et `clay_pool_with_dripleaves`. Cette dernière utilise
|
||||
`waterlogged_vegetation_patch`, rayon nominal 4–7 blocs, profondeur de
|
||||
substrat 3 et recherche verticale bornée à 5. Le biome possède aussi
|
||||
`spring_water`. Cela fournit des exemples concrets de décorations locales,
|
||||
sans construction préalable d'un réseau régional de cours d'eau.
|
||||
Leur coût réel dans Sanctuary n'a pas été mesuré isolément.
|
||||
|
||||
Les **144,569 s** du [comparatif beta.174](worldgen-lab-beta174.md) couvrent
|
||||
le lancement et la zone de contrôle du profil complet. La région
|
||||
hydrologique centrale coûtait **20,168 s et 5 541 443 sondages** lors de ce
|
||||
passage ; d'autres planifications contribuaient à l'attente. Les durées sont
|
||||
parfois imbriquées. Ni « 145 s d'eau seule », ni « 2 s garantis pour la
|
||||
nouvelle eau » ne sont des conclusions justifiées par ces mesures.
|
||||
|
||||
## Proposition pour la prochaine expérience
|
||||
|
||||
Un nouveau profil de labo réunirait ces changements, en conservant le relief
|
||||
comme témoin commun. Les sous-étapes suivantes sont une proposition
|
||||
d'implémentation ; palettes, dimensions et budget restent à éprouver.
|
||||
|
||||
1. **Strates continues.** Déformer une coordonnée de profondeur avec un bruit
|
||||
lent, puis sélectionner des couches cohérentes : pierre, andésite, tuf,
|
||||
deepslate, avec quelques lentilles claires. Faire varier hauteur et
|
||||
épaisseur sur de grandes distances ; garder des contacts lisibles sur
|
||||
plusieurs blocs. Des poches plus locales peuvent accompagner ces bancs,
|
||||
sans produire un mélange aléatoire bloc par bloc. Préserver suffisamment
|
||||
de deepslate profonde pour les filons de diamant et refaire leur relevé.
|
||||
2. **Quelques grandes régions au sol.** Introduire automne et cerisiers par
|
||||
un champ climatique à grande échelle, avec des lisières forestières et
|
||||
des prairies communes. Le mélange concerne aussi les arbres, arbustes
|
||||
et sols ; le simple lissage de couleur du client ne suffit pas. Mesurer
|
||||
l'aire et la continuité des régions avant de régler les proportions.
|
||||
3. **Une identité par récif.** Affecter un biome dominant stable à chaque
|
||||
volume déjà déterminé par la seed, jusqu'à 512. Adapter les arbres au
|
||||
gabarit : une mangrove exige un sol humide et un bassin retenu ; les plus
|
||||
petits fragments accueillent une végétation courte ou un sujet isolé.
|
||||
Limiter aussi la décoration pour garder Y=512–639 libre, pas seulement
|
||||
le champ de roche. Liste exacte des identités à comparer ensuite.
|
||||
4. **Petites eaux retenues.** Essayer quelques mares peu profondes en surface
|
||||
et en cavité, avec une inspection bornée du fond et des berges. Refuser
|
||||
les emplacements ouverts vers le vide. Employer uniquement la zone de
|
||||
génération disponible, sans charger des chunks pour chercher un site.
|
||||
Des formes plus grandes pourront être calculées depuis les coordonnées
|
||||
et la seed, puis écrites par chaque chunk dans son emprise. Pas de
|
||||
parcours global de l'île pour cette première expérience.
|
||||
5. **Structures autorisées explicitement.** Exclure les mineshafts de
|
||||
départ, y compris leurs starts et références. Les villages Sanctuary
|
||||
feront ensuite l'objet d'une scène et d'un catalogue propres ; cette
|
||||
passe de terrain ne promet pas encore de les construire.
|
||||
|
||||
Les petites mares peuvent suffire à rendre le monde vivant et fournir de
|
||||
l'eau au joueur. Elles ne résolvent pas encore le placement de grands lacs,
|
||||
les longues rivières ou les cascades entre plusieurs plateaux. Les captures
|
||||
montrent déjà des colonnes de fluide vers le vide : reprendre `spring_water`
|
||||
sans contrôle ne constitue pas à lui seul la solution proposée.
|
||||
|
||||
## Préserver l'intérêt des expansions
|
||||
|
||||
Proposition : Sanctuary Island offre un territoire habitable et une palette
|
||||
de construction variée ; ses récifs donnent de courtes excursions. Une
|
||||
expansion apporte une géographie à parcourir, une contrainte, un lieu
|
||||
spécifique et une récompense ou progression propres. Exemples à concevoir :
|
||||
mine reconfigurée, village Sanctuary, ruine, donjon, ressource avancée,
|
||||
rencontre ou activité liée au Storyquest.
|
||||
|
||||
La petite taille d'un îlot ne suffit pas à préserver l'exclusivité d'une
|
||||
ressource renouvelable : récupérer une pousse permet ensuite de cultiver
|
||||
son bois. Il faut donc décider ce qui est librement cultivable au départ
|
||||
et ce qui donne encore une raison d'ouvrir l'expansion. Proposition : ne
|
||||
pas faire reposer tout l'intérêt d'une destination sur une couleur de bois ;
|
||||
réserver les défis, découvertes et étapes de progression à ces territoires.
|
||||
La répartition déjà demandée, notamment sans or ni redstone sur l'île,
|
||||
reste une contrainte indépendante à contrôler lorsque des biomes natifs
|
||||
et leurs décorations sont introduits.
|
||||
|
||||
Le mot « acide » est conservé dans la transcription. Aucun biome acide,
|
||||
liquide, dégâts ou bloc nouveau n'est déduit de cette formulation seule.
|
||||
|
||||
## Relevés qui permettront de choisir
|
||||
|
||||
- Graines 0, 42 et 173, nouveaux mondes seulement ; conserver la visite
|
||||
`visite175` et son code de génération pour comparer.
|
||||
- Coupes de matériaux sur les mêmes coordonnées : continuité des couches,
|
||||
épaisseur, transition vers la deepslate et absence de modification du
|
||||
relief brut.
|
||||
- Cartes de biomes à la surface réelle de l'île et de chaque récif : aire,
|
||||
taille des régions connectées, lisières, décoration effective et limite
|
||||
à 512. Une simple carte climatique à une altitude fixe serait insuffisante.
|
||||
- Comptage d'eau placée et retenue après ticks de fluides, examen des bords
|
||||
et du dessous, répétabilité et réouverture. Mesurer le temps de placement
|
||||
par chunk et les temps de démarrage séparément, avec un nombre d'essais
|
||||
borné, en vérifiant zéro plan hydrologique régional.
|
||||
- Vérifier les structures enregistrées sur l'emprise complète de départ,
|
||||
pas seulement l'absence de planches au spawn. Contrôler aussi les minerais
|
||||
après les nouvelles décorations et terminer le comptage global de diamants.
|
||||
- Procéder au recensement par lots avec mémoire bornée : la première
|
||||
tentative exhaustive beta.175 a épuisé son heap de 1 Gio. Elle ne donne
|
||||
aucun total exploitable pour toute l'île. Éviter de la relancer en
|
||||
parallèle de la visite solo à 32 chunks sur la machine de 8 Gio.
|
||||
|
||||
La variante devra porter de nouveaux identifiants de génération et sa
|
||||
version binaire propre lors de la livraison, avec `check build` et les
|
||||
assemblages concernés. Aucun changement de preset, de chunks, de structures
|
||||
ou de biomes n'est appliqué au monde actuellement visité par cette fiche.
|
||||
@@ -1,5 +1,11 @@
|
||||
# Premier monde Sanctuary
|
||||
|
||||
**Reprise du 29 septembre 2026 :** le
|
||||
[labo worldgen beta.174](worldgen-lab-beta174.md) permet d'itérer sur le relief
|
||||
sans les plans hydrologiques et structurels. Ce profil optionnel concerne
|
||||
uniquement de nouveaux mondes de laboratoire ; la génération normale reste
|
||||
inchangée. Les sections ci-dessous conservent l'historique des générateurs.
|
||||
|
||||
## Alpha.13 — génération unifiée
|
||||
|
||||
Le choix public **Sanctuary** (`sanctuary:sanctuary`) crée l’île principale à
|
||||
|
||||
+2
-2
@@ -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.145
|
||||
pack_version=beta.145
|
||||
mod_version=beta.178
|
||||
pack_version=beta.178
|
||||
resource_pack_version=beta.121
|
||||
maven_group=fr.koka.sanctuary
|
||||
jei_version=30.32.0-sanctuary.3
|
||||
|
||||
@@ -16,5 +16,39 @@ processResources {
|
||||
filesMatching('fabric.mod.json') { expand(values) }
|
||||
}
|
||||
loom { runs { client { runDir 'run/quick-test' } } }
|
||||
loom {
|
||||
runs {
|
||||
duoServer {
|
||||
server()
|
||||
name 'Sanctuary duo lab'
|
||||
runDir '../../build/duo/server'
|
||||
vmArg '-Xms256m'
|
||||
vmArg '-Xmx768m'
|
||||
vmArg '-Dsanctuary.duo=true'
|
||||
}
|
||||
duoClient {
|
||||
client()
|
||||
name 'Sanctuary duo client'
|
||||
runDir '../../build/duo/client'
|
||||
vmArg '-Xms512m'
|
||||
vmArg '-Xmx2G'
|
||||
programArgs '--graphicsBackend', 'vulkan', '--username', 'KokaLab', '--width', '1280', '--height', '720'
|
||||
}
|
||||
}
|
||||
}
|
||||
// Export the Loom launcher so play sessions do not retain a Gradle JVM.
|
||||
tasks.register('exportDuoLaunch') {
|
||||
dependsOn tasks.named('runDuoServer').get().taskDependencies.getDependencies(tasks.named('runDuoServer').get())
|
||||
dependsOn tasks.named('runDuoClient').get().taskDependencies.getDependencies(tasks.named('runDuoClient').get())
|
||||
doLast {
|
||||
['server': 'runDuoServer', 'client': 'runDuoClient'].each { kind, taskName ->
|
||||
def launch = tasks.named(taskName).get()
|
||||
def destination = rootProject.layout.buildDirectory.file("duo/${kind}.args").get().asFile
|
||||
destination.parentFile.mkdirs()
|
||||
def arguments = launch.allJvmArgs.findAll { !it.toString().startsWith('@') } + ['-classpath', launch.classpath.asPath, launch.mainClass.get()] + launch.args + launch.argumentProviders.collectMany { it.asArguments().toList() }
|
||||
destination.text = arguments.collect { '"' + it.toString().replace('\\', '\\\\').replace('"', '\\"') + '"' }.join('\n') + '\n'
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.named('test') { failOnNoDiscoveredTests=false }
|
||||
tasks.named('jar') { from(rootProject.file('LICENSE')) { rename { 'LICENSE_sanctuary_test' } } }
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import fr.koka.sanctuary.operator.OperatorService;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.PacketFlow;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ClientInformation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.CommonListenerCookie;
|
||||
import net.minecraft.world.level.GameType;
|
||||
|
||||
/** Optional development module only. No sockets, renderer or additional JVM per actor. */
|
||||
final class DuoLab {
|
||||
private static final Map<String, ServerPlayer> ACTORS = new LinkedHashMap<>();
|
||||
private static java.io.BufferedWriter trace;
|
||||
private static int ticks;
|
||||
private static long traceStarted;
|
||||
private DuoLab() { }
|
||||
|
||||
static void register() {
|
||||
// An explicit JVM opt-in keeps the ordinary quick-test pack unchanged.
|
||||
if (!Boolean.getBoolean("sanctuary.duo")) return;
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registries, environment) ->
|
||||
dispatcher.register(Commands.literal("duo").requires(OperatorService::canCommand)
|
||||
.then(Commands.literal("trace")
|
||||
.then(Commands.literal("start").executes(c -> trace(c.getSource(), true)))
|
||||
.then(Commands.literal("stop").executes(c -> trace(c.getSource(), false))))
|
||||
.then(Commands.literal("spawn").then(Commands.argument("name", StringArgumentType.word())
|
||||
.executes(c -> spawn(c.getSource(), StringArgumentType.getString(c, "name")))))
|
||||
.then(Commands.literal("remove").then(Commands.argument("name", StringArgumentType.word())
|
||||
.executes(c -> remove(c.getSource(), StringArgumentType.getString(c, "name")))))
|
||||
.then(Commands.literal("list").executes(c -> {
|
||||
c.getSource().sendSuccess(() -> Component.translatable("sanctuary_test.duo.list", String.join(", ", ACTORS.keySet())), false);
|
||||
return ACTORS.size();
|
||||
}))));
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> {
|
||||
if (trace != null && System.nanoTime() - traceStarted >= 300_000_000_000L) closeTrace();
|
||||
if (trace == null || ++ticks % 40 != 0) return;
|
||||
try {
|
||||
for (var player : server.getPlayerList().getPlayers()) {
|
||||
var row = new com.google.gson.JsonObject();
|
||||
row.addProperty("time", java.time.Instant.now().toString());
|
||||
row.addProperty("player", player.getGameProfile().name());
|
||||
row.addProperty("actor", ACTORS.containsValue(player));
|
||||
row.addProperty("dimension", player.level().dimension().identifier().toString());
|
||||
row.addProperty("x", player.getX()); row.addProperty("y", player.getY()); row.addProperty("z", player.getZ());
|
||||
row.addProperty("yaw", player.getYRot()); row.addProperty("pitch", player.getXRot());
|
||||
trace.write(row.toString()); trace.newLine();
|
||||
}
|
||||
trace.flush();
|
||||
} catch (java.io.IOException error) {
|
||||
org.slf4j.LoggerFactory.getLogger("sanctuary-duo").error("Trace stopped: cannot write session", error);
|
||||
closeTrace();
|
||||
}
|
||||
});
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(server -> { ACTORS.clear(); closeTrace(); });
|
||||
}
|
||||
|
||||
private static int trace(CommandSourceStack source, boolean start) {
|
||||
closeTrace();
|
||||
if (start) try {
|
||||
var folder = java.nio.file.Path.of("duo-sessions");
|
||||
java.nio.file.Files.createDirectories(folder);
|
||||
var path = folder.resolve("positions-" + System.currentTimeMillis() + ".jsonl");
|
||||
trace = java.nio.file.Files.newBufferedWriter(path, StandardCharsets.UTF_8, java.nio.file.StandardOpenOption.CREATE_NEW);
|
||||
traceStarted = System.nanoTime(); ticks = 0;
|
||||
} catch (java.io.IOException error) {
|
||||
source.sendFailure(Component.translatable("sanctuary_test.duo.trace_failed")); return 0;
|
||||
}
|
||||
source.sendSuccess(() -> Component.translatable("sanctuary_test.duo.trace_" + (start ? "started" : "stopped")), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static void closeTrace() {
|
||||
if (trace != null) try { trace.close(); } catch (java.io.IOException ignored) { }
|
||||
trace = null;
|
||||
}
|
||||
|
||||
private static int spawn(CommandSourceStack source, String name) {
|
||||
if (!name.matches("[A-Za-z0-9_]{1,12}") || ACTORS.size() >= 4
|
||||
|| source.getServer().getPlayerList().getPlayerByName("Lab_" + name) != null) {
|
||||
source.sendFailure(Component.translatable("sanctuary_test.duo.invalid"));
|
||||
return 0;
|
||||
}
|
||||
String fullName = "Lab_" + name;
|
||||
var profile = new GameProfile(UUID.nameUUIDFromBytes(("SanctuaryDuo:" + fullName).getBytes(StandardCharsets.UTF_8)), fullName);
|
||||
var player = new ServerPlayer(source.getServer(), source.getLevel(), profile, ClientInformation.createDefault());
|
||||
source.getServer().getPlayerList().placeNewPlayer(new SinkConnection(), player, CommonListenerCookie.createInitial(profile, false));
|
||||
var position = source.getPosition();
|
||||
player.teleportTo(source.getLevel(), position.x + 2, position.y, position.z, java.util.Set.of(), 0, 0, false);
|
||||
player.setGameMode(GameType.CREATIVE);
|
||||
ACTORS.put(name, player);
|
||||
source.sendSuccess(() -> Component.translatable("sanctuary_test.duo.spawned", fullName), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int remove(CommandSourceStack source, String name) {
|
||||
var player = ACTORS.remove(name);
|
||||
if (player == null) {
|
||||
source.sendFailure(Component.translatable("sanctuary_test.duo.missing", name));
|
||||
return 0;
|
||||
}
|
||||
source.getServer().getPlayerList().remove(player);
|
||||
source.sendSuccess(() -> Component.translatable("sanctuary_test.duo.removed", name), false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static final class SinkConnection extends Connection {
|
||||
private net.minecraft.network.PacketListener listener;
|
||||
SinkConnection() { super(PacketFlow.SERVERBOUND); }
|
||||
@Override public <T extends net.minecraft.network.PacketListener> void setupInboundProtocol(net.minecraft.network.ProtocolInfo<T> protocol, T listener) { this.listener = listener; }
|
||||
@Override public net.minecraft.network.PacketListener getPacketListener() { return listener; }
|
||||
@Override public void flushChannel() { }
|
||||
@Override public void send(Packet<?> packet) { }
|
||||
@Override public void send(Packet<?> packet, io.netty.channel.ChannelFutureListener listener) { }
|
||||
@Override public void send(Packet<?> packet, io.netty.channel.ChannelFutureListener listener, boolean flush) { }
|
||||
@Override public boolean isConnected() { return true; }
|
||||
@Override public boolean isConnecting() { return false; }
|
||||
@Override public boolean isMemoryConnection() { return true; }
|
||||
@Override public SocketAddress getRemoteAddress() { return new InetSocketAddress("127.0.0.1", 0); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
|
||||
/** New lab-only ecology. All returned holders belong to this palette, including the cave variants. */
|
||||
public final class EcologyBiomes176 extends BiomeSource {
|
||||
public static final MapCodec<EcologyBiomes176> CODEC = RecordCodecBuilder.mapCodec(i -> i.group(
|
||||
Biome.CODEC.listOf().fieldOf("biomes").forGetter(s -> s.biomes)
|
||||
).apply(i, EcologyBiomes176::new));
|
||||
private final List<Holder<Biome>> biomes;
|
||||
private final Map<String,Holder<Biome>> named;
|
||||
private record State(long seed, List<SkyFragments175.Reef> reefs, double cos, double sin) {}
|
||||
private volatile State state;
|
||||
|
||||
public EcologyBiomes176(List<Holder<Biome>> biomes) {
|
||||
this.biomes=List.copyOf(biomes);
|
||||
var values=new HashMap<String,Holder<Biome>>();
|
||||
for(var b:biomes) values.put(b.unwrapKey().orElseThrow().identifier().getPath(),b);
|
||||
named=Map.copyOf(values);
|
||||
for(String name:List.of("forest","birch","flowers","meadow","autumn","cherry","jungle",
|
||||
"swamp","mangrove","autumn_scrub","cherry_scrub","lush","dripstone","void"))
|
||||
if(!named.containsKey("ecology_"+name))throw new IllegalArgumentException("Missing ecology biome "+name);
|
||||
}
|
||||
public void bind(long seed,SkyFragments175.Field field) {
|
||||
double angle=(mix(seed^0x176B10L)>>>11)*0x1.0p-53*Math.PI*2;
|
||||
state=new State(seed,field.reefs(),Math.cos(angle),Math.sin(angle));
|
||||
}
|
||||
@Override protected MapCodec<EcologyBiomes176> codec(){return CODEC;}
|
||||
@Override protected Stream<Holder<Biome>> collectPossibleBiomes(){return biomes.stream();}
|
||||
@Override public BiomeResolver createResolver(Climate.Sampler ignored){
|
||||
return (qx,qy,qz)->resolve(qx*4,qy*4,qz*4);
|
||||
}
|
||||
public Holder<Biome> resolve(int x,int y,int z) {
|
||||
var s=state;
|
||||
if(s==null)throw new IllegalStateException("Ecology palette must be bound before chunk generation");
|
||||
if(y<0 || y>=512 || Math.hypot(x,z)>398)return get("void");
|
||||
if(y>=320) {
|
||||
int nearest=-1;double best=Double.POSITIVE_INFINITY;
|
||||
for(int i=0;i<s.reefs().size();i++) {
|
||||
var r=s.reefs().get(i);
|
||||
double distance=Math.pow((x-r.x())/(double)(r.length()+20),2)
|
||||
+Math.pow((z-r.z())/(double)(r.length()+20),2)+Math.pow((y-r.y())/35.0,2);
|
||||
if(distance<best){best=distance;nearest=i;}
|
||||
}
|
||||
return get(switch(nearest){case 0->"jungle";case 1->"mangrove";case 2->"swamp";
|
||||
case 3->"autumn_scrub";case 4->"cherry_scrub";default->"void";});
|
||||
}
|
||||
double humidity=noise(s.seed()^0xCAE176L,x/110.0,z/110.0);
|
||||
if(y<174)return get(humidity>-.1?"lush":"dripstone");
|
||||
double across=x*s.cos()+z*s.sin()+65*noise(s.seed(),x/170.0,z/170.0);
|
||||
if(across<-105)return get("autumn");
|
||||
if(across<-65)return get("birch");
|
||||
if(across>105)return get("cherry");
|
||||
if(across>65)return get("flowers");
|
||||
return get(noise(s.seed()^0x6ADE176L,x/130.0,z/130.0)>.05?"meadow":"forest");
|
||||
}
|
||||
private Holder<Biome> get(String name){return named.get("ecology_"+name);}
|
||||
public static long mix(long n){n=(n^(n>>>30))*0xbf58476d1ce4e5b9L;n=(n^(n>>>27))*0x94d049bb133111ebL;return n^(n>>>31);}
|
||||
private static double at(long seed,int x,int z){return (mix(seed^x*0x9e3779b97f4a7c15L^z*0xc2b2ae3d27d4eb4fL)>>>11)*0x1.0p-52-1;}
|
||||
public static double noise(long seed,double x,double z){
|
||||
int ix=(int)Math.floor(x),iz=(int)Math.floor(z);double u=x-ix,v=z-iz;
|
||||
u=u*u*(3-2*u);v=v*v*(3-2*v);
|
||||
return lerp(lerp(at(seed,ix,iz),at(seed,ix+1,iz),u),lerp(at(seed,ix,iz+1),at(seed,ix+1,iz+1),u),v);
|
||||
}
|
||||
private static double lerp(double a,double b,double t){return a+(b-a)*t;}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.material.MaterialRuleContext;
|
||||
import net.minecraft.world.level.levelgen.material.rule.*;
|
||||
|
||||
/** Continuous warped beds, evaluated natively during material generation. No second volume scan. */
|
||||
public record EcologyStrata176() implements MaterialRule {
|
||||
public static final MapCodec<EcologyStrata176> CODEC=MapCodec.unit(EcologyStrata176::new);
|
||||
private static final Identifier SALT=Identifier.fromNamespaceAndPath("sanctuary_test","ecology_strata_v1");
|
||||
public static long seed(RandomState random){return random.getOrCreateRandomFactory(SALT).at(0,0,0).nextLong();}
|
||||
public static double warp(long seed,int x,int z){
|
||||
return 25*EcologyBiomes176.noise(seed,x/135.0,z/135.0)
|
||||
+7*EcologyBiomes176.noise(seed^0x57A7AL,x/43.0,z/43.0);
|
||||
}
|
||||
public static BlockState rock(double depth,int y){
|
||||
if(y>=320){double bed=depth-Math.floor(depth/24)*24;
|
||||
return (bed<3?Blocks.CALCITE:bed<9?Blocks.ANDESITE:Blocks.STONE).defaultBlockState();}
|
||||
double bed=depth-Math.floor(depth/31)*31;
|
||||
Block block;
|
||||
if(depth<112)block=bed<3?Blocks.TUFF:Blocks.DEEPSLATE;
|
||||
else if(depth<144)block=bed<12?Blocks.TUFF:Blocks.DEEPSLATE;
|
||||
else if(depth<174)block=bed<18?Blocks.TUFF:Blocks.ANDESITE;
|
||||
else block=bed<3?Blocks.CALCITE:bed<11?Blocks.ANDESITE:Blocks.STONE;
|
||||
return block.defaultBlockState();
|
||||
}
|
||||
@Override public RuleEvaluator compile(MaterialRuleContext context){
|
||||
long seed=context.getOrCreateRandomFactory(SALT).at(0,0,0).nextLong();
|
||||
// Each evaluator belongs to a material context; the column cache has no shared mutable state.
|
||||
return new RuleEvaluator(){
|
||||
int lastX=Integer.MIN_VALUE,lastZ=Integer.MIN_VALUE;double displacement;
|
||||
@Override public BlockState tryApply(int x,int y,int z){
|
||||
if(x!=lastX || z!=lastZ){displacement=warp(seed,x,z);lastX=x;lastZ=z;}
|
||||
if(y>=174 && context.stoneDepthAbove()<=3){
|
||||
String biome=context.getBiome().unwrapKey().orElseThrow().identifier().getPath();
|
||||
if(biome.equals("ecology_mangrove"))return Blocks.MUD.defaultBlockState();
|
||||
return (context.stoneDepthAbove()==1?Blocks.GRASS_BLOCK:Blocks.DIRT).defaultBlockState();
|
||||
}
|
||||
return rock(y+displacement,y);
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override public MapCodec<EcologyStrata176> codec(){return CODEC;}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.google.gson.*;
|
||||
import fr.koka.sanctuary.worldgen.PopulationHydrologyRuntime;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.*;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.*;
|
||||
|
||||
/** Reproducible density/biome maps plus actual material cuts, reef witnesses and native fluid ticks. */
|
||||
final class EcologySurvey176 {
|
||||
private static final Gson JSON=new GsonBuilder().setPrettyPrinting().create();
|
||||
private record Pending(Path directory,Path out,Map<String,Object> result,Map<String,Object> data,
|
||||
long started,List<EcologyWater176.Pond> ponds,List<Set<BlockPos>> before,int ticks) {}
|
||||
private static final Map<MinecraftServer,Pending> PENDING=new WeakHashMap<>();
|
||||
private EcologySurvey176(){}
|
||||
static void begin(MinecraftServer server,Path directory,Map<String,Object> result,long started,boolean fresh){
|
||||
long measureStarted=System.nanoTime();
|
||||
var level=server.overworld();var generator=(NoiseBasedChunkGenerator)level.getChunkSource().getGenerator();
|
||||
var random=level.getChunkSource().randomState();
|
||||
boolean relief=WorldgenLab.ecology177(generator);
|
||||
var field=random.getSampler(generator.generatorSettings().value().noiseRouter().finalDensity());
|
||||
var reefs=relief?((ReliefEcology177.Field)field).base().reefs():((SkyFragments175.Field)field).reefs();
|
||||
String profile=relief?"relief-ecology-v1":"ecology-v1";
|
||||
var data=new LinkedHashMap<String,Object>();
|
||||
data.put("seed",level.getSeed());data.put("version",relief?"beta.177":"beta.176");data.put("preset",relief?"sanctuary_test:relief_ecology_v1":"sanctuary_test:ecology_v1");
|
||||
data.put("fresh",fresh);data.put("reefs",reefs);
|
||||
if(!level.getChunkSource().getGeneratorState().possibleStructureSets().isEmpty())
|
||||
throw new IllegalStateException("Native structures admitted into the ecology lab");
|
||||
data.put("admittedStructureSets",0);
|
||||
var oldSettings=level.registryAccess().lookupOrThrow(Registries.NOISE_SETTINGS)
|
||||
.getValue(WorldgenLab.SKY_SETTINGS);
|
||||
var oldRandom=RandomState.create(level.registryAccess().lookupOrThrow(Registries.NOISE),level.getSeed(),oldSettings);
|
||||
var oldField=oldRandom.getSampler(oldSettings.noiseRouter().finalDensity());
|
||||
int matching=0;
|
||||
for(int x=-384;x<=384;x+=32)for(int z=-384;z<=384;z+=32)for(int y=0;y<640;y+=8){
|
||||
if(relief && y>216 && y<320)continue;
|
||||
float a=field.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z),b=oldField.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z);
|
||||
if(Float.floatToIntBits(a)!=Float.floatToIntBits(b))throw new IllegalStateException("Relief changed");
|
||||
matching++;
|
||||
}
|
||||
data.put(relief?"matchingDeepAndSkyDensitySamples":"matchingSky175DensitySamples",matching);
|
||||
var surface=new ArrayList<List<Object>>();var upper=new ArrayList<List<Object>>();
|
||||
for(int z=-380;z<=380;z+=8)for(int x=-380;x<=380;x+=8){
|
||||
int y=top(field,x,z,319,0);
|
||||
if(y>=0)surface.add(List.of(x,z,y,biome(generator,x,y,z)));
|
||||
y=top(field,x,z,511,320);
|
||||
if(y>=320)upper.add(List.of(x,z,y,biome(generator,x,y,z)));
|
||||
}
|
||||
data.put("surfaceMap",surface);data.put("reefMap",upper);data.put("mapStep",8);
|
||||
data.put("mapMethod","Native density > 0 upper surface per column before decoration, native biome resolver; step 8 blocks");
|
||||
var reefWitness=new ArrayList<Map<String,Object>>();
|
||||
for(var reef:reefs){
|
||||
int cx=reef.x()>>4,cz=reef.z()>>4;
|
||||
for(int dz=-1;dz<=1;dz++)for(int dx=-1;dx<=1;dx++)level.getChunk(cx+dx,cz+dz);
|
||||
long logs=0,leaves=0,plants=0,air=0;
|
||||
for(int dz=-1;dz<=1;dz++)for(int dx=-1;dx<=1;dx++){
|
||||
var chunk=level.getChunk(cx+dx,cz+dz);var pos=new BlockPos.MutableBlockPos();
|
||||
if(!chunk.getAllStarts().isEmpty())throw new IllegalStateException("Unexpected structure starts");
|
||||
for(int z=chunk.getPos().getMinBlockZ();z<=chunk.getPos().getMaxBlockZ();z++)
|
||||
for(int x=chunk.getPos().getMinBlockX();x<=chunk.getPos().getMaxBlockX();x++)for(int y=320;y<640;y++){
|
||||
var state=chunk.getBlockState(pos.set(x,y,z));
|
||||
if(y>=512){if(!state.isAir())throw new IllegalStateException("Decoration invaded ISS airspace at "+pos);air++;}
|
||||
else if(state.is(BlockTags.LOGS))logs++;
|
||||
else if(state.is(BlockTags.LEAVES))leaves++;
|
||||
else if(!state.isAir() && !state.isSolidRender() && state.getFluidState().isEmpty())plants++;
|
||||
}
|
||||
}
|
||||
if(leaves+plants==0 && (!relief || reef.y()<480))throw new IllegalStateException("Bare reef "+reef);
|
||||
reefWitness.add(Map.of("x",reef.x(),"z",reef.z(),"y",reef.y(),"logs",logs,"leaves",leaves,"plants",plants,"airAbove512",air));
|
||||
}
|
||||
data.put("reefWitnesses",reefWitness);
|
||||
Path out=directory.resolve(profile+(fresh?"-cold":"-warm"));
|
||||
var palette=new LinkedHashMap<String,Integer>();palette.put("air",0);
|
||||
var sections=new ArrayList<Map<String,Object>>();
|
||||
try{
|
||||
Files.createDirectories(out);
|
||||
for(int z:new int[]{0,96}){
|
||||
byte[] blocks=new byte[96*512];
|
||||
for(int ix=0;ix<96;ix++){
|
||||
int x=-380+ix*8;var chunk=level.getChunk(x>>4,z>>4);
|
||||
for(int y=0;y<512;y++){
|
||||
String id=BuiltInRegistries.BLOCK.getKey(chunk.getBlockState(new BlockPos(x,y,z)).getBlock()).getPath();
|
||||
int index=palette.computeIfAbsent(id,k->palette.size());
|
||||
if(index>255)throw new IllegalStateException("Section palette overflow");
|
||||
blocks[y*96+ix]=(byte)index;
|
||||
}
|
||||
}
|
||||
String file="section-"+z+".bin";Files.write(out.resolve(file),blocks);
|
||||
sections.add(Map.of("z",z,"xStart",-380,"step",8,"width",96,"height",512,"file",file));
|
||||
}
|
||||
data.put("sectionPalette",palette);data.put("sections",sections);
|
||||
data.put("minerals",Boolean.getBoolean("sanctuary.worldgenLab.wholeIsland")
|
||||
? IslandMineralSurvey175.measure(level):SkySurvey175.minerals(level));
|
||||
if(relief)data.put("reliefChecks",ReliefChecks177.check(level,(ReliefEcology177.Field)field,(ReliefBiomes177)generator.getBiomeSource(),data,fresh,directory));
|
||||
List<EcologyWater176.Pond> candidates=EcologyWater176.ponds(level);
|
||||
if(!fresh){
|
||||
var previous=JsonParser.parseString(Files.readString(directory.resolve(profile+"-cold/survey.json"))).getAsJsonObject();
|
||||
candidates=Arrays.asList(JSON.fromJson(previous.get("ponds"),EcologyWater176.Pond[].class));
|
||||
}
|
||||
var sorted=candidates.stream().sorted(Comparator.comparingLong(p->(long)p.x()*p.x()+(long)p.z()*p.z())).toList();
|
||||
var selected=new LinkedHashSet<EcologyWater176.Pond>();
|
||||
sorted.stream().filter(p->p.y()>=320).limit(2).forEach(selected::add);
|
||||
sorted.stream().filter(p->p.y()<174).limit(2).forEach(selected::add);
|
||||
for(var p:sorted){if(selected.size()>=8)break;selected.add(p);}
|
||||
var ponds=List.copyOf(selected);
|
||||
data.put("generatedPondCount",candidates.size());
|
||||
if(ponds.isEmpty())throw new IllegalStateException("No bounded water basins generated");
|
||||
var before=new ArrayList<Set<BlockPos>>();
|
||||
for(var pond:ponds){level.setChunkForced(pond.x()>>4,pond.z()>>4,true);before.add(water(level,pond));}
|
||||
data.put("ponds",ponds);data.put("waterBeforeTicks",before.stream().map(Set::size).toList());
|
||||
data.put("surveyBeforeTicksMs",(System.nanoTime()-measureStarted)/1_000_000);
|
||||
PENDING.put(server,new Pending(directory,out,result,data,started,ponds,before,0));
|
||||
fr.koka.sanctuary.SanctuaryMod.LOGGER.info("ECOLOGY_FLUID_CHECK {} basins, 240 native ticks",ponds.size());
|
||||
}catch(java.io.IOException e){throw new IllegalStateException(e);}
|
||||
}
|
||||
static void tick(MinecraftServer server){
|
||||
var p=PENDING.get(server);if(p==null)return;
|
||||
if(p.ticks()<240){PENDING.put(server,new Pending(p.directory(),p.out(),p.result(),p.data(),p.started(),p.ponds(),p.before(),p.ticks()+1));return;}
|
||||
var level=server.overworld();var after=new ArrayList<Integer>();
|
||||
for(int i=0;i<p.ponds().size();i++){
|
||||
var pond=p.ponds().get(i);var water=water(level,pond);after.add(water.size());
|
||||
if(!water.equals(p.before().get(i)))throw new IllegalStateException("Basin water changed: "+pond+" before="+p.before().get(i)+" after="+water);
|
||||
level.setChunkForced(pond.x()>>4,pond.z()>>4,false);
|
||||
}
|
||||
if(PopulationHydrologyRuntime.cachedRegionCount(level.getChunkSource().randomState())!=0)throw new IllegalStateException("Unexpected hydrology");
|
||||
p.data().put("waterAfterTicks",after);p.data().put("fluidTicks",240);p.data().put("hydrologyRegions",0);
|
||||
try{Files.writeString(p.out().resolve("survey.json"),JSON.toJson(p.data())+"\n");}catch(java.io.IOException e){throw new IllegalStateException(e);}
|
||||
p.result().put("survey",Map.of("path",p.out().toString(),"fluidTicks",240,"ponds",p.ponds().size()));
|
||||
p.result().put("readyForVisitMs",(System.nanoTime()-p.started())/1_000_000);
|
||||
PENDING.remove(server);WorldgenLab.writeReport(p.directory(),p.result());
|
||||
}
|
||||
private static Set<BlockPos> water(ServerLevel level,EcologyWater176.Pond p){
|
||||
var sources=new HashSet<BlockPos>();int retained=0;
|
||||
// Native dripstone clusters also create source puddles near our basin.
|
||||
// Snapshot their positions too: a source outside our circle is not itself a leak.
|
||||
// Any flow, loss or new source during the tick window still fails the check.
|
||||
for(int dx=-p.radius()-1;dx<=p.radius()+1;dx++)for(int dz=-p.radius()-1;dz<=p.radius()+1;dz++){
|
||||
boolean inside=dx*dx+dz*dz<=p.radius()*p.radius();
|
||||
for(int dy=-4;dy<=1;dy++){
|
||||
var pos=new BlockPos(p.x()+dx,p.y()+dy,p.z()+dz);
|
||||
var fluid=level.getFluidState(pos);
|
||||
if(!fluid.is(FluidTags.WATER))continue;
|
||||
if(!fluid.isSource())throw new IllegalStateException("Flow near basin "+p+" at "+pos+": "+level.getBlockState(pos));
|
||||
sources.add(pos);
|
||||
if(inside && dy>=-1 && dy<=0)retained++;
|
||||
}
|
||||
}
|
||||
if(retained==0)throw new IllegalStateException("Empty basin "+p);
|
||||
return Set.copyOf(sources);
|
||||
}
|
||||
private static int top(DensitySampler f,int x,int z,int max,int min){
|
||||
for(int y=max;y>=min;y--)if(f.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0)return y;return -1;
|
||||
}
|
||||
private static String biome(net.minecraft.world.level.chunk.ChunkGenerator generator,int x,int y,int z){
|
||||
return name(generator.getBiomeSource() instanceof ReliefBiomes177 b?b.resolve(x,y,z):((EcologyBiomes176)generator.getBiomeSource()).resolve(x,y,z));
|
||||
}
|
||||
private static String name(net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> b){return b.unwrapKey().orElseThrow().identifier().getPath();}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
|
||||
/** Bounded, owned basins. Inspect the whole footprint before any write; no hydrology or chunk loads. */
|
||||
public final class EcologyWater176 {
|
||||
public record Pond(int x,int y,int z,int radius,int waterBlocks) {}
|
||||
private static final Map<ServerLevel,Map<Long,Pond>> PLACED=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private EcologyWater176(){}
|
||||
public static List<Pond> ponds(ServerLevel level){synchronized(PLACED){return List.copyOf(PLACED.getOrDefault(level,Map.of()).values());}}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.ecology(generator))return;
|
||||
long seed=level.getSeed()^chunk.getPos().pack()^0x176BA51AL;
|
||||
var random=RandomSource.create(seed);
|
||||
if(random.nextInt(3)!=0)return;
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(int attempt=0;attempt<5;attempt++){
|
||||
int x=chunk.getPos().getMinBlockX()+7+random.nextInt(3),z=chunk.getPos().getMinBlockZ()+7+random.nextInt(3);
|
||||
int radius=3+random.nextInt(2);
|
||||
// First try a top surface, then bounded existing cavity floors.
|
||||
int start=attempt<3?508:170;
|
||||
for(int y=start;y>=36;y--){
|
||||
if(!solid(chunk.getBlockState(pos.set(x,y,z))) || !chunk.getBlockState(pos.set(x,y+1,z)).isAir())continue;
|
||||
if(basin(chunk,x,y,z,radius)){
|
||||
int water=0;
|
||||
for(int dx=-radius;dx<=radius;dx++)for(int dz=-radius;dz<=radius;dz++)if(inside(dx,dz,radius)){
|
||||
chunk.setBlockState(pos.set(x+dx,y-2,z+dz),Blocks.CLAY.defaultBlockState());
|
||||
for(int dy=-1;dy<=0;dy++){
|
||||
chunk.setBlockState(pos.set(x+dx,y+dy,z+dz),Blocks.WATER.defaultBlockState());
|
||||
chunk.markPosForPostProcessing(pos);water++;
|
||||
}
|
||||
for(int dy=1;dy<=3;dy++)chunk.setBlockState(pos.set(x+dx,y+dy,z+dz),Blocks.AIR.defaultBlockState());
|
||||
}
|
||||
if(Boolean.getBoolean("sanctuary.worldgenLab.verify"))synchronized(PLACED){
|
||||
PLACED.computeIfAbsent(level.getLevel(),ignored->new ConcurrentHashMap<>())
|
||||
.put(chunk.getPos().pack(),new Pond(x,y,z,radius,water));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// One floor per attempt: reject, never hunt the whole vertical column for a fitting lake.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
private static boolean inside(int x,int z,int radius){return x*x+z*z<=radius*radius;}
|
||||
private static boolean solid(BlockState b){return b.isSolidRender() && b.getFluidState().isEmpty()
|
||||
&& !b.hasBlockEntity() && !b.is(net.minecraft.tags.BlockTags.LOGS) && !b.is(net.minecraft.tags.BlockTags.LEAVES);}
|
||||
private static boolean basin(ChunkAccess chunk,int x,int y,int z,int radius){
|
||||
if(y+3>=512)return false;
|
||||
for(int dx=-radius;dx<=radius;dx++)for(int dz=-radius;dz<=radius;dz++)if(inside(dx,dz,radius)){
|
||||
// Two intact layers below the bed, intact walls on both water levels.
|
||||
for(int dy=-4;dy<=0;dy++)if(!solid(chunk.getBlockState(new BlockPos(x+dx,y+dy,z+dz))))return false;
|
||||
for(int dy=1;dy<=3;dy++){
|
||||
var b=chunk.getBlockState(new BlockPos(x+dx,y+dy,z+dz));
|
||||
if(!b.isAir() && !solid(b))return false;
|
||||
}
|
||||
if(!chunk.getBlockState(new BlockPos(x+dx,y+4,z+dz)).isAir())return false;
|
||||
for(var direction:Direction.Plane.HORIZONTAL){
|
||||
int nx=dx+direction.getStepX(),nz=dz+direction.getStepZ();
|
||||
if(!inside(nx,nz,radius))for(int dy=-1;dy<=0;dy++)
|
||||
if(!solid(chunk.getBlockState(new BlockPos(x+nx,y+dy,z+nz))))return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.horde.*;
|
||||
import fr.koka.sanctuary.testing.HordeLabScene;
|
||||
import fr.koka.sanctuary.progression.*;
|
||||
import java.nio.file.*;
|
||||
import java.util.Set;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
|
||||
/** Only loaded in the optional development module, with a dedicated directory and explicit switch. */
|
||||
final class HordeLab {
|
||||
private static boolean active;
|
||||
private HordeLab() { }
|
||||
static void register() {
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> { if (requested(server)) prepare(server); });
|
||||
ServerPlayConnectionEvents.JOIN.register((handler,sender,server) -> {
|
||||
if (active) server.execute(() -> enter(handler.player));
|
||||
});
|
||||
ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer,newPlayer,alive) -> {
|
||||
if (active) newPlayer.level().getServer().execute(() -> enter(newPlayer));
|
||||
});
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher,registries,environment) -> dispatcher.register(
|
||||
Commands.literal("horde").requires(source -> active)
|
||||
.then(Commands.literal("cartes").executes(c -> {
|
||||
var p=c.getSource().getPlayerOrException();if(HordeTrials.enrolled(p)!=null)return 0;
|
||||
for(int t=0;t<3;t++){var card=HordeCards.create(p.level(),t,p.getRandom().nextLong());
|
||||
if(!p.getInventory().add(card))p.spawnAtLocation(p.level(),card);}
|
||||
return 1;
|
||||
}))
|
||||
.then(Commands.literal("retour").executes(context -> {
|
||||
var player=context.getSource().getPlayerOrException();
|
||||
if(HordeTrials.enrolled(player)!=null)return 0;
|
||||
enter(player);return 1;
|
||||
}))));
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(server -> active=false);
|
||||
}
|
||||
private static boolean requested(MinecraftServer server){
|
||||
if(Boolean.getBoolean("sanctuary.hordeLab"))return true;
|
||||
var world=server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize();
|
||||
return world.getFileName().toString().equals("horde-flat-168")&&fr.koka.sanctuary.testing.QuickTestWorld.active(server);
|
||||
}
|
||||
private static void prepare(MinecraftServer server) {
|
||||
var world=server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize();
|
||||
if(!world.getFileName().toString().equals("horde-flat-168") || !fr.koka.sanctuary.testing.QuickTestWorld.active(server))
|
||||
throw new IllegalStateException("Horde prototype requires its dedicated horde-flat-168 development world");
|
||||
var marker=world.resolve("horde-lab-168.txt");
|
||||
try {
|
||||
if(!Files.exists(marker)) {
|
||||
Files.writeString(marker,"preparing\n",StandardOpenOption.CREATE_NEW);
|
||||
HordeLabScene.build(server.overworld(),HordeLabScene.CENTER,false);
|
||||
Files.writeString(marker,"ready-v1\n");
|
||||
} else {
|
||||
if(!Files.readString(marker).equals("ready-v1\n")
|
||||
|| !server.overworld().getBlockState(HordeLabScene.CENTER.below()).is(net.minecraft.world.level.block.Blocks.POLISHED_BLACKSTONE))
|
||||
throw new IllegalStateException("Incomplete or modified horde lab preserved; use a fresh development directory");
|
||||
}
|
||||
}catch(java.io.IOException error){throw new IllegalStateException("Cannot prepare isolated horde lab",error);}
|
||||
var rules=server.overworld().getGameRules();
|
||||
rules.set(GameRules.KEEP_INVENTORY,true,server);
|
||||
rules.set(GameRules.SPAWN_MOBS,false,server);
|
||||
rules.set(GameRules.MOB_GRIEFING,false,server);
|
||||
rules.set(GameRules.ADVANCE_WEATHER,false,server);
|
||||
active=true;
|
||||
}
|
||||
private static void enter(ServerPlayer player) {
|
||||
var center=HordeLabScene.CENTER;
|
||||
player.teleportTo(player.level().getServer().overworld(),center.getX()+.5,center.getY(),center.getZ()+4.5,Set.of(),180,0,false);
|
||||
player.setGameMode(GameType.ADVENTURE);
|
||||
// This test character alone gets normal combat capacities; no production profile is touched.
|
||||
var record=ProgressEffects.get(player);
|
||||
if(record!=null) {
|
||||
while(record.vitality()<7)record=record.buy("vitality",0);
|
||||
while(record.hunger()<7)record=record.buy("hunger",0);
|
||||
player.setAttached(ProgressionService.RECORD,record.json());
|
||||
ProgressEffects.apply(player,record);ProgressionService.send(player,"");
|
||||
}
|
||||
player.addEffect(new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.NIGHT_VISION,-1,0,false,false));
|
||||
player.setHealth(player.getMaxHealth());player.getFoodData().setFoodLevel(20);
|
||||
if(!player.entityTags().contains("sanctuary_horde_kit168")) {
|
||||
player.getInventory().add(new ItemStack(Items.IRON_SWORD));
|
||||
player.getInventory().add(HordeCards.create(player.level(),0,player.getRandom().nextLong()));
|
||||
for(int tier=1;tier<3;tier++)player.getInventory().add(HordeCards.create(player.level(),tier,player.getRandom().nextLong()));
|
||||
player.getInventory().add(new ItemStack(Items.COOKED_BEEF,32));
|
||||
player.setItemSlot(EquipmentSlot.CHEST,new ItemStack(Items.IRON_CHESTPLATE));
|
||||
player.setItemSlot(EquipmentSlot.LEGS,new ItemStack(Items.IRON_LEGGINGS));
|
||||
player.setItemSlot(EquipmentSlot.FEET,new ItemStack(Items.IRON_BOOTS));
|
||||
player.addTag("sanctuary_horde_kit168");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.SanctuaryMod;
|
||||
import fr.koka.sanctuary.worldgen.IslandCapacity;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
/** Exhaustive development-only census of the island envelope, not a central-window extrapolation. */
|
||||
final class IslandMineralSurvey175 {
|
||||
private IslandMineralSurvey175() {}
|
||||
static Map<String,Object> measure(ServerLevel level) {
|
||||
long start=System.nanoTime();
|
||||
var chunks=new ArrayList<List<Integer>>();
|
||||
for(int z=-25;z<=24;z++)for(int x=-25;x<=24;x++)
|
||||
if(IslandCapacity.TEN.areaExists(x,z))chunks.add(List.of(x,z));
|
||||
var counts=new TreeMap<String,Long>();
|
||||
var byHeight=new TreeMap<String,long[]>();
|
||||
var byChunk=new ArrayList<Map<String,Object>>();
|
||||
int done=0,exposed=0,high=0;
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(var pair:chunks) {
|
||||
int cx=pair.get(0),cz=pair.get(1);
|
||||
// Finish immediate neighbours too before inspecting decoration at the boundary.
|
||||
for(int dz=-1;dz<=1;dz++)for(int dx=-1;dx<=1;dx++)level.getChunk(cx+dx,cz+dz);
|
||||
var chunk=level.getChunk(cx,cz);
|
||||
var local=new TreeMap<String,Long>();
|
||||
for(int si=0;si<40;si++) {
|
||||
var section=chunk.getSections()[si];
|
||||
section.getStates().count((state,count)->{
|
||||
String id=BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath();
|
||||
counts.merge(id,(long)count,Long::sum);
|
||||
if(id.endsWith("_ore"))local.merge(id,(long)count,Long::sum);
|
||||
});
|
||||
if(!section.maybeHas(IslandMineralSurvey175::ore))continue;
|
||||
for(int y=0;y<16;y++)for(int z=0;z<16;z++)for(int x=0;x<16;x++) {
|
||||
var state=section.getBlockState(x,y,z);
|
||||
if(!ore(state))continue;
|
||||
String id=BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath();
|
||||
int worldY=si*16+y;
|
||||
byHeight.computeIfAbsent(id,k->new long[640])[worldY]++;
|
||||
if(id.endsWith("diamond_ore")) {
|
||||
if(worldY>=136 || id.equals("diamond_ore"))high++;
|
||||
pos.set(cx*16+x,worldY,cz*16+z);
|
||||
boolean air=false;
|
||||
for(var direction:Direction.values())if(level.getBlockState(pos.relative(direction)).isAir())air=true;
|
||||
if(air)exposed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
byChunk.add(Map.of("x",cx,"z",cz,"ores",local));
|
||||
// This finite census owns at most the island envelope plus immediate neighbours.
|
||||
// Retain chunks for this pass. Artificially ticking expiry while the server is
|
||||
// inside SERVER_STARTED reloads/snapshots the same generation halo repeatedly.
|
||||
done++;
|
||||
if(done%128==0 || done==chunks.size())SanctuaryMod.LOGGER.info("ISLAND_MINERALS {} / {} chunks; {} diamond blocks",
|
||||
done,chunks.size(),total(counts,"diamond"));
|
||||
if(done%128==0)SanctuaryMod.LOGGER.info("ISLAND_MINERALS_MEMORY loadedChunks={} usedMiB={}",
|
||||
level.getChunkSource().getLoadedChunksCount(),(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory())/1048576);
|
||||
}
|
||||
if(total(counts,"gold")!=0 || total(counts,"redstone")!=0 || exposed!=0 || high!=0)
|
||||
throw new IllegalStateException("Whole-island mineral contract failed: "+counts+" exposed="+exposed+" high="+high);
|
||||
return Map.of("scope","whole-island","chunks",chunks,"yMin",0,"yMaxExclusive",640,
|
||||
"counts",counts,"oresByHeight",byHeight,"byChunk",byChunk,"exposedDiamonds",exposed,
|
||||
"highDiamonds",high,"durationMs",(System.nanoTime()-start)/1_000_000);
|
||||
}
|
||||
private static boolean ore(BlockState state) {return BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath().endsWith("_ore");}
|
||||
private static long total(Map<String,Long> counts,String name) {
|
||||
return counts.getOrDefault(name+"_ore",0L)+counts.getOrDefault("deepslate_"+name+"_ore",0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.*;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.feature.*;
|
||||
|
||||
/** Finite deposits in the main island only. Chunk-owned writes; no terrain sondages or hydrology. */
|
||||
public final class LabMinerals175 {
|
||||
private LabMinerals175() {}
|
||||
private record Deposit(Block stone, Block deep, int attempts, int size, double minDepth, double maxDepth) {}
|
||||
private static final Deposit[] DEPOSITS={
|
||||
new Deposit(Blocks.COPPER_ORE,Blocks.DEEPSLATE_COPPER_ORE,34,18,.08,.72),
|
||||
new Deposit(Blocks.COAL_ORE,Blocks.DEEPSLATE_COAL_ORE,28,16,.04,.78),
|
||||
new Deposit(Blocks.IRON_ORE,Blocks.DEEPSLATE_IRON_ORE,3,5,.18,.86),
|
||||
new Deposit(Blocks.LAPIS_ORE,Blocks.DEEPSLATE_LAPIS_ORE,4,5,.35,.88),
|
||||
new Deposit(Blocks.DIAMOND_ORE,Blocks.DEEPSLATE_DIAMOND_ORE,1,5,.65,.86)
|
||||
};
|
||||
private static final Set<Block> REPLACED=Set.of(Blocks.COAL_ORE,Blocks.DEEPSLATE_COAL_ORE,
|
||||
Blocks.COPPER_ORE,Blocks.DEEPSLATE_COPPER_ORE,Blocks.IRON_ORE,Blocks.DEEPSLATE_IRON_ORE,
|
||||
Blocks.GOLD_ORE,Blocks.DEEPSLATE_GOLD_ORE,Blocks.REDSTONE_ORE,Blocks.DEEPSLATE_REDSTONE_ORE,
|
||||
Blocks.LAPIS_ORE,Blocks.DEEPSLATE_LAPIS_ORE,Blocks.DIAMOND_ORE,Blocks.DEEPSLATE_DIAMOND_ORE);
|
||||
|
||||
public static boolean active(ChunkGenerator generator) {
|
||||
return generator instanceof NoiseBasedChunkGenerator noise && (noise.generatorSettings().is(WorldgenLab.SKY_SETTINGS) || WorldgenLab.ecology(generator));
|
||||
}
|
||||
public static boolean replaces(Feature feature) {
|
||||
// Native geodes can cross chunk boundaries and expose a neighbour's already-placed diamond.
|
||||
// The same geode engine is called below with a wholly owned, bounded envelope instead.
|
||||
return feature instanceof GeodeFeature || feature instanceof AbstractOreFeature ore
|
||||
&& ore.targetStates().stream().anyMatch(t -> REPLACED.contains(t.state().getBlock()));
|
||||
}
|
||||
private static boolean host(BlockState state) {
|
||||
return state.is(BlockTags.BASE_STONE_OVERWORLD) || state.is(Blocks.BLACKSTONE) || state.is(Blocks.BASALT);
|
||||
}
|
||||
private static boolean deep(BlockState state) {
|
||||
return state.is(Blocks.DEEPSLATE) || state.is(Blocks.BLACKSTONE) || state.is(Blocks.BASALT) || state.is(Blocks.TUFF);
|
||||
}
|
||||
private static boolean geodeHost(BlockState state) {
|
||||
return state.isSolidRender() && state.getFluidState().isEmpty() && !state.hasBlockEntity()
|
||||
&& !state.is(BlockTags.LOGS) && !state.is(BlockTags.LEAVES) && !state.is(Blocks.MAGMA_BLOCK)
|
||||
&& !state.is(Blocks.BUDDING_AMETHYST);
|
||||
}
|
||||
private static int[] range(ChunkAccess chunk,int x,int z) {
|
||||
int min=-1,max=-1;
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(int y=1;y<320;y++) if(host(chunk.getBlockState(pos.set(x,y,z)))) {if(min<0)min=y;max=y;}
|
||||
return new int[]{min,max};
|
||||
}
|
||||
private static boolean enclosed(ChunkAccess chunk,BlockPos pos,int radius) {
|
||||
for(int dx=-radius;dx<=radius;dx++) for(int dz=-radius;dz<=radius;dz++) for(int dy=-radius;dy<=radius;dy++) {
|
||||
var state=chunk.getBlockState(pos.offset(dx,dy,dz));
|
||||
if(!state.isSolidRender() || !state.getFluidState().isEmpty()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk) {
|
||||
if(!active(generator))return;
|
||||
int minX=chunk.getPos().getMinBlockX(),minZ=chunk.getPos().getMinBlockZ();
|
||||
long seed=level.getSeed() ^ (long)chunk.getPos().x()*0x632BE59BD9B4E019L
|
||||
^ (long)chunk.getPos().z()*0x9E3779B97F4A7C15L;
|
||||
// Geodes precede minerals, so the diamond enclosure test sees the final cavity.
|
||||
geode(level,generator,chunk,RandomSource.create(seed^0x175A6E0DEL));
|
||||
var ranges=new HashMap<Integer,int[]>();
|
||||
for(int i=0;i<DEPOSITS.length;i++) {
|
||||
var deposit=DEPOSITS[i];var random=RandomSource.create(seed^(0x1750AE5L+i*7919L));
|
||||
boolean diamond=deposit.stone()==Blocks.DIAMOND_ORE;
|
||||
for(int attempt=0;attempt<deposit.attempts();attempt++) {
|
||||
int lx=4+random.nextInt(8),lz=4+random.nextInt(8),x=minX+lx,z=minZ+lz;
|
||||
var bounds=ranges.computeIfAbsent(lx*16+lz,key->range(chunk,x,z));
|
||||
if(bounds[1]-bounds[0]<32)continue;
|
||||
double depth=deposit.minDepth()+random.nextDouble()*(deposit.maxDepth()-deposit.minDepth());
|
||||
int y=(int)Math.round(bounds[1]-depth*(bounds[1]-bounds[0]));
|
||||
var center=new BlockPos(x,y,z);
|
||||
// Compact irregular veins stay at least two blocks from a neighbouring chunk.
|
||||
for(int block=0;block<deposit.size();block++) {
|
||||
var pos=center.offset(random.nextInt(3)-1,random.nextInt(3)-1,random.nextInt(3)-1);
|
||||
var state=chunk.getBlockState(pos);
|
||||
if(!host(state))continue;
|
||||
if(diamond && (!state.is(Blocks.DEEPSLATE) || pos.getY()>=136 || !enclosed(chunk,pos,2)))continue;
|
||||
chunk.setBlockState(pos,(deep(state)?deposit.deep():deposit.stone()).defaultBlockState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void geode(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk,RandomSource random) {
|
||||
if(random.nextInt(8)!=0)return;
|
||||
int x=chunk.getPos().getMinBlockX()+7,z=chunk.getPos().getMinBlockZ()+7;
|
||||
var bounds=range(chunk,x,z);
|
||||
if(bounds[1]-bounds[0]<48)return;
|
||||
var feature=level.registryAccess().lookupOrThrow(Registries.FEATURE)
|
||||
.getValue(Identifier.fromNamespaceAndPath("sanctuary","population_amethyst_geode"));
|
||||
if(!(feature instanceof GeodeFeature geode) || geode.minGenOffset()!=-5 || geode.maxGenOffset()!=7)
|
||||
throw new IllegalStateException("Unexpected bounded amethyst geode");
|
||||
for(int attempt=0;attempt<3;attempt++) {
|
||||
int y=bounds[0]+12+random.nextInt(bounds[1]-bounds[0]-24);
|
||||
var origin=new BlockPos(x,y,z);
|
||||
boolean solid=true;
|
||||
// Includes the engine's write cube and the neighbours of its crystals: local 1..15.
|
||||
for(int dx=-6;solid&&dx<=8;dx++) for(int dz=-6;solid&&dz<=8;dz++) for(int dy=-6;dy<=8;dy++)
|
||||
if(!geodeHost(chunk.getBlockState(origin.offset(dx,dy,dz)))) {solid=false;break;}
|
||||
if(solid) {geode.place(level,generator,random,origin);return;}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.google.gson.*;
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import fr.koka.sanctuary.testing.QuickTestWorld;
|
||||
import java.nio.file.*;
|
||||
import java.util.Set;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.*;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
|
||||
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.gamerules.GameRules;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
|
||||
/** One seed, one palace. Explicitly confined to a fresh optional-module development world. */
|
||||
final class PalaceLab {
|
||||
private static PalacePlan plan;
|
||||
private static MinecraftServer owner;
|
||||
private static Path journal;
|
||||
private static int cursor;
|
||||
private static boolean ready;
|
||||
private PalaceLab() { }
|
||||
|
||||
static void register() {
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(s->{if(Boolean.getBoolean("sanctuary.palaceLab"))prepare(s);});
|
||||
ServerTickEvents.END_SERVER_TICK.register(s->{
|
||||
if(s!=owner || ready)return;
|
||||
for(int n=0;n<768 && cursor<plan.blocks.size();n++) {
|
||||
var v=plan.blocks.get(cursor++);s.overworld().setBlock(v.pos(),v.state(),Block.UPDATE_CLIENTS);
|
||||
}
|
||||
if(cursor==plan.blocks.size()) {
|
||||
s.saveEverything(false,true,true);
|
||||
writeJournal("ready",false);ready=true;
|
||||
s.getPlayerList().getPlayers().forEach(PalaceLab::enter);
|
||||
}
|
||||
});
|
||||
ServerPlayConnectionEvents.JOIN.register((h,sender,s)->{if(s==owner&&ready)s.execute(()->enter(h.player));});
|
||||
ServerPlayerEvents.AFTER_RESPAWN.register((old,p,alive)->{if(p.level().getServer()==owner&&ready)enter(p);});
|
||||
UseBlockCallback.EVENT.register((player,level,hand,hit)->{
|
||||
if(!(player instanceof ServerPlayer p) || level.getServer()!=owner || !ready
|
||||
|| level!=owner.overworld() || hand!=InteractionHand.MAIN_HAND || p.isSpectator())return InteractionResult.PASS;
|
||||
var pos=hit.getBlockPos();
|
||||
if(pos.equals(plan.origin)) {
|
||||
var core=level.getBlockState(pos);
|
||||
if(!core.is(OriginBlock.BLOCK))return InteractionResult.PASS;
|
||||
core=core.cycle(OriginBlock.LIT);level.setBlock(pos,core,Block.UPDATE_ALL);
|
||||
p.sendSystemMessage(Component.translatable(core.getValue(OriginBlock.LIT)?"sanctuary.palace.on":"sanctuary.palace.off"),true);
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
int sector=plan.relays.indexOf(pos);
|
||||
if(sector<0)return InteractionResult.PASS;
|
||||
var result=PalaceAnchors.offer(owner.overworld(),plan,sector,p.getItemInHand(hand));
|
||||
var demand=PalaceAnchors.DEMANDS.get(sector);
|
||||
String key=switch(result){case CORE_OFF->"core_off";case ACTIVATED->"activated";case ALREADY_ACTIVE->"already";case INVALID->"invalid";default->"required";};
|
||||
p.sendSystemMessage(Component.translatable("sanctuary.palace."+key,
|
||||
Component.translatable("sanctuary.palace.direction."+sector),demand.count(),new ItemStack(demand.item()).getHoverName()),true);
|
||||
return InteractionResult.SUCCESS;
|
||||
});
|
||||
CommandRegistrationCallback.EVENT.register((d,r,e)->d.register(Commands.literal("palais")
|
||||
.requires(s->s.getServer()==owner&&ready)
|
||||
.then(Commands.literal("retour").executes(c->{enter(c.getSource().getPlayerOrException());return 1;}))
|
||||
.then(Commands.literal("materiaux").executes(c->{
|
||||
var p=c.getSource().getPlayerOrException();
|
||||
for(var demand:PalaceAnchors.DEMANDS) {
|
||||
var stack=new ItemStack(demand.item(),demand.count());
|
||||
if(!p.getInventory().add(stack))p.spawnAtLocation(p.level(),stack);
|
||||
}
|
||||
return 1;
|
||||
}))));
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(s->{if(s==owner){owner=null;plan=null;journal=null;ready=false;cursor=0;}});
|
||||
}
|
||||
|
||||
private static void prepare(MinecraftServer s) {
|
||||
var world=s.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize();
|
||||
if(!world.getFileName().toString().equals("palace-flat-173")||!QuickTestWorld.active(s))
|
||||
throw new IllegalStateException("Palace laboratory requires its dedicated palace-flat-173 flat world");
|
||||
owner=s;plan=new PalacePlan(s.overworld().getSeed(),new BlockPos(0,96,0));
|
||||
journal=world.resolve("palace-lab-173.json");
|
||||
try {
|
||||
if(Files.exists(journal)) {
|
||||
var data=JsonParser.parseString(Files.readString(journal)).getAsJsonObject();
|
||||
if(data.get("schema").getAsInt()!=1 || data.get("generation").getAsInt()!=PalacePlan.VERSION
|
||||
||data.get("seed").getAsLong()!=plan.seed || !data.get("phase").getAsString().equals("ready")
|
||||
||data.get("originY").getAsInt()!=plan.origin.getY()
|
||||
||!s.overworld().getBlockState(plan.origin).is(OriginBlock.BLOCK))
|
||||
throw new IllegalStateException("Unknown, incomplete or modified palace lab preserved. Use a fresh seed directory.");
|
||||
ready=true;
|
||||
} else {
|
||||
// Refuse populated save data even if somebody removes the marker.
|
||||
if(Files.exists(world.resolve("playerdata")))try(var files=Files.list(world.resolve("playerdata"))) {
|
||||
if(files.findAny().isPresent())throw new IllegalStateException("A fresh laboratory world is required");
|
||||
}
|
||||
plan.checkSpace(s.overworld());writeJournal("preparing",true);
|
||||
}
|
||||
}catch(java.io.IOException | JsonParseException | NullPointerException error){
|
||||
throw new IllegalStateException("Cannot prepare palace lab; world and journal preserved",error);
|
||||
}
|
||||
var rules=s.overworld().getGameRules();
|
||||
rules.set(GameRules.KEEP_INVENTORY,true,s);rules.set(GameRules.SPAWN_MOBS,false,s);
|
||||
rules.set(GameRules.MOB_GRIEFING,false,s);rules.set(GameRules.ADVANCE_WEATHER,false,s);
|
||||
}
|
||||
|
||||
private static void writeJournal(String phase,boolean create) {
|
||||
var data=new JsonObject();data.addProperty("schema",1);data.addProperty("generation",PalacePlan.VERSION);
|
||||
data.addProperty("seed",plan.seed);data.addProperty("originY",plan.origin.getY());data.addProperty("phase",phase);
|
||||
try {
|
||||
if(create)Files.writeString(journal,data+"\n",StandardOpenOption.CREATE_NEW);
|
||||
else {
|
||||
var temp=journal.resolveSibling("palace-lab-173.json.tmp");Files.writeString(temp,data+"\n");
|
||||
Files.move(temp,journal,StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
}catch(java.io.IOException error){throw new IllegalStateException("Cannot save palace generation journal",error);}
|
||||
}
|
||||
|
||||
private static void enter(ServerPlayer p) {
|
||||
p.setGameMode(GameType.CREATIVE);
|
||||
p.teleportTo(owner.overworld(),.5,plan.origin.getY(),plan.radius-5.5,Set.of(),180,0,false);
|
||||
p.sendSystemMessage(Component.translatable("sanctuary.palace.welcome",plan.seed),false);
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,14 @@ public final class QuickTestMod implements ModInitializer {
|
||||
}
|
||||
public void onInitialize(){
|
||||
QuickTestWorld.enable();
|
||||
DuoLab.register();
|
||||
HordeLab.register();
|
||||
PalaceLab.register();
|
||||
WorldgenLab.register();
|
||||
PayloadTypeRegistry.clientboundConfiguration().register(Mode.TYPE,Mode.CODEC);
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler,server)->{
|
||||
if(ServerConfigurationNetworking.canSend(handler,Mode.TYPE))
|
||||
ServerConfigurationNetworking.send(handler,new Mode(QuickTestWorld.active(server)));
|
||||
ServerConfigurationNetworking.send(handler,new Mode((QuickTestWorld.active(server) || WorldgenLab.relief(server.overworld())) && !Boolean.getBoolean("sanctuary.duo.intro")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
|
||||
/** Surface height chooses the outdoor ecology; rock cover chooses the cave ecology first. */
|
||||
public final class ReliefBiomes177 extends BiomeSource {
|
||||
public static final MapCodec<ReliefBiomes177> CODEC=RecordCodecBuilder.mapCodec(i->i.group(
|
||||
Biome.CODEC.listOf().fieldOf("biomes").forGetter(s->s.biomes)
|
||||
).apply(i,ReliefBiomes177::new));
|
||||
private final List<Holder<Biome>> biomes;
|
||||
private final Map<String,Holder<Biome>> named;
|
||||
private record State(long seed,ReliefEcology177.Field field){}
|
||||
private volatile State state;
|
||||
public ReliefBiomes177(List<Holder<Biome>> biomes){
|
||||
this.biomes=List.copyOf(biomes);var names=new HashMap<String,Holder<Biome>>();
|
||||
for(var b:biomes)names.put(b.unwrapKey().orElseThrow().identifier().getPath(),b);
|
||||
named=Map.copyOf(names);
|
||||
for(var name:List.of("forest","flowers","autumn","cherry","jungle","swamp","bare","caves","void"))
|
||||
if(!named.containsKey("relief_"+name))throw new IllegalArgumentException("Missing relief biome "+name);
|
||||
}
|
||||
public void bind(long seed,ReliefEcology177.Field field){state=new State(seed,field);}
|
||||
@Override protected MapCodec<ReliefBiomes177> codec(){return CODEC;}
|
||||
@Override protected Stream<Holder<Biome>> collectPossibleBiomes(){return biomes.stream();}
|
||||
@Override public BiomeResolver createResolver(Climate.Sampler ignored){return (x,y,z)->resolve(x*4,y*4,z*4);}
|
||||
public Holder<Biome> resolve(int x,int y,int z){
|
||||
var s=state;if(s==null)throw new IllegalStateException("Unbound relief ecology");
|
||||
if(y<0 || y>=512 || Math.hypot(x,z)>398)return get("void");
|
||||
if(y>=320){
|
||||
int nearest=-1;double best=Double.POSITIVE_INFINITY;var reefs=s.field().base().reefs();
|
||||
for(int i=0;i<reefs.size();i++){
|
||||
var r=reefs.get(i);
|
||||
double d=Math.pow((x-r.x())/(double)(r.length()+20),2)+Math.pow((z-r.z())/(double)(r.length()+20),2)+Math.pow((y-r.y())/35.0,2);
|
||||
if(d<best){best=d;nearest=i;}
|
||||
}
|
||||
return get(switch(nearest){case 0->"jungle";case 1->"forest";case 2->"swamp";default->"bare";});
|
||||
}
|
||||
int top=s.field().surface(x,z);
|
||||
if(top<0)return get("void");
|
||||
if(y<top-12)return get("caves");
|
||||
double edge=3*EcologyBiomes176.noise(s.seed()^0x177B10L,x/55.0,z/55.0);
|
||||
if(top<237+edge)return get("autumn");
|
||||
if(top>264+edge)return get("cherry");
|
||||
return get(EcologyBiomes176.noise(s.seed()^0x177F10L,x/68.0,z/68.0)>.05?"flowers":"forest");
|
||||
}
|
||||
private Holder<Biome> get(String name){return named.get("relief_"+name);}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.google.gson.*;
|
||||
import fr.koka.sanctuary.atlas.AtlasSampler;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.*;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
/** Acceptance witnesses from native blocks, native biome climate and the actual atlas sampler. */
|
||||
final class ReliefChecks177 {
|
||||
private ReliefChecks177(){}
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map<String,Object> check(ServerLevel level,ReliefEcology177.Field field,ReliefBiomes177 source,
|
||||
Map<String,Object> data,boolean fresh,Path directory)throws java.io.IOException{
|
||||
var counts=(Map<String,Number>)((Map<?,?>)data.get("minerals")).get("counts");
|
||||
for(var id:List.of("snow","snow_block","ice","packed_ice","blue_ice","mangrove_log","mangrove_roots","muddy_mangrove_roots"))
|
||||
if(counts.getOrDefault(id,0).longValue()!=0)throw new IllegalStateException("Forbidden block: "+id);
|
||||
int warm=0;
|
||||
for(var name:List.of("forest","flowers","autumn","cherry","jungle","swamp","bare","caves","void")){
|
||||
var biome=level.registryAccess().lookupOrThrow(Registries.BIOME).getValue(Identifier.fromNamespaceAndPath("sanctuary_test","relief_"+name));
|
||||
for(int y:new int[]{100,300,511,639}){
|
||||
if(biome.coldEnoughToSnow(new BlockPos(0,y,0),level.getSeaLevel()))throw new IllegalStateException("Snow climate: "+name+" "+y);
|
||||
warm++;
|
||||
}
|
||||
}
|
||||
var surfaceCounts=new TreeMap<String,Integer>();
|
||||
for(var row:(List<List<Object>>)data.get("surfaceMap"))surfaceCounts.merge((String)row.get(3),1,Integer::sum);
|
||||
if(surfaceCounts.containsKey("relief_caves") || surfaceCounts.containsKey("relief_swamp"))throw new IllegalStateException("Cave or swamp on exterior map");
|
||||
for(var name:List.of("forest","flowers","autumn","cherry"))if(surfaceCounts.getOrDefault("relief_"+name,0)==0)throw new IllegalStateException("Missing surface ecology: "+name);
|
||||
int caveSamples=0;
|
||||
for(int x=-240;x<=240;x+=24)for(int z=-240;z<=240;z+=24){
|
||||
int roof=field.surface(x,z);if(roof<80)continue;
|
||||
if(!source.resolve(x,roof-24,z).unwrapKey().orElseThrow().identifier().getPath().equals("relief_caves"))throw new IllegalStateException("Autumn intruded below a roof");
|
||||
caveSamples++;
|
||||
}
|
||||
List<ReliefGroves177.Grove> groves=ReliefGroves177.groves(level);
|
||||
if(!fresh){
|
||||
var previous=JsonParser.parseString(Files.readString(directory.resolve("relief-ecology-v1-cold/survey.json"))).getAsJsonObject();
|
||||
groves=Arrays.asList(new Gson().fromJson(previous.getAsJsonObject("reliefChecks").get("caveGroves"),ReliefGroves177.Grove[].class));
|
||||
}
|
||||
var plants=new TreeMap<String,Integer>();
|
||||
for(var grove:groves){
|
||||
var chunk=level.getChunk(grove.x()>>4,grove.z()>>4);boolean found=false;
|
||||
for(int y=grove.y();y<grove.y()+12;y++){
|
||||
var id=BuiltInRegistries.BLOCK.getKey(chunk.getBlockState(new BlockPos(grove.x(),y,grove.z())).getBlock()).getPath();
|
||||
if(id.equals("dark_oak_log") || id.equals("mushroom_stem")){found=true;break;}
|
||||
}
|
||||
if(found)plants.merge(grove.feature(),1,Integer::sum);
|
||||
}
|
||||
fr.koka.sanctuary.SanctuaryMod.LOGGER.info("RELIEF_CAVE_GROVES recorded={} surviving={}",groves.size(),plants);
|
||||
if(Boolean.getBoolean("sanctuary.worldgenLab.wholeIsland"))for(var type:List.of("dark_oak","huge_brown_mushroom","huge_red_mushroom"))
|
||||
if(plants.getOrDefault(type,0)==0)throw new IllegalStateException("No covered native grove: "+type+"; recorded="+groves+"; surviving="+plants);
|
||||
var chunks=level.getChunkSource();int loaded=chunks.getLoadedChunksCount();
|
||||
var atlas=AtlasSampler.sample(level,0,0);int pixels=0;
|
||||
if(atlas==null)throw new IllegalStateException("Atlas returned no tile for loaded spawn chunk");
|
||||
for(byte color:atlas.colors())if(Byte.toUnsignedInt(color)>1)pixels++;
|
||||
if(pixels==0 || chunks.getLoadedChunksCount()!=loaded)throw new IllegalStateException("Atlas blank or loaded chunks");
|
||||
return Map.of("surfaceBiomeCounts",surfaceCounts,"coveredBiomeSamples",caveSamples,"warmClimateChecks",warm,
|
||||
"caveGroves",groves,"nativeCavePlants",plants,"atlasSpawnPixels",pixels,"atlasLoadedNewChunks",false,
|
||||
"peak",List.of(field.peakX(),field.surface(field.peakX(),field.peakZ()),field.peakZ()),"cachedQuartColumns",field.cachedColumns());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.util.Interval;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.*;
|
||||
|
||||
/** A local wooded summit; the deep island and the aerial field remain unchanged. */
|
||||
public record ReliefEcology177(DensityFunction base) implements DensityFunction {
|
||||
public static final MapCodec<ReliefEcology177> CODEC=RecordCodecBuilder.mapCodec(i->i.group(
|
||||
DensityFunction.CODEC.fieldOf("base").forGetter(ReliefEcology177::base)
|
||||
).apply(i,ReliefEcology177::new));
|
||||
@Override public DensitySampler compileSampler(CompileContext context){
|
||||
var baseField=(SkyFragments175.Field)DfRewriteRule.INLINE_REFERENCE.rewrite(base).compileSampler(context);
|
||||
var random=context.createRandom(Identifier.fromNamespaceAndPath("sanctuary_test","relief_ecology_v1"));
|
||||
int bestX=0,bestZ=0,bestY=-1;
|
||||
for(int i=0;i<12;i++){
|
||||
double angle=random.nextDouble()*Math.PI*2, distance=70+random.nextInt(90);
|
||||
int x=(int)(Math.cos(angle)*distance),z=(int)(Math.sin(angle)*distance);
|
||||
int y=top(baseField,x,z);
|
||||
if(y>bestY){bestX=x;bestZ=z;bestY=y;}
|
||||
}
|
||||
return new Field(baseField,bestX,bestZ);
|
||||
}
|
||||
private static int top(DensitySampler field,int x,int z){
|
||||
for(int y=316;y>=48;y-=4)if(field.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0){
|
||||
for(int fine=y+3;fine>=y;fine--)if(field.sampleValue(SamplerContext.EMPTY_UNCACHED,x,fine,z)>0)return fine;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
public static final class Field implements DensitySampler {
|
||||
private final SkyFragments175.Field base;
|
||||
private final int peakX,peakZ;
|
||||
// Quart columns are shared by all biome heights. Fixed island bounds limit this cache.
|
||||
private final ConcurrentHashMap<Long,Integer> tops=new ConcurrentHashMap<>();
|
||||
Field(SkyFragments175.Field base,int x,int z){this.base=base;peakX=x;peakZ=z;}
|
||||
public SkyFragments175.Field base(){return base;}
|
||||
public int peakX(){return peakX;}public int peakZ(){return peakZ;}
|
||||
public int cachedColumns(){return tops.size();}
|
||||
public int surface(int x,int z){
|
||||
x=x>>2<<2;z=z>>2<<2;
|
||||
if(Math.hypot(x,z)>398)return -1;
|
||||
final int sx=x,sz=z;
|
||||
return tops.computeIfAbsent(((long)x<<32)^(z&0xffffffffL),ignored->top(this,sx,sz));
|
||||
}
|
||||
@Override public float sampleValue(SamplerContext context,int x,int y,int z){
|
||||
if(y<=216 || y>=320)return base.sampleValue(context,x,y,z);
|
||||
double radius=((x-peakX)*(double)(x-peakX)+(z-peakZ)*(double)(z-peakZ))/(96*96.0);
|
||||
if(radius>=1)return base.sampleValue(context,x,y,z);
|
||||
double lift=34*(1-radius)*(1-radius);
|
||||
double source=216+(y-216)/(1+lift/56);
|
||||
int low=(int)Math.floor(source);double blend=source-low;
|
||||
double density=base.sampleValue(context,x,low,z)*(1-blend)+base.sampleValue(context,x,low+1,z)*blend;
|
||||
return (float)Math.min(density,(316-y)*.15);
|
||||
}
|
||||
@Override public void sampleVolume(SamplerContext c,DensityBuffer b,DensityVolume v){DensitySampler.sampleVolumeNaive(c,b,v,this);}
|
||||
}
|
||||
@Override public DensityFunction rewriteChildren(DfRewriteRule rule){return new ReliefEcology177(rule.rewrite(base));}
|
||||
@Override public Interval range(){return Interval.of(-1,1);}
|
||||
@Override public int domainAxes(){return ALL_AXES;}
|
||||
@Override public MapCodec<ReliefEcology177> codec(){return CODEC;}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** Native dark oaks and giant mushrooms in pre-existing covered cavities. Never excavates a room. */
|
||||
public final class ReliefGroves177 {
|
||||
public record Grove(int x,int y,int z,String feature){}
|
||||
private static final Map<ServerLevel,Map<Long,Grove>> GROVES=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
public static List<Grove> groves(ServerLevel level){synchronized(GROVES){return List.copyOf(GROVES.getOrDefault(level,Map.of()).values());}}
|
||||
private ReliefGroves177(){}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
boolean rocky=WorldgenLab.ecology178(generator);
|
||||
if(!WorldgenLab.ecology177(generator) && !rocky)return;
|
||||
var random=RandomSource.create(level.getSeed()^chunk.getPos().pack()^0x177CA7EL);
|
||||
if(random.nextInt(2)!=0)return;
|
||||
var source=((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
int x=chunk.getPos().getMinBlockX()+7,z=chunk.getPos().getMinBlockZ()+7;
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(int y=238;y>=70;y--){
|
||||
if(!chunk.getBlockState(pos.set(x,y,z)).isSolidRender() || !chunk.getBlockState(pos.set(x,y+1,z)).isAir())continue;
|
||||
String biome=(rocky?((RockyBiomes178)source).resolve(x,y+1,z):((ReliefBiomes177)source).resolve(x,y+1,z)).unwrapKey().orElseThrow().identifier().getPath();
|
||||
if(!biome.equals("relief_caves") && !biome.equals("rocky_caves") && !biome.equals("rocky_grove"))continue;
|
||||
boolean roof=false;
|
||||
for(int dy=13;dy<=64 && y+dy<320;dy++)if(chunk.getBlockState(pos.set(x,y+dy,z)).isSolidRender()){roof=true;break;}
|
||||
if(!roof)continue;
|
||||
boolean room=true;
|
||||
for(int dx=-3;room&&dx<=4;dx++)for(int dz=-3;room&&dz<=4;dz++)for(int dy=1;dy<=10;dy++)
|
||||
if(!chunk.getBlockState(pos.set(x+dx,y+dy,z+dz)).isAir()){room=false;break;}
|
||||
if(!room)continue;
|
||||
// Only the trunk needs a level foundation; cave floors are naturally uneven.
|
||||
for(int dx=0;room&&dx<=1;dx++)for(int dz=0;dz<=1;dz++)
|
||||
if(!chunk.getBlockState(pos.set(x+dx,y,z+dz)).isSolidRender()){room=false;break;}
|
||||
if(!room)continue;
|
||||
String id=switch(random.nextInt(3)){case 0->"dark_oak";case 1->"huge_brown_mushroom";default->"huge_red_mushroom";};
|
||||
for(int dx=-2;dx<=3;dx++)for(int dz=-2;dz<=3;dz++)
|
||||
if(chunk.getBlockState(pos.set(x+dx,y,z+dz)).isSolidRender())chunk.setBlockState(pos,Blocks.MOSS_BLOCK.defaultBlockState());
|
||||
// Native placement performs its own trunk/canopy collision checks as well.
|
||||
var feature=level.registryAccess().lookupOrThrow(Registries.FEATURE).getValue(Identifier.withDefaultNamespace(id));
|
||||
if(feature.place(level,generator,random,new BlockPos(x,y+1,z)) && Boolean.getBoolean("sanctuary.worldgenLab.verify"))synchronized(GROVES){
|
||||
GROVES.computeIfAbsent(level.getLevel(),ignored->new ConcurrentHashMap<>()).put(chunk.getPos().pack(),new Grove(x,y+1,z,id));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.material.MaterialRuleContext;
|
||||
import net.minecraft.world.level.levelgen.material.rule.*;
|
||||
|
||||
/** Keep the deep beds, soften the repetition with broad three-dimensional rock deposits. */
|
||||
public record ReliefStone177() implements MaterialRule {
|
||||
public static final MapCodec<ReliefStone177> CODEC=MapCodec.unit(ReliefStone177::new);
|
||||
private static double volume(long seed,int x,int y,int z,double size){
|
||||
int layer=(int)Math.floor(y/size);double v=y/size-layer;v=v*v*(3-2*v);
|
||||
double a=EcologyBiomes176.noise(seed+layer*7919L,x/size,z/size);
|
||||
double b=EcologyBiomes176.noise(seed+(layer+1)*7919L,x/size,z/size);
|
||||
return a+(b-a)*v;
|
||||
}
|
||||
@Override public RuleEvaluator compile(MaterialRuleContext context){
|
||||
long seed=context.getOrCreateRandomFactory(Identifier.fromNamespaceAndPath("sanctuary_test","ecology_strata_v1")).at(0,0,0).nextLong();
|
||||
return new RuleEvaluator(){
|
||||
int lastX=Integer.MIN_VALUE,lastZ=Integer.MIN_VALUE;double warp;
|
||||
@Override public BlockState tryApply(int x,int y,int z){
|
||||
if(x!=lastX || z!=lastZ){lastX=x;lastZ=z;warp=EcologyStrata176.warp(seed,x,z);}
|
||||
String biome=context.getBiome().unwrapKey().orElseThrow().identifier().getPath();
|
||||
if(y>=48 && context.stoneDepthAbove()<=3){
|
||||
if(biome.equals("relief_caves"))return (context.stoneDepthAbove()==1?Blocks.MOSS_BLOCK:Blocks.DIRT).defaultBlockState();
|
||||
return (context.stoneDepthAbove()==1?Blocks.GRASS_BLOCK:Blocks.DIRT).defaultBlockState();
|
||||
}
|
||||
double depth=y+warp;
|
||||
var bed=EcologyStrata176.rock(depth,y);
|
||||
// Leave the deep diamond-bearing deepslate exactly as before.
|
||||
if(depth<174)return bed;
|
||||
double deposit=volume(seed^0x177DEFL,x,y,z,32);
|
||||
if(deposit>.32)return Blocks.ANDESITE.defaultBlockState();
|
||||
if(deposit<-.38)return (volume(seed^0x1776AAL,x,y,z,23)>0?Blocks.GRANITE:Blocks.DIORITE).defaultBlockState();
|
||||
// Some districts retain strata; others have larger plain stone faces.
|
||||
if(volume(seed^0x177BEDL,x,y,z,64)<.12)return Blocks.STONE.defaultBlockState();
|
||||
return bed;
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override public MapCodec<ReliefStone177> codec(){return CODEC;}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.world.level.biome.*;
|
||||
|
||||
/** Bounded valley floors, exposed rock faces and sparse vegetated cave districts. */
|
||||
public final class RockyBiomes178 extends BiomeSource {
|
||||
public static final int AUTUMN_MIN=200, AUTUMN_MAX=232;
|
||||
public static final MapCodec<RockyBiomes178> CODEC=RecordCodecBuilder.mapCodec(i->i.group(
|
||||
Biome.CODEC.listOf().fieldOf("biomes").forGetter(s->s.biomes)
|
||||
).apply(i,RockyBiomes178::new));
|
||||
public record Terrain(int top,boolean cliff,boolean valley,int lowestNeighbour,int higherNeighbours){}
|
||||
private record State(long seed,ReliefEcology177.Field field,Map<Long,Terrain> terrain){}
|
||||
private final List<Holder<Biome>> biomes;
|
||||
private final Map<String,Holder<Biome>> named;
|
||||
private volatile State state;
|
||||
private static final int[][] DIRECTIONS={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
|
||||
public RockyBiomes178(List<Holder<Biome>> biomes){
|
||||
this.biomes=List.copyOf(biomes);var names=new HashMap<String,Holder<Biome>>();
|
||||
for(var b:biomes)names.put(b.unwrapKey().orElseThrow().identifier().getPath(),b);
|
||||
named=Map.copyOf(names);
|
||||
for(var n:List.of("forest","flowers","autumn","cherry","jungle","swamp","bare","cliffs","caves","grove","void"))
|
||||
if(!named.containsKey("rocky_"+n))throw new IllegalArgumentException("Missing rocky biome "+n);
|
||||
}
|
||||
public void bind(long seed,ReliefEcology177.Field field){state=new State(seed,field,new ConcurrentHashMap<>());}
|
||||
@Override protected MapCodec<RockyBiomes178> codec(){return CODEC;}
|
||||
@Override protected Stream<Holder<Biome>> collectPossibleBiomes(){return biomes.stream();}
|
||||
@Override public BiomeResolver createResolver(Climate.Sampler ignored){return (x,y,z)->resolve(x*4,y*4,z*4);}
|
||||
public Terrain terrain(int x,int z){
|
||||
var s=state;if(s==null)throw new IllegalStateException("Unbound rocky ecology");
|
||||
final int qx=x>>2<<2,qz=z>>2<<2;
|
||||
if(Math.hypot(qx,qz)>398)return new Terrain(-1,true,false,-1,0);
|
||||
return s.terrain().computeIfAbsent(((long)qx<<32)^(qz&0xffffffffL),ignored->{
|
||||
int top=s.field().surface(qx,qz),lowest=320,higher=0,highest=-1;boolean steep=false;
|
||||
for(var d:DIRECTIONS){
|
||||
int near=s.field().surface(qx+d[0]*8,qz+d[1]*8);
|
||||
if(near<top-12)steep=true;
|
||||
int far=s.field().surface(qx+d[0]*32,qz+d[1]*32);
|
||||
lowest=Math.min(lowest,far);highest=Math.max(highest,far);
|
||||
if(far>=top+5)higher++;
|
||||
}
|
||||
boolean cliff=top<AUTUMN_MIN || steep;
|
||||
boolean valley=!cliff && top<=AUTUMN_MAX && lowest>=top-20 && higher>=3 && highest>=top+10;
|
||||
return new Terrain(top,cliff,valley,lowest,higher);
|
||||
});
|
||||
}
|
||||
public static double moisture(long seed,int x,int y,int z){
|
||||
double h=y/48.0;int layer=(int)Math.floor(h);double t=h-layer;t=t*t*(3-2*t);
|
||||
double a=EcologyBiomes176.noise((seed^0x178CA7EL)+layer*7919L,x/64.0,z/64.0);
|
||||
double b=EcologyBiomes176.noise((seed^0x178CA7EL)+(layer+1)*7919L,x/64.0,z/64.0);
|
||||
return a+(b-a)*t;
|
||||
}
|
||||
public Holder<Biome> resolve(int x,int y,int z){
|
||||
var s=state;if(s==null)throw new IllegalStateException("Unbound rocky ecology");
|
||||
if(y<0 || y>=512 || Math.hypot(x,z)>398)return get("void");
|
||||
if(y>=320){
|
||||
int nearest=-1;double best=Double.POSITIVE_INFINITY;var reefs=s.field().base().reefs();
|
||||
for(int i=0;i<reefs.size();i++){
|
||||
var r=reefs.get(i);double d=Math.pow((x-r.x())/(double)(r.length()+20),2)+Math.pow((z-r.z())/(double)(r.length()+20),2)+Math.pow((y-r.y())/35.0,2);
|
||||
if(d<best){best=d;nearest=i;}
|
||||
}
|
||||
return get(switch(nearest){case 0->"jungle";case 1->"forest";case 2->"swamp";default->"bare";});
|
||||
}
|
||||
var t=terrain(x,z);if(t.top()<0)return get("void");
|
||||
if(y<t.top()-12)return get(moisture(s.seed(),x,y,z)>.38?"grove":"caves");
|
||||
if(t.cliff())return get("cliffs");
|
||||
if(t.valley())return get("autumn");
|
||||
double edge=3*EcologyBiomes176.noise(s.seed()^0x177B10L,x/55.0,z/55.0);
|
||||
if(t.top()>264+edge)return get("cherry");
|
||||
return get(EcologyBiomes176.noise(s.seed()^0x177F10L,x/68.0,z/68.0)>.05?"flowers":"forest");
|
||||
}
|
||||
private Holder<Biome> get(String name){return named.get("rocky_"+name);}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** Small launch acceptance check; no whole-island census or generated atlas. */
|
||||
final class RockyChecks178 {
|
||||
private RockyChecks178(){}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var source=(RockyBiomes178)((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).getBiomeSource();
|
||||
int autumn=0,cliffs=0,caves=0,groves=0;
|
||||
for(int x=-216;x<=216;x+=24)for(int z=-216;z<=216;z+=24){
|
||||
var t=source.terrain(x,z);if(t.top()<0)continue;
|
||||
String biome=source.resolve(x,t.top(),z).unwrapKey().orElseThrow().identifier().getPath();
|
||||
if(biome.equals("rocky_autumn")){
|
||||
if(t.top()<200 || t.top()>232 || t.lowestNeighbour()<t.top()-20 || t.higherNeighbours()<3 || t.cliff())throw new IllegalStateException("Autumn outside a bounded valley");
|
||||
autumn++;
|
||||
}
|
||||
if(t.top()<200 && !biome.equals("rocky_cliffs"))throw new IllegalStateException("Vegetated deep exterior");
|
||||
if(biome.equals("rocky_cliffs"))cliffs++;
|
||||
for(int y=96;y<t.top()-12;y+=24){
|
||||
String interior=source.resolve(x,y,z).unwrapKey().orElseThrow().identifier().getPath();
|
||||
if(interior.equals("rocky_grove"))groves++;
|
||||
else if(interior.equals("rocky_caves"))caves++;
|
||||
else throw new IllegalStateException("Outdoor biome inside rock cover");
|
||||
}
|
||||
}
|
||||
if(autumn==0 || cliffs==0 || caves<=groves*3)throw new IllegalStateException("Ecology lost its mineral majority");
|
||||
Set<String> rock=Set.of("stone","andesite","diorite","granite","tuff","deepslate","calcite");
|
||||
int floors=0,mineral=0,moss=0,water=0;var pos=new BlockPos.MutableBlockPos();
|
||||
for(int cz=-1;cz<=1;cz++)for(int cx=-1;cx<=1;cx++){
|
||||
var chunk=level.getChunk(cx,cz);
|
||||
for(int x=cx*16+2;x<cx*16+14;x+=2)for(int z=cz*16+2;z<cz*16+14;z+=2)for(int y=64;y<source.terrain(x,z).top()-12;y++){
|
||||
var state=chunk.getBlockState(pos.set(x,y,z));
|
||||
if(state.getFluidState().isSource())water++;
|
||||
if(!state.isSolidRender() || chunk.getBlockState(pos.set(x,y+1,z)).isSolidRender())continue;
|
||||
String id=BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath();
|
||||
if(rock.contains(id) || id.equals("moss_block") || id.equals("dirt") || id.equals("clay")){
|
||||
floors++;if(rock.contains(id))mineral++;if(id.equals("moss_block"))moss++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(floors<20 || mineral<.65*floors || moss>.30*floors || water==0)
|
||||
throw new IllegalStateException("Cave launch check: floors="+floors+" mineral="+mineral+" moss="+moss+" water="+water);
|
||||
return Map.of("autumnColumns",autumn,"cliffColumns",cliffs,"mineralCaveBiomeSamples",caves,"groveBiomeSamples",groves,
|
||||
"checkedCaveFloors",floors,"mineralFloors",mineral,"mossFloors",moss,"sampledWaterSources",water);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.levelgen.material.MaterialRuleContext;
|
||||
import net.minecraft.world.level.levelgen.material.rule.*;
|
||||
|
||||
/** Expose the actual geological beds; moss is a thin local patch, never a universal cave soil. */
|
||||
public record RockyStone178() implements MaterialRule {
|
||||
public static final MapCodec<RockyStone178> CODEC=MapCodec.unit(RockyStone178::new);
|
||||
private static double volume(long seed,int x,int y,int z,double size){
|
||||
int layer=(int)Math.floor(y/size);double v=y/size-layer;v=v*v*(3-2*v);
|
||||
double a=EcologyBiomes176.noise(seed+layer*7919L,x/size,z/size);
|
||||
return a+(EcologyBiomes176.noise(seed+(layer+1)*7919L,x/size,z/size)-a)*v;
|
||||
}
|
||||
@Override public RuleEvaluator compile(MaterialRuleContext context){
|
||||
long seed=context.getOrCreateRandomFactory(Identifier.fromNamespaceAndPath("sanctuary_test","ecology_strata_v1")).at(0,0,0).nextLong();
|
||||
return new RuleEvaluator(){
|
||||
int lastX=Integer.MIN_VALUE,lastZ=Integer.MIN_VALUE;double warp;
|
||||
@Override public BlockState tryApply(int x,int y,int z){
|
||||
if(x!=lastX || z!=lastZ){lastX=x;lastZ=z;warp=EcologyStrata176.warp(seed,x,z);}
|
||||
String biome=context.getBiome().unwrapKey().orElseThrow().identifier().getPath();
|
||||
boolean cave=biome.equals("rocky_caves") || biome.equals("rocky_grove");
|
||||
if(y>=48 && context.stoneDepthAbove()<=3){
|
||||
if(cave){
|
||||
double patch=volume(seed^0x178A055L,x,y,z,8);
|
||||
if(context.stoneDepthAbove()==1 && patch>(biome.equals("rocky_grove")?.55:.72))return Blocks.MOSS_BLOCK.defaultBlockState();
|
||||
}else if(!biome.equals("rocky_cliffs") && !biome.equals("rocky_void"))
|
||||
return (context.stoneDepthAbove()==1?Blocks.GRASS_BLOCK:Blocks.DIRT).defaultBlockState();
|
||||
}
|
||||
double depth=y+warp;var bed=EcologyStrata176.rock(depth,y);
|
||||
if(depth<174)return bed;
|
||||
double deposit=volume(seed^0x177DEFL,x,y,z,32);
|
||||
if(deposit>.32)return Blocks.ANDESITE.defaultBlockState();
|
||||
if(deposit<-.38)return (volume(seed^0x1776AAL,x,y,z,23)>0?Blocks.GRANITE:Blocks.DIORITE).defaultBlockState();
|
||||
if(volume(seed^0x177BEDL,x,y,z,64)<.12)return Blocks.STONE.defaultBlockState();
|
||||
return bed;
|
||||
}
|
||||
};
|
||||
}
|
||||
@Override public MapCodec<RockyStone178> codec(){return CODEC;}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** Tiny retained cave pools: one shallow pool per chunk at most, no regional planning. */
|
||||
public final class RockyWater178 {
|
||||
private RockyWater178(){}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.ecology178(generator))return;
|
||||
var source=(RockyBiomes178)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var random=RandomSource.create(level.getSeed()^chunk.getPos().pack()^0x178DAB1L);
|
||||
if(random.nextInt(2)!=0)return;
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(int attempt=0;attempt<3;attempt++){
|
||||
int x=chunk.getPos().getMinBlockX()+5+random.nextInt(6),z=chunk.getPos().getMinBlockZ()+5+random.nextInt(6);
|
||||
int radius=1+random.nextInt(2);
|
||||
for(int y=232;y>=64;y--){
|
||||
if(!solid(chunk.getBlockState(pos.set(x,y,z))) || !chunk.getBlockState(pos.set(x,y+1,z)).isAir())continue;
|
||||
if(y>=source.terrain(x,z).top()-12)continue;
|
||||
boolean covered=false;
|
||||
for(int dy=5;dy<=64 && y+dy<320;dy++)if(solid(chunk.getBlockState(pos.set(x,y+dy,z)))){covered=true;break;}
|
||||
if(!covered)continue;
|
||||
if(!basin(chunk,x,y,z,radius))continue;
|
||||
for(int dx=-radius;dx<=radius;dx++)for(int dz=-radius;dz<=radius;dz++)if(inside(dx,dz,radius)){
|
||||
chunk.setBlockState(pos.set(x+dx,y-1,z+dz),Blocks.CLAY.defaultBlockState());
|
||||
chunk.setBlockState(pos.set(x+dx,y,z+dz),Blocks.WATER.defaultBlockState());
|
||||
chunk.markPosForPostProcessing(pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
private static boolean inside(int x,int z,int r){return x*x+z*z<=r*r;}
|
||||
private static boolean solid(BlockState s){return s.isSolidRender() && s.getFluidState().isEmpty() && !s.hasBlockEntity()
|
||||
&& !s.is(net.minecraft.tags.BlockTags.LOGS) && !s.is(net.minecraft.tags.BlockTags.LEAVES);}
|
||||
private static boolean basin(ChunkAccess chunk,int x,int y,int z,int r){
|
||||
for(int dx=-r;dx<=r;dx++)for(int dz=-r;dz<=r;dz++)if(inside(dx,dz,r)){
|
||||
for(int dy=-2;dy<=0;dy++)if(!solid(chunk.getBlockState(new BlockPos(x+dx,y+dy,z+dz))))return false;
|
||||
if(!chunk.getBlockState(new BlockPos(x+dx,y+1,z+dz)).isAir())return false;
|
||||
for(var d:Direction.Plane.HORIZONTAL){
|
||||
int nx=dx+d.getStepX(),nz=dz+d.getStepZ();
|
||||
if(!inside(nx,nz,r) && !solid(chunk.getBlockState(new BlockPos(x+nx,y,z+nz))))return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.*;
|
||||
import fr.koka.sanctuary.worldgen.PopulationIslandDensity;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.util.Interval;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.*;
|
||||
|
||||
/** Lab-only v1 field: the accepted main island, replacement noisy reefs, then empty ISS airspace. */
|
||||
public record SkyFragments175(DensityFunction ground, DensityFunction coarse, DensityFunction detail)
|
||||
implements DensityFunction {
|
||||
public static final MapCodec<SkyFragments175> CODEC = RecordCodecBuilder.mapCodec(i -> i.group(
|
||||
DensityFunction.CODEC.fieldOf("ground").forGetter(SkyFragments175::ground),
|
||||
DensityFunction.CODEC.fieldOf("coarse").forGetter(SkyFragments175::coarse),
|
||||
DensityFunction.CODEC.fieldOf("detail").forGetter(SkyFragments175::detail)
|
||||
).apply(i, SkyFragments175::new));
|
||||
public static final int BASE = 320, CEILING = 512;
|
||||
|
||||
public record Reef(int x, int y, int z, int length, int width, int depth, int rise, double angle) {}
|
||||
|
||||
@Override public DensitySampler compileSampler(CompileContext context) {
|
||||
var random = context.createRandom(Identifier.fromNamespaceAndPath("sanctuary_test", "sky_v1"));
|
||||
var reefs = new ArrayList<Reef>();
|
||||
double phase = random.nextDouble() * Math.PI * 2;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
boolean high = i >= 3;
|
||||
double bearing = phase + (high ? (i - 3) * Math.PI + .8 : i * Math.PI * 2 / 3)
|
||||
+ (random.nextDouble() - .5) * .35;
|
||||
double distance = high ? 175 + random.nextInt(85) : 170 + random.nextInt(75);
|
||||
reefs.add(new Reef((int)Math.round(Math.cos(bearing) * distance),
|
||||
high ? 492 + random.nextInt(8) : 362 + i * 40 + random.nextInt(8),
|
||||
(int)Math.round(Math.sin(bearing) * distance),
|
||||
high ? 14 + random.nextInt(8) : 46 + random.nextInt(17),
|
||||
high ? 10 + random.nextInt(6) : 26 + random.nextInt(11),
|
||||
high ? 8 + random.nextInt(4) : 15 + random.nextInt(6),
|
||||
high ? 3 : 5, bearing + .5 + random.nextDouble()));
|
||||
}
|
||||
var island = DfRewriteRule.INLINE_REFERENCE.rewrite(ground);
|
||||
if (!(island instanceof PopulationIslandDensity population))
|
||||
throw new IllegalStateException("Sky v1 requires the accepted population island field");
|
||||
return new Field(population.compileWithoutUpperBodies(context), coarse.compileSampler(context),
|
||||
detail.compileSampler(context), List.copyOf(reefs));
|
||||
}
|
||||
|
||||
public static final class Field implements DensitySampler {
|
||||
private final DensitySampler ground, coarse, detail;
|
||||
private final List<Reef> reefs;
|
||||
private final double[] cos, sin;
|
||||
Field(DensitySampler ground, DensitySampler coarse, DensitySampler detail, List<Reef> reefs) {
|
||||
this.ground=ground; this.coarse=coarse; this.detail=detail; this.reefs=reefs;
|
||||
cos=new double[reefs.size()]; sin=new double[reefs.size()];
|
||||
for (int i=0;i<reefs.size();i++) {cos[i]=Math.cos(reefs.get(i).angle());sin[i]=Math.sin(reefs.get(i).angle());}
|
||||
}
|
||||
public List<Reef> reefs() { return reefs; }
|
||||
|
||||
@Override public float sampleValue(SamplerContext sample, int x, int y, int z) {
|
||||
// No upper noise, altered interpolation or extra ground probe in the accepted island.
|
||||
if (y < BASE) return ground.sampleValue(sample,x,y,z);
|
||||
if (y >= CEILING) return -1;
|
||||
float value=-1;
|
||||
for (int i=0;i<reefs.size();i++) {
|
||||
var reef=reefs.get(i);
|
||||
if (y < reef.y()-reef.depth()-4 || y > reef.y()+reef.rise()+2) continue;
|
||||
double dx=(double)x-reef.x(), dz=(double)z-reef.z();
|
||||
double u=(dx*cos[i]+dz*sin[i])/reef.length(), v=(-dx*sin[i]+dz*cos[i])/reef.width();
|
||||
double radial=u*u+v*v;
|
||||
if (radial > 1.7) continue;
|
||||
// Only two native 3D noise evaluations inside these small analytic envelopes.
|
||||
double n=Math.clamp(2*coarse.sampleValue(sample,x,y,z),-1,1);
|
||||
double d=Math.clamp(2*detail.sampleValue(sample,x,y,z),-1,1);
|
||||
double edge=1-radial+.48*n+.16*d;
|
||||
double top=reef.y()+reef.rise()*(.7*n+.3*d);
|
||||
double bottom=reef.y()-reef.depth()*Math.sqrt(Math.max(0,Math.min(1.25,edge)));
|
||||
double body=Math.min(edge*.4, Math.min((top-y)*.12,(y-bottom)*.12));
|
||||
value=Math.max(value,(float)Math.clamp(body,-1,1));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@Override public void sampleVolume(SamplerContext sample, DensityBuffer buffer, DensityVolume volume) {
|
||||
DensitySampler.sampleVolumeNaive(sample,buffer,volume,this);
|
||||
}
|
||||
}
|
||||
@Override public DensityFunction rewriteChildren(DfRewriteRule rule) {
|
||||
return new SkyFragments175(rule.rewrite(ground),rule.rewrite(coarse),rule.rewrite(detail));
|
||||
}
|
||||
@Override public Interval range() { return Interval.of(-1,1); }
|
||||
@Override public int domainAxes() { return ALL_AXES; }
|
||||
@Override public MapCodec<SkyFragments175> codec() { return CODEC; }
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import fr.koka.sanctuary.worldgen.PopulationHydrologyRuntime;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.*;
|
||||
|
||||
/** Measured native density, not an independently reimplemented approximation of the generator. */
|
||||
final class SkySurvey175 {
|
||||
private static final int WIDTH=192, STEP=4, START=-382, Y_START=302, LAYERS=85;
|
||||
private SkySurvey175() {}
|
||||
|
||||
static Map<String,Object> measure(ServerLevel level, Path directory, RandomState reference, NoiseGeneratorSettings old, boolean fresh) {
|
||||
long started=System.nanoTime();
|
||||
var generator=(NoiseBasedChunkGenerator)level.getChunkSource().getGenerator();
|
||||
var random=level.getChunkSource().randomState();
|
||||
var current=random.getSampler(generator.generatorSettings().value().noiseRouter().finalDensity());
|
||||
if (!(current instanceof SkyFragments175.Field field)) throw new IllegalStateException("Missing native sky field");
|
||||
var before=reference.getSampler(old.noiseRouter().finalDensity());
|
||||
var context=SamplerContext.EMPTY_UNCACHED;
|
||||
var data=new LinkedHashMap<String,Object>();
|
||||
data.put("schema",1);data.put("seed",level.getSeed());data.put("preset","sanctuary_test:sky_v1");
|
||||
data.put("minecraft","26.3");data.put("version",FabricLoader.getInstance().getModContainer("sanctuary_test").orElseThrow().getMetadata().getVersion().getFriendlyString());
|
||||
data.put("radius",362);data.put("xStart",START);data.put("zStart",START);data.put("width",WIDTH);
|
||||
data.put("step",STEP);data.put("yStart",Y_START);data.put("layers",LAYERS);
|
||||
data.put("layout","uint8 occupancy, Y then Z then X; density > 0; sampled block coordinates, not full voxels");
|
||||
data.put("reefs",field.reefs());
|
||||
byte[] previous=new byte[WIDTH*WIDTH*LAYERS], next=new byte[previous.length];
|
||||
int samples=0, occupied=0, reserved=0, removed=0, added=0;
|
||||
for (int iy=0;iy<LAYERS;iy++) for (int iz=0;iz<WIDTH;iz++) for (int ix=0;ix<WIDTH;ix++) {
|
||||
int x=START+ix*STEP, y=Y_START+iy*STEP, z=START+iz*STEP;
|
||||
int index=(iy*WIDTH+iz)*WIDTH+ix;
|
||||
previous[index]=(byte)(before.sampleValue(context,x,y,z)>0?1:0);
|
||||
next[index]=(byte)(field.sampleValue(context,x,y,z)>0?1:0);
|
||||
if (y>=512) {
|
||||
if(next[index]!=0) throw new IllegalStateException("ISS airspace occupied");
|
||||
reserved++;
|
||||
}
|
||||
if(y>=320 && y<512 && Math.hypot(x,z)<=362) {samples++;occupied+=next[index];}
|
||||
if (previous[index]==1 && next[index]==0) removed++;
|
||||
if (previous[index]==0 && next[index]==1) added++;
|
||||
}
|
||||
if(occupied==0 || occupied/(double)samples>.05 || removed==0 || added==0)
|
||||
throw new IllegalStateException("Sky field must replace the old bodies and remain sparse");
|
||||
// Reverse order proves sampling history cannot change the field at these coordinates.
|
||||
for(int index=next.length-1;index>=0;index-=97) {
|
||||
int ix=index%WIDTH, iz=(index/WIDTH)%WIDTH, iy=index/(WIDTH*WIDTH);
|
||||
int value=field.sampleValue(context,START+ix*STEP,Y_START+iy*STEP,START+iz*STEP)>0?1:0;
|
||||
if(value!=next[index]) throw new IllegalStateException("Order-dependent sky density");
|
||||
}
|
||||
data.put("upperSolidFraction",occupied/(double)samples);
|
||||
data.put("upperSolidSamples",occupied);data.put("upperSamples",samples);
|
||||
data.put("reservedAirSamples",reserved);data.put("removedSamples",removed);data.put("addedSamples",added);
|
||||
data.put("densitySurveyMs",(System.nanoTime()-started)/1_000_000);
|
||||
try {
|
||||
Path out=directory.resolve(fresh ? "sky-survey-v1-cold" : "sky-survey-v1-warm");Files.createDirectories(out);
|
||||
Files.write(out.resolve("before.bin"),previous);Files.write(out.resolve("after.bin"),next);
|
||||
var sections=new ArrayList<Map<String,Object>>();
|
||||
for(int s=0;s<2;s++) {
|
||||
int z=field.reefs().get(s).z();
|
||||
byte[] a=new byte[WIDTH*320], b=new byte[a.length];
|
||||
int identical=0;
|
||||
for(int iy=0;iy<320;iy++) for(int ix=0;ix<WIDTH;ix++) {
|
||||
int x=START+ix*STEP, y=1+iy*2, index=iy*WIDTH+ix;
|
||||
float oldValue=before.sampleValue(context,x,y,z), newValue=field.sampleValue(context,x,y,z);
|
||||
if(y<300) {
|
||||
if(Float.floatToIntBits(oldValue)!=Float.floatToIntBits(newValue))
|
||||
throw new IllegalStateException("Main island changed at "+new BlockPos(x,y,z));
|
||||
identical++;
|
||||
}
|
||||
a[index]=(byte)(oldValue>0?1:0);b[index]=(byte)(newValue>0?1:0);
|
||||
}
|
||||
Files.write(out.resolve("section-"+s+"-before.bin"),a);
|
||||
Files.write(out.resolve("section-"+s+"-after.bin"),b);
|
||||
sections.add(Map.of("z",z,"width",WIDTH,"height",320,"yStart",1,"yStep",2,
|
||||
"identicalLowerSamples",identical,"before","section-"+s+"-before.bin","after","section-"+s+"-after.bin"));
|
||||
}
|
||||
data.put("sections",sections);
|
||||
long chunksStarted=System.nanoTime();
|
||||
var witnesses=new ArrayList<Map<String,Object>>();
|
||||
// Full chunks, not just density: each new body has actual stone and every selected
|
||||
// column in its central chunk is empty from the reserved floor through world ceiling.
|
||||
for(var reef:field.reefs()) {
|
||||
var chunk=level.getChunk(reef.x()>>4,reef.z()>>4);
|
||||
int solid=0, skyAir=0;
|
||||
for(int y=SkyFragments175.BASE;y<512;y++) if(!chunk.getBlockState(new BlockPos(reef.x(),y,reef.z())).isAir()) solid++;
|
||||
if(solid==0) throw new IllegalStateException("Missing generated reef at "+reef);
|
||||
for(int z=chunk.getPos().getMinBlockZ();z<=chunk.getPos().getMaxBlockZ();z++)
|
||||
for(int x=chunk.getPos().getMinBlockX();x<=chunk.getPos().getMaxBlockX();x++)
|
||||
for(int y=512;y<640;y++) {
|
||||
if(!chunk.getBlockState(new BlockPos(x,y,z)).isAir()) throw new IllegalStateException("Generated block in ISS airspace");
|
||||
skyAir++;
|
||||
}
|
||||
witnesses.add(Map.of("x",reef.x(),"z",reef.z(),"columnSolidBlocks",solid,"reservedAirBlocks",skyAir));
|
||||
}
|
||||
data.put("chunkWitnessMs",(System.nanoTime()-chunksStarted)/1_000_000);data.put("chunkWitnesses",witnesses);
|
||||
data.put("minerals", Boolean.getBoolean("sanctuary.worldgenLab.wholeIsland") ? IslandMineralSurvey175.measure(level) : minerals(level));
|
||||
if(PopulationHydrologyRuntime.cachedRegionCount(random)!=0) throw new IllegalStateException("Survey initialized hydrology");
|
||||
data.put("totalMs",(System.nanoTime()-started)/1_000_000);
|
||||
Files.writeString(out.resolve("survey.json"),new GsonBuilder().setPrettyPrinting().create().toJson(data)+"\n");
|
||||
return Map.of("path",out.toString(),"solidFraction",occupied/(double)samples,
|
||||
"reservedAirSamples",reserved,"totalMs",data.get("totalMs"));
|
||||
} catch(java.io.IOException error) {throw new IllegalStateException("Cannot write scientific survey",error);}
|
||||
}
|
||||
|
||||
static Map<String,Object> minerals(ServerLevel level) {
|
||||
long start=System.nanoTime();
|
||||
var counts=new TreeMap<String,Long>();
|
||||
var oresByHeight=new TreeMap<String,long[]>();
|
||||
var chunks=new ArrayList<List<Integer>>();
|
||||
int exposedDiamonds=0, highDiamonds=0;
|
||||
// A fixed contiguous 9x9 window, including chunk edges and natural cavities.
|
||||
// All chunks reach FULL before counting, so neighbouring decoration has completed.
|
||||
for(int cz=-4;cz<=4;cz++)for(int cx=-4;cx<=4;cx++) {level.getChunk(cx,cz);chunks.add(List.of(cx,cz));}
|
||||
var pos=new BlockPos.MutableBlockPos();
|
||||
for(var pair:chunks) {
|
||||
var chunk=level.getChunk(pair.get(0),pair.get(1));
|
||||
for(int y=0;y<320;y++)for(int z=0;z<16;z++)for(int x=0;x<16;x++) {
|
||||
pos.set(pair.get(0)*16+x,y,pair.get(1)*16+z);
|
||||
var state=chunk.getBlockState(pos);
|
||||
String block=BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath();
|
||||
counts.merge(block,1L,Long::sum);
|
||||
if(block.endsWith("_ore"))oresByHeight.computeIfAbsent(block,k->new long[320])[y]++;
|
||||
if(block.endsWith("diamond_ore")) {
|
||||
if(y>=136 || block.equals("diamond_ore"))highDiamonds++;
|
||||
boolean exposed=false;
|
||||
for(var direction:Direction.values()) if(level.getBlockState(pos.relative(direction)).isAir())exposed=true;
|
||||
if(exposed)exposedDiamonds++;
|
||||
}
|
||||
}
|
||||
}
|
||||
long gold=countOre(counts,"gold"),redstone=countOre(counts,"redstone");
|
||||
long copper=countOre(counts,"copper"),coal=countOre(counts,"coal"),iron=countOre(counts,"iron");
|
||||
if(gold!=0 || redstone!=0 || exposedDiamonds!=0 || highDiamonds!=0)
|
||||
throw new IllegalStateException("Mineral exclusions failed: "+counts+" exposed diamonds="+exposedDiamonds);
|
||||
if(copper<=iron*3 || coal<=iron*3 || countOre(counts,"lapis")==0 || counts.getOrDefault("deepslate",0L)==0)
|
||||
throw new IllegalStateException("Mineral abundance/deep rock contract failed: "+counts);
|
||||
return Map.of("chunks",chunks,"yMin",0,"yMaxExclusive",320,"counts",counts,"oresByHeight",oresByHeight,
|
||||
"exposedDiamonds",exposedDiamonds,"highDiamonds",highDiamonds,"durationMs",(System.nanoTime()-start)/1_000_000);
|
||||
}
|
||||
private static long countOre(Map<String,Long> counts,String mineral) {
|
||||
return counts.getOrDefault(mineral+"_ore",0L)+counts.getOrDefault("deepslate_"+mineral+"_ore",0L);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import com.google.gson.GsonBuilder;
|
||||
import fr.koka.sanctuary.SanctuaryMod;
|
||||
import fr.koka.sanctuary.expansion.ExpansionIsland;
|
||||
import fr.koka.sanctuary.worldgen.PopulationHydrologyRuntime;
|
||||
import fr.koka.sanctuary.worldgen.SanctuaryBiomeSource;
|
||||
import fr.koka.sanctuary.worldgen.SanctuaryChunkGenerator;
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.*;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.levelgen.*;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
|
||||
/** Optional, separately identified terrain preview. Never toggles an existing generator. */
|
||||
public final class WorldgenLab {
|
||||
public static final Identifier SETTINGS = Identifier.fromNamespaceAndPath("sanctuary_test", "relief_v1_10");
|
||||
public static final Identifier SKY_SETTINGS = Identifier.fromNamespaceAndPath("sanctuary_test", "sky_v1_10");
|
||||
public static final Identifier ECOLOGY_SETTINGS = Identifier.fromNamespaceAndPath("sanctuary_test", "ecology_v1_10");
|
||||
public static final Identifier ECOLOGY177_SETTINGS = Identifier.fromNamespaceAndPath("sanctuary_test", "relief_ecology_v1_10");
|
||||
public static final Identifier ECOLOGY178_SETTINGS = Identifier.fromNamespaceAndPath("sanctuary_test", "rocky_ecology_v1_10");
|
||||
private record Run(long started, boolean fresh) {}
|
||||
private static final Map<MinecraftServer, Run> RUNS = new WeakHashMap<>();
|
||||
private WorldgenLab() {}
|
||||
|
||||
public static boolean relief(ServerLevel level) {
|
||||
return level.dimension() == Level.OVERWORLD
|
||||
&& level.getChunkSource().getGenerator() instanceof NoiseBasedChunkGenerator noise
|
||||
&& (noise.generatorSettings().is(SETTINGS) || noise.generatorSettings().is(SKY_SETTINGS) || ecology(noise));
|
||||
}
|
||||
public static boolean ecology(net.minecraft.world.level.chunk.ChunkGenerator generator) {
|
||||
return generator instanceof NoiseBasedChunkGenerator noise && (noise.generatorSettings().is(ECOLOGY_SETTINGS) || ecology177(generator) || ecology178(generator));
|
||||
}
|
||||
|
||||
public static boolean ecology178(net.minecraft.world.level.chunk.ChunkGenerator generator) {
|
||||
return generator instanceof NoiseBasedChunkGenerator noise && noise.generatorSettings().is(ECOLOGY178_SETTINGS);
|
||||
}
|
||||
|
||||
public static boolean ecology177(net.minecraft.world.level.chunk.ChunkGenerator generator) {
|
||||
return generator instanceof NoiseBasedChunkGenerator noise && noise.generatorSettings().is(ECOLOGY177_SETTINGS);
|
||||
}
|
||||
|
||||
static void register() {
|
||||
Registry.register(BuiltInRegistries.BIOME_SOURCE, Identifier.fromNamespaceAndPath("sanctuary_test", "rocky_ecology_v1"), RockyBiomes178.CODEC);
|
||||
Registry.register(BuiltInRegistries.MATERIAL_RULE_TYPE, Identifier.fromNamespaceAndPath("sanctuary_test", "rocky_stone_v1"), RockyStone178.CODEC);
|
||||
Registry.register(BuiltInRegistries.DENSITY_FUNCTION_TYPE, Identifier.fromNamespaceAndPath("sanctuary_test", "relief_ecology_v1"), ReliefEcology177.CODEC);
|
||||
Registry.register(BuiltInRegistries.BIOME_SOURCE, Identifier.fromNamespaceAndPath("sanctuary_test", "relief_ecology_v1"), ReliefBiomes177.CODEC);
|
||||
Registry.register(BuiltInRegistries.MATERIAL_RULE_TYPE, Identifier.fromNamespaceAndPath("sanctuary_test", "relief_stone_v1"), ReliefStone177.CODEC);
|
||||
Registry.register(BuiltInRegistries.DENSITY_FUNCTION_TYPE,
|
||||
Identifier.fromNamespaceAndPath("sanctuary_test", "sky_v1"), SkyFragments175.CODEC);
|
||||
Registry.register(BuiltInRegistries.BIOME_SOURCE,
|
||||
Identifier.fromNamespaceAndPath("sanctuary_test", "ecology_v1"), EcologyBiomes176.CODEC);
|
||||
Registry.register(BuiltInRegistries.MATERIAL_RULE_TYPE,
|
||||
Identifier.fromNamespaceAndPath("sanctuary_test", "ecology_strata_v1"), EcologyStrata176.CODEC);
|
||||
ServerTickEvents.END_SERVER_TICK.register(EcologySurvey176::tick);
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> RUNS.put(server,
|
||||
new Run(System.nanoTime(), !server.getWorldData().overworldData().isInitialized())));
|
||||
ServerLevelEvents.LOAD.register((server, level) -> {
|
||||
if (!relief(level)) return;
|
||||
var generator = (NoiseBasedChunkGenerator) level.getChunkSource().getGenerator();
|
||||
if(ecology(generator)) {
|
||||
var field=level.getChunkSource().randomState().getSampler(generator.generatorSettings().value().noiseRouter().finalDensity());
|
||||
if(ecology178(generator))((RockyBiomes178)generator.getBiomeSource()).bind(level.getSeed(),(ReliefEcology177.Field)field);
|
||||
else if(ecology177(generator))((ReliefBiomes177)generator.getBiomeSource()).bind(level.getSeed(),(ReliefEcology177.Field)field);
|
||||
else ((EcologyBiomes176)generator.getBiomeSource()).bind(level.getSeed(),(SkyFragments175.Field)field);
|
||||
} else {
|
||||
if (!(generator.getBiomeSource() instanceof SanctuaryBiomeSource source) || source.generation() != 24)
|
||||
throw new IllegalStateException("Terrain lab v1 requires the Sanctuary v24 biome source");
|
||||
// The production biome selector needs an origin but no expansion journal or planners.
|
||||
source.setIslands(List.of(ExpansionIsland.origin(724, level.getSeed(), 24)));
|
||||
}
|
||||
if (PopulationHydrologyRuntime.enabled(generator))
|
||||
throw new IllegalStateException("Terrain lab unexpectedly enables regional hydrology");
|
||||
});
|
||||
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
|
||||
var run = RUNS.get(server);
|
||||
if (relief(server.overworld()) && run.fresh()) findSpawn(server.overworld());
|
||||
if (Boolean.getBoolean("sanctuary.worldgenLab")) report(server, run);
|
||||
});
|
||||
ServerLifecycleEvents.SERVER_STOPPED.register(RUNS::remove);
|
||||
}
|
||||
|
||||
private static void findSpawn(ServerLevel level) {
|
||||
var generator = (NoiseBasedChunkGenerator) level.getChunkSource().getGenerator();
|
||||
for (int r = 0; r <= 160; r += 16) for (int x = -r; x <= r; x += 16) for (int z = -r; z <= r; z += 16) {
|
||||
if (Math.max(Math.abs(x), Math.abs(z)) != r) continue;
|
||||
int sampled = generator.getBaseHeight(x, z, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES,
|
||||
level, level.getChunkSource().randomState());
|
||||
if (sampled <= level.getMinY()) continue;
|
||||
level.getChunk(x >> 4, z >> 4);
|
||||
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z);
|
||||
var pos = new BlockPos(x, y, z);
|
||||
if (!level.getBlockState(pos.below()).isSolidRender() || !level.getBlockState(pos).isAir()
|
||||
|| !level.getBlockState(pos.above()).isAir()) continue;
|
||||
level.setRespawnData(LevelData.RespawnData.of(Level.OVERWORLD, pos, 0, 0));
|
||||
SanctuaryMod.LOGGER.info("WORLDGEN_LAB_SPAWN {}", pos);
|
||||
return;
|
||||
}
|
||||
throw new IllegalStateException("No safe terrain lab spawn in the bounded search; use another seed");
|
||||
}
|
||||
|
||||
private static void report(MinecraftServer server, Run run) {
|
||||
var level = server.overworld();
|
||||
var folder = server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize();
|
||||
if (!folder.getFileName().toString().equals("worldgen-lab-v1"))
|
||||
throw new IllegalStateException("Worldgen metrics require the dedicated worldgen-lab-v1 directory");
|
||||
boolean fast = relief(level);
|
||||
if (!fast && !(level.getChunkSource().getGenerator() instanceof SanctuaryChunkGenerator))
|
||||
throw new IllegalStateException("Unrecognized worldgen laboratory profile");
|
||||
var result = new LinkedHashMap<String, Object>();
|
||||
boolean ecology=ecology(level.getChunkSource().getGenerator());
|
||||
boolean sky = fast && (((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).generatorSettings().is(SKY_SETTINGS) || ecology);
|
||||
result.put("profile", ecology178(level.getChunkSource().getGenerator()) ? "rocky-ecology-v1" : ecology177(level.getChunkSource().getGenerator()) ? "relief-ecology-v1" : ecology ? "ecology-v1" : sky ? "sky-v1" : fast ? "relief-v1" : "reference");
|
||||
result.put("seed", level.getSeed()); result.put("fresh", run.fresh());
|
||||
result.put("serverStartedMs", (System.nanoTime() - run.started()) / 1_000_000);
|
||||
if (Boolean.getBoolean("sanctuary.worldgenLab.verify")) {
|
||||
long start = System.nanoTime();
|
||||
int[][] chunks = {{0,0},{1,0},{-1,0},{0,1},{0,-1},{12,0},{-12,0},{0,12},{0,-12},{24,0}};
|
||||
for (int[] chunk : chunks) level.getChunk(chunk[0], chunk[1]);
|
||||
result.put("probeChunks", chunks); result.put("probeChunksMs", (System.nanoTime()-start)/1_000_000);
|
||||
if (fast) {
|
||||
var noise = (NoiseBasedChunkGenerator) level.getChunkSource().getGenerator();
|
||||
var random = level.getChunkSource().randomState();
|
||||
var expected = level.registryAccess().lookupOrThrow(Registries.NOISE_SETTINGS)
|
||||
.getOrThrow(ResourceKey.create(Registries.NOISE_SETTINGS, SanctuaryMod.id("unified_10"))).value();
|
||||
var reference = RandomState.create(level.registryAccess().lookupOrThrow(Registries.NOISE), level.getSeed(), expected);
|
||||
int samples = 0;
|
||||
for (int x = -384; x <= 384; x += 96) for (int z = -384; z <= 384; z += 96)
|
||||
for (int y = 16; y < (ecology177(noise) || ecology178(noise) ? 216 : sky ? 300 : 640); y += 32) {
|
||||
double actual = random.sampleBlockValueUncached(noise.generatorSettings().value().noiseRouter().finalDensity(), x,y,z);
|
||||
double wanted = reference.sampleBlockValueUncached(expected.noiseRouter().finalDensity(), x,y,z);
|
||||
if (Double.doubleToLongBits(actual) != Double.doubleToLongBits(wanted))
|
||||
throw new IllegalStateException("Relief diverged from Sanctuary at " + new BlockPos(x,y,z));
|
||||
samples++;
|
||||
}
|
||||
int plans = PopulationHydrologyRuntime.cachedRegionCount(random);
|
||||
if (plans != 0) throw new IllegalStateException("Terrain preview computed hydrology");
|
||||
if (Files.exists(folder.resolve("data/sanctuary-world-v24")))
|
||||
throw new IllegalStateException("Terrain preview created an expansion journal");
|
||||
result.put("matchingDensitySamples", samples); result.put("hydrologyRegions", plans);
|
||||
if(ecology178(noise)) {
|
||||
result.put("quickChecks",RockyChecks178.check(level));
|
||||
} else if(ecology) {
|
||||
EcologySurvey176.begin(server,folder.getParent(),result,run.started(),run.fresh());
|
||||
return;
|
||||
}
|
||||
if (sky && !ecology178(noise)) result.put("survey", SkySurvey175.measure(level, folder.getParent(), reference, expected, run.fresh()));
|
||||
}
|
||||
}
|
||||
result.put("readyForVisitMs", (System.nanoTime() - run.started()) / 1_000_000);
|
||||
writeReport(folder.getParent(),result);
|
||||
}
|
||||
static void writeReport(Path directory,Map<String,Object> result) {
|
||||
try {
|
||||
Files.writeString(directory.resolve("worldgen-lab-metrics.json"),
|
||||
new GsonBuilder().setPrettyPrinting().create().toJson(result) + "\n");
|
||||
} catch (java.io.IOException error) { throw new IllegalStateException("Cannot write worldgen metrics", error); }
|
||||
SanctuaryMod.LOGGER.info("WORLDGEN_LAB_READY {}", result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package fr.koka.sanctuarytest.mixin;
|
||||
|
||||
import fr.koka.sanctuarytest.LabMinerals175;
|
||||
import fr.koka.sanctuarytest.EcologyWater176;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ChunkGenerator.class)
|
||||
public abstract class LabDecorationMixin {
|
||||
@Inject(method="applyBiomeDecoration",at=@At("HEAD"))
|
||||
private void sanctuaryTest$localWater(WorldGenLevel level,ChunkAccess chunk,StructureManager structures,CallbackInfo callback) {
|
||||
fr.koka.sanctuarytest.ReliefGroves177.place(level,(ChunkGenerator)(Object)this,chunk);
|
||||
EcologyWater176.place(level,(ChunkGenerator)(Object)this,chunk);
|
||||
fr.koka.sanctuarytest.RockyWater178.place(level,(ChunkGenerator)(Object)this,chunk);
|
||||
}
|
||||
@Inject(method="applyBiomeDecoration",at=@At("TAIL"))
|
||||
private void sanctuaryTest$labDeposits(WorldGenLevel level,ChunkAccess chunk,StructureManager structures,CallbackInfo callback) {
|
||||
LabMinerals175.place(level,(ChunkGenerator)(Object)this,chunk);
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package fr.koka.sanctuarytest.mixin;
|
||||
|
||||
import fr.koka.sanctuarytest.LabMinerals175;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
|
||||
import net.minecraft.world.level.levelgen.placement.FeaturePlacer;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(FeaturePlacer.class)
|
||||
public abstract class LabFeaturePlacerMixin {
|
||||
@Shadow @Final private ChunkGenerator generator;
|
||||
@Inject(method="place(Lnet/minecraft/world/level/levelgen/placement/PlacedFeature;Lnet/minecraft/util/RandomSource;Lnet/minecraft/core/BlockPos;Z)Z",at=@At("HEAD"),cancellable=true)
|
||||
private void sanctuaryTest$labMinerals(PlacedFeature feature,RandomSource random,
|
||||
BlockPos origin,boolean biomeCheck,CallbackInfoReturnable<Boolean> callback) {
|
||||
if(LabMinerals175.active(generator) && LabMinerals175.replaces(feature.feature().value()))
|
||||
callback.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,50 @@
|
||||
{
|
||||
"generator.sanctuary_test.flat": "Sanctuary — Quick Test"
|
||||
"generator.sanctuary_test.flat": "Sanctuary — Quick Test",
|
||||
"sanctuary_test.duo.list": "Lab actors: %s",
|
||||
"sanctuary_test.duo.invalid": "Name: 1–12 letters/digits/_; up to 4 actors, unique names.",
|
||||
"sanctuary_test.duo.spawned": "%s spawned nearby (test actor).",
|
||||
"sanctuary_test.duo.missing": "Lab actor not found: %s",
|
||||
"sanctuary_test.duo.removed": "Lab actor removed: %s",
|
||||
"sanctuary_test.duo.trace_failed": "Cannot write session log.",
|
||||
"sanctuary_test.duo.trace_started": "Position log started (one sample every 2 seconds).",
|
||||
"sanctuary_test.duo.trace_stopped": "Position log stopped.",
|
||||
"generator.sanctuary_test.relief_v1": "Sanctuary — Terrain lab v1",
|
||||
"generator.sanctuary_test.sky_v1": "Sanctuary — Sky reef lab v1",
|
||||
"generator.sanctuary_test.ecology_v1": "Sanctuary — Strata and biome lab v1",
|
||||
"biome.sanctuary_test.ecology_forest": "Lab forest",
|
||||
"biome.sanctuary_test.ecology_birch": "Birch fringe",
|
||||
"biome.sanctuary_test.ecology_flowers": "Flower fringe",
|
||||
"biome.sanctuary_test.ecology_meadow": "Lab meadow",
|
||||
"biome.sanctuary_test.ecology_autumn": "Autumn forest",
|
||||
"biome.sanctuary_test.ecology_cherry": "Cherry forest",
|
||||
"biome.sanctuary_test.ecology_jungle": "Tropical reef",
|
||||
"biome.sanctuary_test.ecology_swamp": "Swamp reef",
|
||||
"biome.sanctuary_test.ecology_mangrove": "Mangrove reef",
|
||||
"biome.sanctuary_test.ecology_autumn_scrub": "Autumn fragment",
|
||||
"biome.sanctuary_test.ecology_cherry_scrub": "Blossom fragment",
|
||||
"biome.sanctuary_test.ecology_lush": "Lab lush caves",
|
||||
"biome.sanctuary_test.ecology_dripstone": "Lab dripstone caves",
|
||||
"biome.sanctuary_test.ecology_void": "Lab sky",
|
||||
"generator.sanctuary_test.relief_ecology_v1": "Sanctuary lab - terrain ecology 177",
|
||||
"biome.sanctuary_test.relief_forest": "Plateau forest",
|
||||
"biome.sanctuary_test.relief_flowers": "Flower meadow",
|
||||
"biome.sanctuary_test.relief_autumn": "Autumn valley",
|
||||
"biome.sanctuary_test.relief_cherry": "Cherry heights",
|
||||
"biome.sanctuary_test.relief_jungle": "Jungle reef",
|
||||
"biome.sanctuary_test.relief_swamp": "Swamp reef",
|
||||
"biome.sanctuary_test.relief_bare": "Rock fragment",
|
||||
"biome.sanctuary_test.relief_caves": "Humid caves",
|
||||
"biome.sanctuary_test.relief_void": "Void",
|
||||
"biome.sanctuary_test.rocky_forest": "Plateau forest",
|
||||
"biome.sanctuary_test.rocky_flowers": "Flower meadow",
|
||||
"biome.sanctuary_test.rocky_autumn": "Autumn valley",
|
||||
"biome.sanctuary_test.rocky_cherry": "Cherry highlands",
|
||||
"biome.sanctuary_test.rocky_jungle": "Jungle reef",
|
||||
"biome.sanctuary_test.rocky_swamp": "Swamp reef",
|
||||
"biome.sanctuary_test.rocky_bare": "Rock fragment",
|
||||
"biome.sanctuary_test.rocky_cliffs": "Rocky cliffs",
|
||||
"biome.sanctuary_test.rocky_caves": "Damp caverns",
|
||||
"biome.sanctuary_test.rocky_grove": "Underground grove",
|
||||
"biome.sanctuary_test.rocky_void": "Void",
|
||||
"generator.sanctuary_test.rocky_ecology_v1": "Sanctuary lab — rock and damp caves 178"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,50 @@
|
||||
{
|
||||
"generator.sanctuary_test.flat": "Sanctuary — Test rapide"
|
||||
"generator.sanctuary_test.flat": "Sanctuary — Test rapide",
|
||||
"sanctuary_test.duo.list": "Personnages labo : %s",
|
||||
"sanctuary_test.duo.invalid": "Nom : 1–12 lettres/chiffres/_ ; 4 personnages maximum, noms uniques.",
|
||||
"sanctuary_test.duo.spawned": "%s créé à proximité (personnage de test).",
|
||||
"sanctuary_test.duo.missing": "Personnage labo introuvable : %s",
|
||||
"sanctuary_test.duo.removed": "Personnage labo retiré : %s",
|
||||
"sanctuary_test.duo.trace_failed": "Impossible d’écrire le journal de séance.",
|
||||
"sanctuary_test.duo.trace_started": "Journal des positions démarré (une mesure toutes les 2 secondes).",
|
||||
"sanctuary_test.duo.trace_stopped": "Journal des positions arrêté.",
|
||||
"generator.sanctuary_test.relief_v1": "Sanctuary — Labo relief v1",
|
||||
"generator.sanctuary_test.sky_v1": "Sanctuary — Labo récifs aériens v1",
|
||||
"generator.sanctuary_test.ecology_v1": "Sanctuary — Labo strates et biomes v1",
|
||||
"biome.sanctuary_test.ecology_forest": "Forêt du laboratoire",
|
||||
"biome.sanctuary_test.ecology_birch": "Lisière de bouleaux",
|
||||
"biome.sanctuary_test.ecology_flowers": "Lisière fleurie",
|
||||
"biome.sanctuary_test.ecology_meadow": "Prairie du laboratoire",
|
||||
"biome.sanctuary_test.ecology_autumn": "Forêt automnale",
|
||||
"biome.sanctuary_test.ecology_cherry": "Forêt de cerisiers",
|
||||
"biome.sanctuary_test.ecology_jungle": "Récif tropical",
|
||||
"biome.sanctuary_test.ecology_swamp": "Récif marécageux",
|
||||
"biome.sanctuary_test.ecology_mangrove": "Récif de mangrove",
|
||||
"biome.sanctuary_test.ecology_autumn_scrub": "Fragment automnal",
|
||||
"biome.sanctuary_test.ecology_cherry_scrub": "Fragment fleuri",
|
||||
"biome.sanctuary_test.ecology_lush": "Cavernes luxuriantes du laboratoire",
|
||||
"biome.sanctuary_test.ecology_dripstone": "Cavernes rocheuses du laboratoire",
|
||||
"biome.sanctuary_test.ecology_void": "Ciel du laboratoire",
|
||||
"generator.sanctuary_test.relief_ecology_v1": "Labo Sanctuary — relief et écologie 177",
|
||||
"biome.sanctuary_test.relief_forest": "Forêt du plateau",
|
||||
"biome.sanctuary_test.relief_flowers": "Plaine fleurie",
|
||||
"biome.sanctuary_test.relief_autumn": "Vallée automnale",
|
||||
"biome.sanctuary_test.relief_cherry": "Hauteurs des cerisiers",
|
||||
"biome.sanctuary_test.relief_jungle": "Récif jungle",
|
||||
"biome.sanctuary_test.relief_swamp": "Récif marécageux",
|
||||
"biome.sanctuary_test.relief_bare": "Fragment rocheux",
|
||||
"biome.sanctuary_test.relief_caves": "Cavités humides",
|
||||
"biome.sanctuary_test.relief_void": "Vide",
|
||||
"biome.sanctuary_test.rocky_forest": "Forêt du plateau",
|
||||
"biome.sanctuary_test.rocky_flowers": "Plaine fleurie",
|
||||
"biome.sanctuary_test.rocky_autumn": "Vallée automnale",
|
||||
"biome.sanctuary_test.rocky_cherry": "Hauteurs des cerisiers",
|
||||
"biome.sanctuary_test.rocky_jungle": "Récif jungle",
|
||||
"biome.sanctuary_test.rocky_swamp": "Récif marécageux",
|
||||
"biome.sanctuary_test.rocky_bare": "Fragment rocheux",
|
||||
"biome.sanctuary_test.rocky_cliffs": "Massifs rocheux",
|
||||
"biome.sanctuary_test.rocky_caves": "Cavernes humides",
|
||||
"biome.sanctuary_test.rocky_grove": "Poche de végétation souterraine",
|
||||
"biome.sanctuary_test.rocky_void": "Vide",
|
||||
"generator.sanctuary_test.rocky_ecology_v1": "Labo Sanctuary — roche et humidité 178"
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,6 +1,11 @@
|
||||
{
|
||||
"replace": false,
|
||||
"values": [
|
||||
"sanctuary_test:flat"
|
||||
"sanctuary_test:flat",
|
||||
"sanctuary_test:relief_v1",
|
||||
"sanctuary_test:sky_v1",
|
||||
"sanctuary_test:ecology_v1",
|
||||
"sanctuary_test:relief_ecology_v1",
|
||||
"sanctuary_test:rocky_ecology_v1"
|
||||
]
|
||||
}
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"type": "minecraft:fox",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/fog_color": "#ccd8e2",
|
||||
"minecraft:visual/sky_color": "#7ca3ff",
|
||||
"minecraft:visual/water_fog_color": "#375154"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.6,
|
||||
"effects": {
|
||||
"dry_foliage_color": "#8c3a04",
|
||||
"foliage_color": "#e68e30",
|
||||
"grass_color": "#df6827",
|
||||
"water_color": "#375154"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:trees_dappled_forest",
|
||||
"minecraft:brown_mushroom_dappled_forest",
|
||||
"minecraft:patch_red_shrub",
|
||||
"minecraft:patch_grass_forest"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.6
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"type": "minecraft:fox",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/fog_color": "#ccd8e2",
|
||||
"minecraft:visual/sky_color": "#7ca3ff",
|
||||
"minecraft:visual/water_fog_color": "#375154"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.6,
|
||||
"effects": {
|
||||
"dry_foliage_color": "#8c3a04",
|
||||
"foliage_color": "#e68e30",
|
||||
"grass_color": "#df6827",
|
||||
"water_color": "#375154"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:brown_mushroom_dappled_forest",
|
||||
"minecraft:patch_red_shrub",
|
||||
"minecraft:patch_grass_forest"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.6
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7aa5ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.6,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:forest_flowers",
|
||||
"minecraft:wildflowers_birch_forest",
|
||||
"minecraft:trees_birch",
|
||||
"minecraft:patch_bush",
|
||||
"minecraft:flower_default",
|
||||
"minecraft:patch_grass_forest",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_pumpkin",
|
||||
"minecraft:patch_sugar_cane",
|
||||
"minecraft:patch_firefly_bush_near_water"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.6
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.cherry_grove"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 2,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7ba4ff",
|
||||
"minecraft:visual/water_fog_color": "#5db7ef"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"foliage_color": "#b6db61",
|
||||
"grass_color": "#b6db61",
|
||||
"water_color": "#5db7ef"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:patch_tall_grass_2",
|
||||
"minecraft:patch_grass_plain",
|
||||
"minecraft:flower_cherry",
|
||||
"minecraft:trees_cherry"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.5
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.cherry_grove"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 2,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7ba4ff",
|
||||
"minecraft:visual/water_fog_color": "#5db7ef"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"foliage_color": "#b6db61",
|
||||
"grass_color": "#b6db61",
|
||||
"water_color": "#5db7ef"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:patch_tall_grass_2",
|
||||
"minecraft:patch_grass_plain",
|
||||
"minecraft:flower_cherry"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.5
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.dripstone_caves"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:drowned",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#78a7ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.4,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:large_dripstone"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[
|
||||
"minecraft:dripstone_cluster",
|
||||
"minecraft:pointed_dripstone"
|
||||
],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:patch_tall_grass_2",
|
||||
"minecraft:trees_plains",
|
||||
"minecraft:flower_plains",
|
||||
"minecraft:patch_grass_plain",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_pumpkin"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.8
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.flower_forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 3,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#79a6ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:flower_forest_flowers",
|
||||
"minecraft:trees_flower_forest",
|
||||
"minecraft:flower_flower_forest",
|
||||
"minecraft:patch_grass_badlands",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_pumpkin",
|
||||
"minecraft:patch_sugar_cane",
|
||||
"minecraft:patch_firefly_bush_near_water"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.7
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:wolf",
|
||||
"count": 4,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#79a6ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:forest_flowers",
|
||||
"minecraft:trees_birch_and_oak_leaf_litter",
|
||||
"minecraft:patch_bush",
|
||||
"minecraft:flower_default",
|
||||
"minecraft:patch_grass_forest",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_pumpkin",
|
||||
"minecraft:patch_sugar_cane",
|
||||
"minecraft:patch_firefly_bush_near_water"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.7
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.sparse_jungle"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:wolf",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 8
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#77a8ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:trees_sparse_jungle",
|
||||
"minecraft:flower_warm",
|
||||
"minecraft:patch_grass_jungle",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_pumpkin",
|
||||
"minecraft:patch_sugar_cane",
|
||||
"minecraft:patch_firefly_bush_near_water",
|
||||
"minecraft:vines",
|
||||
"minecraft:patch_melon_sparse"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.95
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.lush_caves"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"axolotls": [
|
||||
{
|
||||
"type": "minecraft:axolotl",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"water_ambient": [
|
||||
{
|
||||
"type": "minecraft:tropical_fish",
|
||||
"count": 8,
|
||||
"weight": 25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7ba4ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.5,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:patch_tall_grass_2",
|
||||
"minecraft:lush_caves_ceiling_vegetation",
|
||||
"minecraft:cave_vines",
|
||||
"minecraft:lush_caves_vegetation",
|
||||
"minecraft:spore_blossom",
|
||||
"minecraft:classic_vines_cave_feature"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.5
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.swamp"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/increased_fire_burnout": true,
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:frog",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 5,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 70
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 1,
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"type": "minecraft:bogged",
|
||||
"count": 4,
|
||||
"weight": 30
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"water_ambient": [
|
||||
{
|
||||
"type": "minecraft:tropical_fish",
|
||||
"count": 8,
|
||||
"weight": 25
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/fog_color": "#c0d8ff",
|
||||
"minecraft:visual/sky_color": "#78a7ff",
|
||||
"minecraft:visual/water_fog_color": "#4d7a60",
|
||||
"minecraft:visual/water_fog_end_distance": {
|
||||
"argument": 0.85,
|
||||
"modifier": "multiply"
|
||||
}
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.9,
|
||||
"effects": {
|
||||
"dry_foliage_color": "#7b5334",
|
||||
"foliage_color": "#8db127",
|
||||
"grass_color_modifier": "swamp",
|
||||
"water_color": "#3a7a6a"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_grass",
|
||||
"minecraft:disk_clay"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:trees_mangrove",
|
||||
"minecraft:patch_grass_normal",
|
||||
"minecraft:patch_dead_bush",
|
||||
"minecraft:patch_waterlily",
|
||||
"minecraft:seagrass_swamp",
|
||||
"minecraft:patch_firefly_bush_near_water"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.8
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.meadow"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:donkey",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 2,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
},
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 2
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7ba4ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.8,
|
||||
"effects": {
|
||||
"water_color": "#0e4ecf"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:patch_tall_grass_2",
|
||||
"minecraft:patch_grass_meadow",
|
||||
"minecraft:flower_meadow",
|
||||
"minecraft:trees_meadow",
|
||||
"minecraft:wildflowers_meadow"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.5
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.swamp"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/increased_fire_burnout": true,
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:frog",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 5,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 70
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 1,
|
||||
"weight": 1
|
||||
},
|
||||
{
|
||||
"type": "minecraft:bogged",
|
||||
"count": 4,
|
||||
"weight": 30
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#78a7ff",
|
||||
"minecraft:visual/water_fog_color": "#232317",
|
||||
"minecraft:visual/water_fog_end_distance": {
|
||||
"argument": 0.85,
|
||||
"modifier": "multiply"
|
||||
}
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.9,
|
||||
"effects": {
|
||||
"dry_foliage_color": "#7b5334",
|
||||
"foliage_color": "#6a7039",
|
||||
"grass_color_modifier": "swamp",
|
||||
"water_color": "#617b64"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_clay"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:trees_swamp",
|
||||
"minecraft:flower_swamp",
|
||||
"minecraft:patch_grass_normal",
|
||||
"minecraft:patch_dead_bush",
|
||||
"minecraft:patch_waterlily",
|
||||
"minecraft:brown_mushroom_swamp",
|
||||
"minecraft:red_mushroom_swamp",
|
||||
"minecraft:brown_mushroom_normal",
|
||||
"minecraft:red_mushroom_normal",
|
||||
"minecraft:patch_sugar_cane_swamp",
|
||||
"minecraft:patch_pumpkin",
|
||||
"minecraft:patch_firefly_bush_swamp",
|
||||
"minecraft:patch_firefly_bush_near_water_swamp",
|
||||
"minecraft:seagrass_swamp"
|
||||
],
|
||||
[
|
||||
"minecraft:freeze_top_layer"
|
||||
]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 0.8
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [],
|
||||
"axolotls": [],
|
||||
"creature": [],
|
||||
"misc": [],
|
||||
"monster": [],
|
||||
"underground_water_creature": [],
|
||||
"water_ambient": [],
|
||||
"water_creature": []
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/sky_color": "#7ba4ff"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.5,
|
||||
"effects": {
|
||||
"water_color": "#3f76e4"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
],
|
||||
"has_precipitation": false,
|
||||
"temperature": 0.5
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
{
|
||||
"attributes": {
|
||||
"minecraft:audio/background_music": {
|
||||
"default": {
|
||||
"max_delay": 24000,
|
||||
"min_delay": 12000,
|
||||
"sound": "minecraft:music.overworld.forest"
|
||||
}
|
||||
},
|
||||
"minecraft:gameplay/natural_mob_spawns": {
|
||||
"argument": {
|
||||
"spawn_costs": {},
|
||||
"spawns_by_category": {
|
||||
"ambient": [
|
||||
{
|
||||
"type": "minecraft:bat",
|
||||
"count": 8,
|
||||
"weight": 10
|
||||
}
|
||||
],
|
||||
"creature": [
|
||||
{
|
||||
"type": "minecraft:sheep",
|
||||
"count": 4,
|
||||
"weight": 12
|
||||
},
|
||||
{
|
||||
"type": "minecraft:pig",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:chicken",
|
||||
"count": 4,
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:cow",
|
||||
"count": 4,
|
||||
"weight": 8
|
||||
},
|
||||
{
|
||||
"type": "minecraft:rabbit",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
},
|
||||
{
|
||||
"type": "minecraft:fox",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 2
|
||||
},
|
||||
"weight": 4
|
||||
}
|
||||
],
|
||||
"monster": [
|
||||
{
|
||||
"type": "minecraft:spider",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"count": 4,
|
||||
"weight": 95
|
||||
},
|
||||
{
|
||||
"type": "minecraft:zombie_villager",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
},
|
||||
{
|
||||
"type": "minecraft:skeleton",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:creeper",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:slime",
|
||||
"count": 4,
|
||||
"weight": 100
|
||||
},
|
||||
{
|
||||
"type": "minecraft:enderman",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 4,
|
||||
"min_inclusive": 1
|
||||
},
|
||||
"weight": 10
|
||||
},
|
||||
{
|
||||
"type": "minecraft:witch",
|
||||
"count": 1,
|
||||
"weight": 5
|
||||
}
|
||||
],
|
||||
"underground_water_creature": [
|
||||
{
|
||||
"type": "minecraft:glow_squid",
|
||||
"count": {
|
||||
"type": "minecraft:uniform",
|
||||
"max_inclusive": 6,
|
||||
"min_inclusive": 4
|
||||
},
|
||||
"weight": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"modifier": "overlay"
|
||||
},
|
||||
"minecraft:visual/fog_color": "#ccd8e2",
|
||||
"minecraft:visual/sky_color": "#7ca3ff",
|
||||
"minecraft:visual/water_fog_color": "#375154"
|
||||
},
|
||||
"carvers": [],
|
||||
"downfall": 0.6,
|
||||
"effects": {
|
||||
"dry_foliage_color": "#8c3a04",
|
||||
"foliage_color": "#e68e30",
|
||||
"grass_color": "#df6827",
|
||||
"water_color": "#375154"
|
||||
},
|
||||
"features": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:disk_sand",
|
||||
"minecraft:disk_clay",
|
||||
"minecraft:disk_gravel"
|
||||
],
|
||||
[],
|
||||
[],
|
||||
[
|
||||
"minecraft:glow_lichen",
|
||||
"minecraft:trees_dappled_forest",
|
||||
"minecraft:brown_mushroom_dappled_forest",
|
||||
"minecraft:patch_red_shrub",
|
||||
"minecraft:patch_grass_forest"
|
||||
],
|
||||
[]
|
||||
],
|
||||
"has_precipitation": true,
|
||||
"temperature": 2.0
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user