Compare commits
58
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6c76df7d9f | ||
|
|
454d9a5c08 | ||
|
|
ba1b228e76 | ||
|
|
1d60a3eedc | ||
|
|
16d81d892f | ||
|
|
7497d9182d | ||
|
|
6b97d57bde | ||
|
|
7c14696020 | ||
|
|
39ff7e95d5 | ||
|
|
918143aaa6 | ||
|
|
9da3f7f13b | ||
|
|
7179b5d4cb | ||
|
|
78f047d3dc | ||
|
|
c7f32c9416 | ||
|
|
97ea94e482 | ||
|
|
51e7e2590e | ||
|
|
658aaf8732 | ||
|
|
c53b39e71c | ||
|
|
f40382ad04 | ||
|
|
a067c7e66b | ||
|
|
6cfa4c07bf | ||
|
|
1ca01a47b7 | ||
|
|
a46946bd4c | ||
|
|
65e7127b1d | ||
|
|
5f32455f79 | ||
|
|
459420503e | ||
|
|
a82fc559b1 | ||
|
|
5a74ba57ed | ||
|
|
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 | ||
|
|
8de569bbf1 |
@@ -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,364 @@
|
||||
# Sanctuary
|
||||
|
||||
## beta.203 — fractures et taïga géante au Nord
|
||||
|
||||
[Dernière passe du Nord](docs/north-fracture-beta203.md) : variations de relief
|
||||
et ouvertures par bruit 3D, lisières de podzol irrégulières, épicéas pouvant
|
||||
atteindre 64 blocs. La neige ajoutée après végétation est retirée ; les sommets
|
||||
mêlent roche, neige et plusieurs glaces. Bassins protégés, cinq igloos conservés,
|
||||
aucun nouveau village. Profils neufs seulement, Nord 1024.
|
||||
|
||||
## beta.202 — taïga et neige étagée au Nord
|
||||
|
||||
[Écologie nordique](docs/north-ecology-beta202.md) : relief et lacs 201 conservés,
|
||||
vallées ouvertes et taïga sur herbe/podzol, grands épicéas et pics de glace natifs.
|
||||
La neige varie avec l'altitude et le climat ; la glace des grottes reste derrière
|
||||
une enveloppe rocheuse. Le village cherche une plaine entièrement enneigée.
|
||||
Profils neufs uniquement, expansion de 1024 blocs.
|
||||
|
||||
## beta.201 — Nord compact et pics de glace vanilla
|
||||
|
||||
[Glacier nordique](docs/north-glacier-beta201.md) : diamètre ramené à 1024,
|
||||
glace visible, deux lacs, bosquets d'épicéas et pics natifs `ice_spike` / `ice_patch`.
|
||||
Le village de cinq igloos et son laboratoire sont conservés et rapprochés du lac.
|
||||
Profils neufs uniquement ; les paysages froids des diagonales restent à concevoir.
|
||||
|
||||
## beta.200 — premier Nord glacial
|
||||
|
||||
[Expansion nordique](docs/north-expansion-beta200.md) : territoire de 2048 blocs,
|
||||
crêtes enneigées, taïga, cavernes de glace, lacs gelés et village de cinq igloos,
|
||||
dont un laboratoire souterrain. Placement dans la direction de l’ancre ; arrivée
|
||||
et relais préparés, puis génération pendant l’exploration. Nouveaux mondes uniquement.
|
||||
Export local : [MRpack de test](build/Sanctuary-Test-beta.200.mrpack).
|
||||
Compilation, contrôles ciblés et activation/reprise du Nord vérifiés ; la suite
|
||||
historique complète reste non validée, avec le détail dans le ticket.
|
||||
|
||||
## beta.199 — relais d’expansion en cours de partie
|
||||
|
||||
[Affinage du moteur existant](docs/runtime-expansions-beta199.md) : recherche
|
||||
automatique proche sur 360°, régions minces, relais de continuation, reprise
|
||||
après fermeture et annonce SGA à la fin. Nouveaux mondes de laboratoire
|
||||
Small/Medium/Large ; étoiles synchronisées sur sept journées Minecraft.
|
||||
|
||||
## beta.198 — correction du plantage de création Large
|
||||
|
||||
[Admission de la galerie centrale](docs/gallery-generation-beta198.md) :
|
||||
correction du défaut reproduit avec la graine Windows fournie, en conservant
|
||||
le portail au centre et les anciens profils. Nouveaux mondes uniquement.
|
||||
|
||||
## beta.197 — arrivée et détails du laboratoire
|
||||
|
||||
[Corrections du laboratoire](docs/worldgen-fixes-beta197.md) : départ sur
|
||||
l’île, cascades ouvertes vers l’extérieur, sol minier sans diagonales répétées
|
||||
et toit de cabane continu. Nouveaux profils Small/Medium/Large ; enquête séparée
|
||||
sur l’arrêt signalé chez un ami à la création Large.
|
||||
|
||||
## beta.196 — laboratoire à partager
|
||||
|
||||
[Pack de test Small, Medium et Large](docs/shared-lab-beta196.md) : une entrée
|
||||
Sanctuary, taille personnalisable, dernier terrain du labo et atlas adapté.
|
||||
Diagnostic du chargement et enregistrement JFR optionnel ; anciens mondes conservés.
|
||||
|
||||
## beta.195 — choix de génération simplifiés
|
||||
|
||||
[Sanctuary et les types vanilla](docs/worldgen-options-beta195.md) restent dans
|
||||
la création de monde ; les variantes de labo sont masquées, leurs sauvegardes
|
||||
restent lisibles. Nouvelle visite demandée sur le profil beta.193, graine 0,
|
||||
avec le menu principal sans Amis. Le terrain est conservé pour cette comparaison.
|
||||
|
||||
## beta.194 — menu principal sans Amis
|
||||
|
||||
[Reprise des interfaces](docs/main-menu-beta194.md) : Solo et Multijoueur sont suivis
|
||||
directement d’Options et Quitter, sans bouton Amis ni ligne vide. Le
|
||||
[fil rouge](docs/storyquest-fil-rouge.md) reprend les interfaces et le Storyquest ;
|
||||
les retouches de génération et les séries de captures sont suspendues.
|
||||
|
||||
## beta.193 — raccordements et comparaison visuelle
|
||||
|
||||
[Laboratoire sur plusieurs graines](docs/worldgen-continuity-beta193.md) : réservations
|
||||
communes pour les bassins, ancres aériennes posées sur le terrain réel et
|
||||
entailles locales du massif. Le nouveau profil `coherent` ajoute des contrôles
|
||||
de collision entre aménagements et des visites photo Vulkan reproductibles.
|
||||
Les anciens profils et sauvegardes restent séparés.
|
||||
|
||||
## beta.192 — couronnes et faces du massif
|
||||
|
||||
[Variante de relief érodé](docs/eroded-massif-beta192.md) : sommets localement
|
||||
plus plats, faces rocheuses plus raides et entailles entre les reliefs.
|
||||
Profil neuf `eroded`, graine 42 ; couverture végétale selon la pente réelle.
|
||||
Les cavités profondes et les aménagements existants restent conservés.
|
||||
|
||||
## beta.191 — relief d’avant les plateaux
|
||||
|
||||
[Retour à la base beta.188](docs/restore-relief-beta191.md) : relief et géologie
|
||||
antérieurs aux plateaux, passes de roche 189/190 retirées. Les corrections
|
||||
indépendantes des berges, ancres, ruines et du portail restent présentes.
|
||||
Profil neuf `original`, graine 42 ; génération, réouverture, 265 GameTests et
|
||||
assemblages validés.
|
||||
|
||||
## beta.190 — retour au relief naturel
|
||||
|
||||
[Affinage de Sanctuary Island](docs/natural-refinement-beta190.md) : fin des
|
||||
terrasses systématiques, épaules locales et affleurements sans cases de quatre
|
||||
blocs. Déversoir proche de cinq blocs de large, coffres de fer enchanté dans les
|
||||
quatre ruines. Profil `refined`, nouveau solo graine 42, vue 32 chunks.
|
||||
Les reliefs très étagés passent dans la réserve des expansions futures.
|
||||
|
||||
## beta.189 — berges, reliefs et portail inférieur
|
||||
|
||||
[Finition de la génération](docs/terrain-finish-beta189.md) : refuges de surface
|
||||
adaptés à la roche, sédiments selon profondeur et pente, berges sableuses,
|
||||
plantations d’arbres protégées, reliefs étagés et affleurements. Portail inférieur
|
||||
éclairé, cascade locale et quatre ruines sans excavation. Profil `finish`, nouvelle
|
||||
sauvegarde, graine 42. 265 GameTests, génération, réouverture et assemblages réussis.
|
||||
|
||||
## beta.188 — fonds d’étangs et soufre vivant
|
||||
|
||||
[Laboratoire de finition](docs/living-details-beta188.md) : refuges adaptés
|
||||
au milieu et à leur couleur, fonds mêlant argile, boue, sable et gravier,
|
||||
végétation aquatique, pics et geysers natifs. Wagonnets avec une ou deux
|
||||
pièces en fer enchanté. Profil `details`, monde neuf, graine 42.
|
||||
265 GameTests, contrôles natifs et assemblages réussis ; solo Vulkan ouvert
|
||||
à 32 chunks. Les anciennes sauvegardes restent inchangées.
|
||||
|
||||
## beta.187 — cavités de soufre et bassins de surface
|
||||
|
||||
[Nouveau laboratoire](docs/natural-caves-beta187.md) : soufre intégré aux
|
||||
cavités existantes, trois bassins ouverts au ciel et huit ancres orientées
|
||||
vers leur expansion, avec approche depuis le centre. Profil `natural`,
|
||||
graine 42, nouvelle sauvegarde. Génération et réouverture vérifiées,
|
||||
265 GameTests et assemblages réussis ; solo Vulkan ouvert à 32 chunks.
|
||||
|
||||
## beta.186 — rosaces et huit refuges d'ancres
|
||||
|
||||
[Nouveau laboratoire](docs/hidden-anchors-beta186.md) : balcon supérieur sans
|
||||
barrières, liserés incrustés dans les deux dômes et huit ancres adaptées au terrain
|
||||
de leur secteur. Dépôts distincts reliés aux gemmes du Bugrock. Profil `anchors`,
|
||||
graine 42, nouvelle sauvegarde. Génération et réouverture vérifiées, 265 GameTests
|
||||
et assemblages réussis ; solo Vulkan ouvert à 32 chunks.
|
||||
|
||||
## beta.185 — tige fine et portail de toiture
|
||||
|
||||
[Variante de laboratoire](docs/fine-stem-beta185.md) : ligne de calcite d’un bloc,
|
||||
sans escalier ni paliers, fleur conservée, portail horizontal incrusté au sommet
|
||||
et jour fermé sous les vitres du palais. Profil `stem`, monde neuf, graine 42.
|
||||
265 GameTests, contrôles natifs et assemblages réussis ; solo Vulkan ouvert
|
||||
à 32 chunks, départ dans le palais supérieur.
|
||||
|
||||
## beta.184 — ascension naturelle
|
||||
|
||||
[Nouveau laboratoire](docs/natural-ascent-beta184.md) : rosace et Bugrock à Y=640,
|
||||
tige parcourable, palais vitré vers Y=1280 avec atlas au sol et portail horizontal.
|
||||
La couronne se découvre en montant. Les bassins supérieurs dessinés sont remplacés
|
||||
par le remplissage de creux existants. Profil `ascent`, nouveau monde uniquement.
|
||||
265 GameTests, contrôles natifs et assemblages réussis ; solo Vulkan ouvert à
|
||||
32 chunks, départ sur la rosace.
|
||||
|
||||
## beta.183 — lieux et ressources de l’île
|
||||
|
||||
[Nouveau laboratoire](docs/island-discovery-beta183.md) : portail horizontal,
|
||||
secteurs de gemmes, soufre repérable, bassin supérieur, ISS et rosace aux huit
|
||||
couleurs du Bugrock. ISS avec cartes horizontales, salle et mosaïque adaptées
|
||||
au rayon du terrain. Profil `discovery`, monde neuf ; contrôles natifs,
|
||||
265 GameTests et assemblages réussis. Solo préparé à 32 chunks.
|
||||
|
||||
## beta.182 — galerie inférieure et rosace
|
||||
|
||||
[Nouvelle scène de laboratoire](docs/origin-gallery-beta182.md) : galerie
|
||||
naturelle sous le centre, trois salles à plusieurs niveaux, emplacement
|
||||
d’obsidienne ; cloche vitrée au-dessus du Bug Rock et huit axes dégagés.
|
||||
Nouveau profil `gallery`, monde neuf, contrôles natifs, 265 GameTests et
|
||||
assemblages réussis. Solo à 32 chunks visité par le créateur, qui valide sa palette et sa rosace.
|
||||
|
||||
## beta.181 — origine à Y=320
|
||||
|
||||
[Premier repère central](docs/origin-landmark-beta181.md) : petite rose des vents
|
||||
ouverte sur un fragment rocheux, Bug Rock en (0,320,0), lumineux et monochrome.
|
||||
Nouveau profil de labo `origin`, fondé sur l’île beta.180 ; contrôle natif réussi
|
||||
et solo Vulkan ouvert à 32 chunks. Suite générale et assemblages réussis.
|
||||
Les salles d’ancres, secteurs de gemmes, soufre, portail et ISS sont cadrés
|
||||
comme suites à construire.
|
||||
|
||||
## beta.180 — bassins et donjon minier
|
||||
|
||||
[Variante de laboratoire](docs/adventure-ecology-beta180.md) : grands bassins
|
||||
avec débordements, trois cerisiers sur l’île, récifs cherry et automne.
|
||||
Donjon ramifié avec spawners et wagonnets à butin (diamants, émeraudes,
|
||||
rubis et saphirs), éclairage réduit et cornichons hors de l’eau retirés.
|
||||
Solo Vulkan ouvert à 32 chunks, contrôles ciblés, validation générale et
|
||||
assemblages réussis.
|
||||
|
||||
## beta.179 — grottes vivantes
|
||||
|
||||
[Essai de laboratoire](docs/living-caves-beta179.md) : quelques cerisiers aux
|
||||
sommets, fin des bordures rocheuses du plateau, poches lush en terrasses,
|
||||
marais à lucioles, chênes noirs et mycélium violet. Première version de
|
||||
complexes miniers et d’une cabane de sorcière souterraine. Monde neuf ;
|
||||
contrôle natif réussi et solo Vulkan ouvert à 32 chunks. Suite générale
|
||||
de validation et assemblages réussis.
|
||||
|
||||
## 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
|
||||
@@ -14,38 +373,38 @@ 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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
|
||||
@@ -53,39 +412,39 @@ 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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 (pack local)
|
||||
## 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
|
||||
@@ -2421,7 +2780,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.151 |
|
||||
| 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
|
||||
@@ -2450,7 +2809,7 @@ décrits dans [Validation](docs/testing.md).
|
||||
|
||||
Résultats :
|
||||
|
||||
- `mods/sanctuary/build/libs/sanctuary-beta.151.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).
|
||||
|
||||
@@ -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,46 @@
|
||||
# WG-ECO-180 — grands bassins et donjon minier
|
||||
|
||||
Suite du retour beta.179 : relief et écologie générale validés ; les petites
|
||||
mares répétées, l’absence de cerisiers et les mines rectilignes sont à corriger.
|
||||
Branche `codex/cave-dungeon-beta180`, nouveau preset de labo
|
||||
`sanctuary_test:adventure_ecology_v1`, profil `adventure`, graine 42.
|
||||
Aucune migration : uniquement un nouveau solo, vue 32, commandes activées.
|
||||
|
||||
Cibles : grands bassins irréguliers réunis par débordements ; arrêt des petites
|
||||
mares et des cornichons hors de l’eau ; trois cerisiers sur l’île principale,
|
||||
un récif cherry et un récif automnal (autres récifs nus, relief conservé).
|
||||
Un donjon ramifié avec salles, boucles, dénivelés, spawners et wagonnets à butin,
|
||||
plus un éclairage ponctuel. La cabane et les habitats souterrains sont conservés.
|
||||
|
||||
Implémentation : deux systèmes de grands bassins sur des sols de cavités,
|
||||
berges irrégulières préservant les reliefs émergents, fonds suivant la roche et
|
||||
bassins inférieurs quatre blocs plus bas. Les anciennes petites mares sont
|
||||
exclues de ce preset. Les trois cerisiers sont réservés et placés avec la
|
||||
fonction native ; les récifs 0 et 1 deviennent cherry et automne, les autres
|
||||
restent nus. La géométrie, les minerais et l’espace ISS sont conservés.
|
||||
|
||||
Donjon : 13 salles, embranchements et boucles, cinq spawners natifs
|
||||
(squelettes et araignées des cavernes pour cette graine), quatre wagonnets-coffres.
|
||||
Butin différé natif, contenant diamants, émeraudes, `sanctuary:ruby` et
|
||||
`sanctuary:sapphire`, plus des provisions. Pas d’emblème : clarification du
|
||||
créateur, il parlait bien des gemmes dans le butin. Une lanterne tous les
|
||||
quatre portiques environ, éclairage sur tonneau dans les caches.
|
||||
|
||||
Contrôle `solo180c`, graine 42 : trois vrais troncs de cerisiers vérifiés,
|
||||
deux surfaces d’eau connectées de 1 232 et 1 221 blocs, passages praticables,
|
||||
spawners et tirage des quatre gemmes vérifiés. La réouverture avec fluides
|
||||
actifs confirme les deux chutes d’eau ; chargements forcés retirés avant arrêt.
|
||||
Dernière correction ensuite : support des lanternes des caches. Contrôle
|
||||
final `solo180d` réussi, quatre wagonnets avec leur table de butin conservée
|
||||
en sauvegarde. Suite `check build assemblePack assembleTestPack` réussie
|
||||
(11 min 26 s, journal local `build/adventure180-check-build.log`).
|
||||
|
||||
Points de visite : cerisiers près de (38,301,101), grand bassin vers
|
||||
(-192,198,32), donjon vers (120,145,48), récifs thématiques aux mêmes
|
||||
emplacements que les récifs précédents. Nouveau solo `visite180/adventure/42`,
|
||||
commandes activées, vue 32, simulation 12, créatif et difficulté normale.
|
||||
Les premières proportions restent à juger en jeu ; aucune ancienne sauvegarde
|
||||
ni distribution personnelle modifiée.
|
||||
|
||||
Ouverture confirmée le 30 septembre à 00:28 : Vulkan sur Apple M1,
|
||||
KokaLab connecté, distance serveur 32 et simulation 12.
|
||||
@@ -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).
|
||||
@@ -0,0 +1,299 @@
|
||||
# Audit du regroupement de Sanctuary beta.203
|
||||
|
||||
Audit du 4 octobre 2026, demandé avant toute refonte. **Le regroupement est
|
||||
réalisable, mais Sanctuary Test contient maintenant une partie du jeu livré,
|
||||
et JEI constitue un moteur conséquent.** La voie recommandée est de conserver
|
||||
le comportement de la beta.203, absorber Demeure et les fonctionnalités du
|
||||
laboratoire, puis traiter le catalogue comme un chantier distinct.
|
||||
|
||||
Cette livraison contient une sauvegarde et un audit. Aucun regroupement de
|
||||
code, changement de génération, migration de monde ou déploiement n'a été
|
||||
effectué. Les travaux proposés ci-dessous restent à réaliser.
|
||||
|
||||
## État de référence et sauvegarde
|
||||
|
||||
La référence est le commit `454d9a5c08717af807f233759d3a96d639a39d44`, branche
|
||||
`codex/north-fracture-beta203`, dans
|
||||
`/Users/koka/.codex/worktrees/storyquest-beta173/sanctuary-beta`.
|
||||
Son état Git était propre. Les propriétés du mod et du pack déclarent
|
||||
`beta.203`, pour Minecraft 26.3, Java 25, Fabric Loader 0.19.5 et Fabric API
|
||||
0.160.5+26.3.
|
||||
|
||||
Le dossier principal ouvert au début de l'audit était encore sur `main`, en
|
||||
beta.168. La branche 203 possède 40 commits absents de ce `main` local. Cela
|
||||
explique le décalage initial ; le présent audit porte bien sur la 203.
|
||||
|
||||
Sauvegarde locale :
|
||||
`/Users/koka/Documents/sanctuary-backups/beta.203-before-single-mod-20261004-225340/`.
|
||||
|
||||
- Branche de sauvegarde : `codex/snapshot-beta203-before-single-mod-20261004-225340`.
|
||||
- `sanctuary-all-refs.bundle` : historique Git et références commises présentes
|
||||
au moment de la sauvegarde, dont la 203 et le `main` local.
|
||||
- `sanctuary-beta.203-sources.tar.gz` : fichiers suivis de la 203.
|
||||
- `jei-upstream-aae2dfc.tar.gz` : sources amont exactes nécessaires au port JEI ;
|
||||
le patch et le script de préparation sont dans les sources Sanctuary.
|
||||
- `artifacts/` : JAR existants de la 203, JAR de sources, MRpack Test, template
|
||||
graphique et guide. Le JAR Sanctuary contient également JEI et MariaDB.
|
||||
- Reçu de livraison et deux journaux de validation 203 ; empreintes SHA-256
|
||||
dans `manifest.json`, procédure dans `RESTORE.txt`.
|
||||
|
||||
Les 16 fichiers du manifeste ont été contrôlés. Le bundle a été vérifié, puis
|
||||
cloné dans un répertoire neuf : commit attendu retrouvé, copie restaurée
|
||||
propre. Les 4 628 fichiers de l'archive source ont également été comparés
|
||||
octet pour octet à la copie 203 d'origine. Le MRpack sauvegardé a pour SHA-256
|
||||
`ba160b4134c0b9dbe402e1834bdbec4f5561bfd6bd9dc147590dd76ee30dbf4f`, identique
|
||||
au [reçu documenté de la 203](north-fracture-beta203.md).
|
||||
|
||||
**Limite de sauvegarde :** la tentative de copie de la branche vers Gitea a
|
||||
échoué faute d'authentification HTTPS. Aucune sauvegarde distante nouvelle
|
||||
n'est confirmée. Cette sauvegarde locale du projet ne contient pas les mondes,
|
||||
réglages personnels, caches Gradle, essais ignorés ni changements non commis
|
||||
des autres worktrees. Elle ne constitue pas une installation complète hors ligne.
|
||||
|
||||
## Inventaire mesuré
|
||||
|
||||
Comptage des fichiers Java et de leurs lignes physiques, commentaires et
|
||||
lignes vides inclus. Les sources JEI sont celles préparées au commit fixé,
|
||||
dans les six répertoires effectivement compilés par son build.
|
||||
|
||||
| Ensemble | Java livré | Lignes Java | Responsabilité actuelle |
|
||||
| --- | ---: | ---: | --- |
|
||||
| Sanctuary | 963 fichiers | 84 353 | Terrain historique, progression, inventaires, recettes côté serveur, Atlas, communauté, rendu, expansions |
|
||||
| Demeure | 4 fichiers | 347 | Empreintes du vivant, activité et persistance pour l'Atlas |
|
||||
| Sanctuary Test | 105 fichiers | 8 775 | Nouveau terrain, palais et cavernes, ancres, adaptation des expansions, outils de labo |
|
||||
| JEI embarqué | 924 fichiers | 69 104 | Recherche, catalogue, écrans de recettes, favoris, catégories, synchronisation et transferts |
|
||||
|
||||
Sanctuary possède également 381 fichiers Java de tests, soit 60 380 lignes,
|
||||
répartis entre tests purs, GameTests serveur et tests client. Demeure et le
|
||||
module Test ont chacun un fichier de test supplémentaire. Le volume de tests
|
||||
ne prouve pas leur exécution sur chaque livraison.
|
||||
|
||||
Les configurations déclarent 286 mixins Sanctuary, un Demeure, sept Sanctuary
|
||||
Test et sept JEI. Ces injections dans Minecraft rendent l'ordre
|
||||
d'initialisation et les interactions client/serveur sensibles aux déplacements.
|
||||
|
||||
Le graphe de construction comprend quatre sous-projets. Sanctuary embarque
|
||||
Demeure, JEI et MariaDB dans son JAR ; JEI embarque deux bibliothèques d'indexation.
|
||||
Le MRpack 203 contient deux JAR de jeu au premier niveau, Sanctuary et Sanctuary
|
||||
Test, et télécharge Fabric API. L'inspection des archives confirme que les
|
||||
identités Fabric `demeure`, `jei` et `sanctuary_test` existent encore réellement.
|
||||
|
||||
Sources : [settings.gradle](../settings.gradle),
|
||||
[dépendances Sanctuary](../mods/sanctuary/build.gradle),
|
||||
[manifeste Sanctuary](../mods/sanctuary/src/main/resources/fabric.mod.json),
|
||||
[port JEI](../mods/jei/build.gradle).
|
||||
|
||||
## Les points qui rendent une fusion directe dangereuse
|
||||
|
||||
### Sanctuary Test porte le terrain utilisé dans la 203
|
||||
|
||||
Ce module n'est plus seulement un moyen de sauter l'introduction dans un monde
|
||||
plat. Il fournit 46 presets, 44 paramètres de bruit, 93 biomes et 13
|
||||
enregistrements de codecs. Son adaptateur `LabExpansions199` branche le terrain
|
||||
récent sur le journal, les réservations et la file de génération de Sanctuary.
|
||||
|
||||
Il existe deux fichiers différents à l'emplacement
|
||||
`data/sanctuary/worldgen/world_preset/sanctuary.json`. Celui de Sanctuary
|
||||
sélectionne `sanctuary:island_v24`. Celui du module Test sélectionne le terrain
|
||||
natif du laboratoire, avec `sanctuary_test:ascent_1600` et les biomes du labo.
|
||||
Une copie avec écrasement implicite pourrait donc changer le terrain proposé
|
||||
par le bouton Sanctuary. Le tag des presets publics présente aussi une collision.
|
||||
|
||||
Les classes `NorthTerrain200` à `NorthTerrain203`, déjà dans Sanctuary,
|
||||
référencent des biomes `sanctuary_test:*` fournis par l'autre module : la frontière
|
||||
des fonctionnalités ne suit pas celle des sous-projets Gradle.
|
||||
|
||||
**Conséquence :** transférer explicitement le terrain et ses ressources, choisir
|
||||
un seul preset public, conserver les anciennes clés de registre, puis déplacer
|
||||
les outils de mesure vers des sources de développement. Le nom du mod peut
|
||||
disparaître sans renommer immédiatement tous les identifiants de ses ressources.
|
||||
Les numéros historiques ne suffisent pas à identifier du code supprimable.
|
||||
|
||||
Sources : [initialisation du labo](../mods/sanctuary-test/src/main/java/fr/koka/sanctuarytest/WorldgenLab.java),
|
||||
[adaptateur des expansions](../mods/sanctuary-test/src/main/java/fr/koka/sanctuarytest/LabExpansions199.java),
|
||||
[preset livré par le labo](../mods/sanctuary-test/src/main/resources/data/sanctuary/worldgen/world_preset/sanctuary.json),
|
||||
[biomes du Nord 203](../mods/sanctuary/src/main/java/fr/koka/sanctuary/expansion/NorthTerrain203.java).
|
||||
|
||||
### Les raccourcis du labo ne doivent pas devenir les règles ordinaires
|
||||
|
||||
`QuickTestMod` active le pont pour mondes plats et enregistre plusieurs outils
|
||||
de test. Son paquet de configuration peut demander de sauter l'introduction
|
||||
pour les mondes du labo. `QuickTestClient` intervient dans la création de monde.
|
||||
`SkySurvey175` exige même un conteneur Fabric nommé `sanctuary_test`.
|
||||
|
||||
Absorber aveuglément ces points d'entrée étendrait les comportements de labo
|
||||
au mod principal, ou laisserait des outils dépendre d'une identité supprimée.
|
||||
Il faut séparer les services nécessaires au jeu, les options explicites de
|
||||
développement et les tests, puis n'enregistrer chaque événement qu'une fois.
|
||||
|
||||
Sources : [QuickTestMod](../mods/sanctuary-test/src/main/java/fr/koka/sanctuarytest/QuickTestMod.java),
|
||||
[QuickTestClient](../mods/sanctuary-test/src/main/java/fr/koka/sanctuarytest/QuickTestClient.java),
|
||||
[activation du gameplay](../mods/sanctuary/src/main/java/fr/koka/sanctuary/progression/SanctuaryGameplay.java).
|
||||
|
||||
### Demeure est petit mais ses données doivent garder leur adresse
|
||||
|
||||
Seuls deux fichiers Java de production Sanctuary référencent directement
|
||||
son package : l'initialisation et le paquet Atlas. L'absorption est donc
|
||||
relativement simple : service interne, événements, mixin de placement et tests.
|
||||
|
||||
Il faut conserver `data/demeure/footprints-v1.json`, son schéma 1, la graine et
|
||||
les protections contre la corruption ou le remplacement externe. Renommer
|
||||
ce chemin pendant la fusion ferait apparaître un historique vide. Préserver
|
||||
aussi le filtre d'éligibilité fixé par Sanctuary et éviter un double comptage
|
||||
si l'ancien JAR Demeure reste installé.
|
||||
|
||||
Sources : [service Demeure](../mods/demeure/src/main/java/fr/koka/demeure/DemeureService.java),
|
||||
[écriture atomique](../mods/demeure/src/main/java/fr/koka/demeure/AtomicJson.java).
|
||||
|
||||
### JEI est le principal choix de conception
|
||||
|
||||
Sanctuary ne référence directement les classes JEI que dans deux fichiers
|
||||
de production, `RecipeClient` et `SanctuaryJeiPlugin`. Cette frontière limitée
|
||||
est favorable à un remplacement. Elle masque toutefois un moteur de 924
|
||||
fichiers Java, avec un patch local de 1 491 lignes touchant 41 fichiers.
|
||||
|
||||
JEI fournit aussi des services serveur : transferts d'ingrédients,
|
||||
sérialiseurs et synchronisation des recettes. Son démarrage dépend des points
|
||||
d'entrée Fabric, de mixins, d'un access widener et de services Java. Effacer
|
||||
son manifeste ne constitue donc pas une intégration fonctionnelle.
|
||||
|
||||
Deux trajectoires sont possibles :
|
||||
|
||||
- **Internaliser le moteur dérivé de JEI.** Faire disparaître le mod distinct,
|
||||
adapter son démarrage et conserver ses comportements. Risque fonctionnel
|
||||
plus limité, mais environ 69 000 lignes tierces restent à maintenir, avec
|
||||
leurs crédits, licences, ressources et conventions.
|
||||
- **Construire le catalogue Sanctuary.** Remplacer progressivement le moteur
|
||||
derrière une interface interne. C'est le meilleur moyen de réduire la
|
||||
dépendance à JEI, avec davantage de développement et de validation.
|
||||
|
||||
La seconde trajectoire doit couvrir la recherche, recettes/usages, favoris,
|
||||
historique, fluides, cuisson, alchimie et autres catégories réellement utilisées,
|
||||
les recettes de datapacks, les interfaces de conteneurs et les transferts avec
|
||||
inventaire étendu. Les règles d'apprentissage, recettes cachées, découvertes et
|
||||
révocation opérateur restent gouvernées par les services serveur Sanctuary.
|
||||
L'interface native seule ne suffit pas à remplacer ce contrat.
|
||||
|
||||
Sources : [RecipeClient](../mods/sanctuary/src/main/java/fr/koka/sanctuary/client/RecipeClient.java),
|
||||
[pont JEI](../mods/sanctuary/src/main/java/fr/koka/sanctuary/client/SanctuaryJeiPlugin.java),
|
||||
[contrat des recettes](recipes-jei-beta024.md),
|
||||
[provenance JEI](../mods/jei/PROVENANCE.md).
|
||||
|
||||
### Le build et la distribution imposent encore plusieurs mods
|
||||
|
||||
`scripts/pack.py` exige explicitement les JAR imbriqués Demeure et JEI,
|
||||
leurs versions, licences et empreintes. `scripts/test_pack.py` exige un JAR
|
||||
`sanctuary_test`. Les scripts de lancement dépendent de ses tâches Gradle
|
||||
et de ses presets. Tous ces contrats devront évoluer dans les mêmes tickets
|
||||
que les modules concernés, en gardant les vérifications d'intégrité.
|
||||
|
||||
Un seul mod de jeu Sanctuary peut continuer à utiliser Fabric Loader,
|
||||
Fabric API et des bibliothèques techniques. L'exigence plus stricte « aucune
|
||||
autre entrée Fabric, même technique » serait un périmètre différent : Fabric
|
||||
API et certaines bibliothèques imbriquées possèdent elles-mêmes des métadonnées.
|
||||
Elle n'est pas nécessaire pour réunir les fonctionnalités demandées.
|
||||
|
||||
Sources : [assemblage du pack](../scripts/pack.py),
|
||||
[assemblage Test](../scripts/test_pack.py),
|
||||
[lancement du terrain](../scripts/worldgen_lab.py).
|
||||
|
||||
## Nettoyage utile du dépôt
|
||||
|
||||
Le découpage interne de Sanctuary existe déjà : progression, inventaire,
|
||||
recettes, communauté, rendu, monde, expansions, etc. Le conserver évite qu'un
|
||||
mod unique devienne une classe unique. Le terrain représente à lui seul
|
||||
35 599 lignes Java dans Sanctuary, avec des générations historiques enregistrées.
|
||||
|
||||
Les premiers gains seraient de clarifier la référence de développement,
|
||||
d'extraire les paramètres et tâches répétitifs du `build.gradle` Sanctuary
|
||||
(1 355 lignes), et de transformer le README de 2 881 lignes en porte d'entrée
|
||||
vers un historique séparé. Certains passages annoncent toujours beta.166 et
|
||||
le README Demeure indique encore 26.3-pre-2 ; les propriétés de build et le
|
||||
ticket 203 sont les références utilisées ici.
|
||||
|
||||
Il y avait 48 worktrees avant celui de cet audit. Ce nombre augmente le risque
|
||||
de travailler sur une ancienne version, comme l'a montré le décalage 168/203.
|
||||
Les archiver demandera de distinguer les branches terminées des travaux encore
|
||||
actifs. Aucun de ces worktrees n'a été supprimé par cet audit. Les gros dossiers
|
||||
`build/` ignorés ne sont pas assimilés à des sources mortes à effacer.
|
||||
|
||||
## Découpage proposé en tickets vérifiables
|
||||
|
||||
| Ticket proposé | Résultat attendu | Vérification déterminante |
|
||||
| --- | --- | --- |
|
||||
| UNI 01 Référence et contrats | Branche issue de 203, inventaire des clés persistantes, fonctions de catalogue et ressources en collision | Reconstruction depuis la sauvegarde, référence de tests documentée, exemplaires de mondes de développement |
|
||||
| UNI 02 Demeure interne | Plus de mod Demeure séparé ; empreintes et Atlas inchangés | Comptage unique des événements, relecture du fichier schéma 1, refus de corruption |
|
||||
| UNI 03 Terrain dans Sanctuary | Terrain 203, Nord, palais et relais disponibles avec Sanctuary seul | Création Small/Medium/Large, graines 0/42/4736390610738281858, reprise d'expansion et rechargement |
|
||||
| UNI 04 Outils de développement séparés | Plus de mod Sanctuary Test ; tâches de labo explicites et sources de test | Parcours normal sans raccourcis d'introduction, parcours labo opt-in, serveur dédié et client Vulkan |
|
||||
| UNI 05 Catalogue interne | Interface indépendante de JEI, puis internalisation ou remplacement selon le périmètre retenu | Recherche, usages, favoris, filtres de découverte et transferts réels en solo et à deux |
|
||||
| UNI 06 Distribution et documentation | Un artefact de jeu Sanctuary, installation existante débarrassée des anciens JAR, documentation actuelle | `check build`, `assemblePack`, contenu des archives et essai d'installation isolé |
|
||||
|
||||
Chaque ticket reçoit une branche `codex/<sujet>`. Les livraisons binaires
|
||||
incrémentent la prochaine version disponible ; cet audit documentaire reste
|
||||
sur beta.203. Les étapes peuvent être rapprochées, mais chacune doit laisser
|
||||
un résultat vérifiable avant la suivante.
|
||||
|
||||
Pour les mondes existants, établir d'abord un contrat de compatibilité : garder
|
||||
les codecs et noms `sanctuary:*` et `sanctuary_test:*`, les schémas et chemins,
|
||||
notamment `sanctuary-lab-expansions<révision>`. Vérifier sur des copies de
|
||||
développement les constructions, progression, recettes, empreintes et expansions
|
||||
en attente. Un changement de nom public ou de package Java ne justifie pas
|
||||
une régénération de chunks. Aucun essai sur un monde personnel n'est requis
|
||||
pour commencer ces tickets.
|
||||
|
||||
## Difficulté estimée
|
||||
|
||||
Estimation de travail concentré pour une personne connaissant le dépôt,
|
||||
incluant vérification et corrections ; ce ne sont pas des durées mesurées.
|
||||
|
||||
| Périmètre | Ordre de grandeur | Incertitude principale |
|
||||
| --- | --- | --- |
|
||||
| Demeure absorbé | 0,5 à 1 jour | Persistance et absence de double enregistrement |
|
||||
| Terrain et fonctions Test absorbés, outils séparés | 3 à 5 jours | Anciennes clés, ressources en collision, chargement et reprise |
|
||||
| JEI internalisé avec son moteur conservé | 2 à 4 jours | Initialisation, services, ressources et transferts |
|
||||
| Catalogue propre remplaçant les usages actuels de JEI | 10 à 20 jours | Étendue exacte des catégories, confort d'usage et parité multijoueur |
|
||||
|
||||
Avec la remise au propre du build et de la distribution, compter **environ
|
||||
une à deux semaines pour une unification conservatrice**, et **trois à cinq
|
||||
semaines pour un ensemble nettoyé avec remplacement du catalogue**. Une
|
||||
équivalence avec toutes les intégrations tierces possibles de JEI dépasserait
|
||||
ce périmètre. La phase UNI 01 doit resserrer ces estimations.
|
||||
|
||||
Ma recommandation est d'absorber Demeure puis le terrain Test, en conservant
|
||||
les formats, et d'encapsuler immédiatement le catalogue avant de le remplacer.
|
||||
Renommer tous les identifiants et réécrire la génération dans cette même passe
|
||||
augmenterait le risque sans être nécessaire à l'objectif d'un mod unique.
|
||||
|
||||
## Vérifications et limites de cet audit
|
||||
|
||||
Sauvegarde et restauration vérifiées, archives existantes inspectées, graphe de
|
||||
dépendances et ressources en collision contrôlés. La copie d'audit possède sa
|
||||
branche `codex/audit-single-mod-beta203`, distincte des branches de jeu.
|
||||
|
||||
Une exécution ciblée a été réalisée dans cette copie isolée :
|
||||
|
||||
```sh
|
||||
./gradlew --no-daemon check build \
|
||||
-PsanctuaryFocusedTests=demeure,recipes,operator,realtime \
|
||||
-PsanctuaryAtlasOnly=true
|
||||
```
|
||||
|
||||
**Résultat : BUILD SUCCESSFUL en 2 min 57 s ; 140 tâches, dont 139 exécutées,
|
||||
et 15/15 GameTests requis réussis.** Les tests numériques rattachés à `check`,
|
||||
la compilation des quatre modules, les contrôles du manifeste pack et des
|
||||
ressources ont également passé. Journal : `build/audit-beta203-check-build.log`.
|
||||
Le cache des sources JEI a été copié depuis le worktree 203, puis contrôlé
|
||||
par le script de préparation ; ce n'est pas une reconstruction sans caches.
|
||||
L'assemblage des packs n'a pas été relancé dans cet audit : les archives
|
||||
203 existantes ont été inspectées et sauvegardées.
|
||||
|
||||
La [livraison 203](north-fracture-beta203.md) documente sept GameTests ciblés,
|
||||
les tests numériques et un essai natif North Small/42. La suite historique
|
||||
complète n'est plus validée depuis la [limite décrite en 200](north-expansion-beta200.md),
|
||||
où une exécution a été interrompue pendant la préparation d'un test hydrologique.
|
||||
Cet arrêt ne démontre pas un interblocage. Les succès antérieurs sur 265 tests
|
||||
ne constituent pas une validation de la 203.
|
||||
|
||||
Cet audit n'est pas un test visuel, un essai Windows, une validation des
|
||||
performances ou une preuve de migration des sauvegardes. Aucun client graphique
|
||||
n'a été lancé ; les futures validations graphiques ciblent uniquement Vulkan.
|
||||
+151
@@ -1,5 +1,156 @@
|
||||
# Backlog Sanctuary
|
||||
|
||||
## WG-RESTORE-191 — annuler les plateaux et leurs affleurements
|
||||
|
||||
La visite beta.190 ne valide pas le relief corrigé. [Retour exact à la base
|
||||
beta.188](restore-relief-beta191.md), pour la densité et la géologie naturelle.
|
||||
Conserver les aménagements indépendants ; réserver les futures singularités
|
||||
de relief à quelques endroits. Nouveau profil `original` ; 134 480 densités
|
||||
comparées à l’identique, génération/réouverture et 265 GameTests réussis.
|
||||
|
||||
## WG-NATURE-190 — revenir au relief organique
|
||||
|
||||
Retour beta.189 : [contrat beta.190](natural-refinement-beta190.md). Relief local
|
||||
adouci, sortie d’étang large et proche, coffres de fer enchanté dans les ruines.
|
||||
Profil neuf de laboratoire ; génération, réouverture, 265 GameTests et assemblages
|
||||
validés. Solo Vulkan ouvert à 32 chunks.
|
||||
|
||||
## Réserve d’expansion — grandes surfaces ciselées
|
||||
|
||||
Conserver la variante de terrasses beta.189 comme piste pour une autre expansion :
|
||||
grands plateaux, falaises étagées, profondeurs et verticalité variables selon
|
||||
l’identité de l’île. Ne plus appliquer ces niveaux partout sur Sanctuary Island.
|
||||
Aucune expansion activée. Le portail à remplissage avec ressource renouvelable
|
||||
reste à concevoir ; les perles d’Ender sont une possibilité, pas une décision.
|
||||
|
||||
## 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).
|
||||
|
||||
@@ -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,85 @@
|
||||
# WG-ERODED-192 — couronnes et faces du massif
|
||||
|
||||
Branche `codex/eroded-massif-beta192`, Minecraft 26.3, graine de visite 42.
|
||||
Profil `eroded`, preset neuf `sanctuary_test:eroded_massif_v1`.
|
||||
|
||||
## Retour et résultat attendu
|
||||
|
||||
Après la restauration beta.191, les captures du 30 septembre à 22:02–22:05
|
||||
montrent des marches de terre et d’herbe sur les sommets arrondis. Le créateur
|
||||
souhaite de vrais changements de forme : quelques dessus plus plats sous les
|
||||
arbres, des faces plus franches, des creux d’érosion et des pentes conservées.
|
||||
Ne pas reproduire les terrasses périodiques 189 ou simplement peindre la roche.
|
||||
|
||||
## Variante
|
||||
|
||||
Trois couronnes sont choisies dans les points hauts du massif existant. Leur
|
||||
altitude dépend du terrain original. Les trois cerisiers réservés occupent ces
|
||||
couronnes, avec exclusion des points de plantation encore trop raides. Une coupe légèrement inclinée rabote le
|
||||
sommet ; une face plus raide regarde son versant descendant. Les empreintes
|
||||
ont des limites adoucies et irrégulières ; les petites entailles entre couronnes
|
||||
s’appuient sur la distance aux deux centres de Voronoï les plus proches.
|
||||
Les coupes retirent au plus 24 blocs du plafond géométrique local. Une cavité
|
||||
préexistante découverte par la coupe peut donner un sol visible plus bas.
|
||||
|
||||
C’est une érosion géométrique locale sur le bruit 3D existant, sans simulation
|
||||
physique. Pas de grille de niveaux Y, de remplissage des cavités ni de lissage
|
||||
global. Aucun changement de densité à Y≤240, dans les îlots aériens ou hors du
|
||||
massif central. Les formes originales restent la base sous le plafond sculpté.
|
||||
|
||||
La couverture végétale du massif n’est posée que sur les surfaces capables de
|
||||
la porter : les fortes pentes gardent la géologie réelle (stone, gisements,
|
||||
strates). Le gradient est mesuré sur des colonnes du massif en coordonnées
|
||||
monde, traversant les limites des chunks et ignorant les îlots aériens. Les
|
||||
hauteurs et pentes sont mises en cache dans un domaine borné. Cette variante
|
||||
n’utilise pas le solveur d’hydrologie ; les étangs et déversoirs existants restent
|
||||
responsables de l’eau.
|
||||
|
||||
Ancres, donjon, gemmes, soufre, ruines à coffres, galerie, rosace et palais sont
|
||||
conservés. La règle de roche historique garde son comportement lorsque son
|
||||
nouveau champ optionnel `slope` est absent. Les anciens presets gardent leurs
|
||||
paramètres ; aucun monde existant n’est modifié ou régénéré.
|
||||
|
||||
## Vérifications et limites
|
||||
|
||||
Génération native finale et réouverture réussies, graine 42 :
|
||||
`build/eroded192e-cold.json` et `build/eroded192e-warm.json`.
|
||||
Les hauteurs mesurées sont identiques après rechargement ; les trois troncs
|
||||
sont relus dans les chunks sauvegardés.
|
||||
|
||||
- 1 008 colonnes modifiées dans le relevé espacé de deux blocs ; 392 échantillons
|
||||
de sommet peu pentus. Trois couronnes et trois cerisiers contrôlés en blocs réels.
|
||||
- 39 366 densités profondes et aériennes identiques à la base ; comparaison
|
||||
supplémentaire hors du massif. Aucun niveau Y périodique réintroduit.
|
||||
- 1 545 échantillons rocheux contre 8 de terre/herbe parmi les fortes pentes
|
||||
contrôlées. Les plantations trouvent un sol de pente ≤0,75 dans leur chunk.
|
||||
- Ancres, 49 cartes/cadres, rosaces, galerie, quatre coffres de ruines, donjon,
|
||||
gemmes, soufre et étangs contrôlés. Cascade large et absence d’arbres dans
|
||||
les colonnes d’eau et de berge contrôlées conservées.
|
||||
|
||||
Démarrage final : 12,126 s à froid et 0,613 s à chaud, zéro région d’hydrologie ;
|
||||
83,610 s à froid et 18,501 s à chaud avec
|
||||
l’ensemble des contrôles et le remplissage de l’atlas. Ces contrôles ne tournent
|
||||
pas au lancement du solo. Les mesures intermédiaires pendant la compilation
|
||||
concurrente ne servent pas de référence de performance.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi en 9 min 9 s,
|
||||
265 GameTests réussis. Après ajustement du placement des cerisiers et de son
|
||||
contrôle de réouverture, `:sanctuary-test:check :sanctuary-test:build` réussi et
|
||||
pack de test réassemblé : 175 classes et 121 ressources vérifiées dans le JAR,
|
||||
identique à sa copie distribuée (`build/eroded192-pack-receipt.json`).
|
||||
|
||||
La validation technique ne vaut pas validation esthétique du créateur. Cette
|
||||
itération vise le massif central de la graine de visite ; elle ne remodèle pas
|
||||
les récifs aériens. Pas de publication ni de mise à jour de l’installation Prism.
|
||||
|
||||
## Visite
|
||||
|
||||
Nouveau solo `Sanctuary-Eroded-Massif-192-Solo`, run `visite192`, profil `eroded`,
|
||||
graine 42, vue 32 et simulation 12, spectateur et commandes autorisées.
|
||||
Position préparée (94,316,153), face au massif. Les 49 cartes et cadres ainsi
|
||||
que les huit ancres éteintes et l’origine sans relais ont été relus dans cette
|
||||
copie neuve. Options et shader repris du solo 191, fermé et sauvegardé à 22:14.
|
||||
Client Vulkan lancé et entrée dans le monde confirmée à 22:34 ; vue 32,
|
||||
simulation 12 et visibilité de l’ascension contrôlées dans
|
||||
`build/eroded192-solo.log`.
|
||||
@@ -0,0 +1,83 @@
|
||||
# STEM-185 — ligne fine, portail de toiture et vitres reliées
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/fine-stem-beta185`.
|
||||
Le créateur valide la fleur et le palais beta.184, ainsi que la galerie inférieure.
|
||||
Il demande de supprimer l'escalier, les paliers et la grosse colonne : une ligne
|
||||
d'un bloc doit guider visuellement le bateau-poule vers le palais. Compléments
|
||||
pendant la réalisation : incruster le portail horizontal au sommet du dôme,
|
||||
libérer l'espace au-dessus de la carte, fermer le jour sous les vitres supérieures.
|
||||
|
||||
## Contrat
|
||||
|
||||
Nouveau profil de labo `stem`, preset `sanctuary_test:fine_stem_v1`, réglages
|
||||
`sanctuary_test:fine_stem_v1_10`, graine de visite **42**, version **beta.185**.
|
||||
La dimension 1600, le champ de bruit 640, l'île et les eaux retenues restent
|
||||
ceux de beta.184. Aucun changement de format, d'ancien preset ou de sauvegarde.
|
||||
La visite beta.184 a été arrêtée proprement à la demande du créateur ; une
|
||||
nouvelle sauvegarde est préparée pour la relance.
|
||||
|
||||
## Résultat
|
||||
|
||||
- Rosace et Bugrock toujours en (0,640,0), palette et huit axes conservés.
|
||||
- **Une seule colonne de calcite en (0,Y,0)** au-dessus du dôme inférieur,
|
||||
de Y=652 jusqu'au départ de l'évasement à 1160. Pas de spirale, de marches,
|
||||
de plateforme intermédiaire, de balustrade ou de pont d'escalier.
|
||||
- Les huit nervures s'élargissent progressivement près du sommet, en partant
|
||||
du même bloc central. Fleur, balcon et pièce du palais à Y=1278 conservés.
|
||||
- Portail horizontal 7×7, ouverture 5×5, **incrusté à Y=1309 dans le dôme**.
|
||||
Son cadre remplace les vitres sommitales et touche la toiture sur tout son
|
||||
pourtour extérieur. Le passage central traverse la voûte ; plus de cadre
|
||||
suspendu au milieu de la salle, au-dessus des 49 cartes au sol.
|
||||
- Un soubassement de calcite ferme le bloc d'air sous les panneaux de verre.
|
||||
Les huit passages restent ouverts. Leur dégagement est vérifié avec les
|
||||
collisions natives, pour une coque standard de 1,375 bloc et son pilote.
|
||||
- Les moteurs du bateau-poule existant ne comportent pas de plafond fixe à
|
||||
640. Aucun changement de mécanique de vol ni de comportement de familier.
|
||||
|
||||
La révélation de la couronne en montant reprend beta.184. La galerie inférieure,
|
||||
son portail 5×5, le soufre, les secteurs miniers, les étangs et le donjon restent
|
||||
ceux validés. Les portails restent des infrastructures, sans voyage dimensionnel.
|
||||
|
||||
## Vérifications et visite
|
||||
|
||||
Contrôle natif final `solo185c/stem/42` réussi : **508 tranches d'un seul bloc**,
|
||||
16 approches libres (huit par lieu, coque et pilote légèrement au-dessus des
|
||||
tapis de mousse), 104 colonnes vitrées appuyées sur calcite, 24 blocs de cadre
|
||||
reliés à la toiture. Les 16 868 entrées du plan correspondent aux blocs générés.
|
||||
La voûte hors de l'ouverture centrale reste identique à beta.184. Aucune marche
|
||||
ni palier dans cette variante. Reçu : `build/stem185-quick-result.json`.
|
||||
|
||||
L'île conserve ses 567 échantillons de densité vérifiés, zéro région d'hydrologie,
|
||||
les trois cerisiers, cinq spawners, quatre wagonnets à butin, la galerie, le soufre
|
||||
et les trois secteurs de gemmes. Le serveur démarre en 5,2 s ; préparation avec
|
||||
sondages, atlas et contrôles en 42,3 s. Ce dernier temps inclut les tests.
|
||||
|
||||
Lecture NBT du monde arrêté : 49 cartes complètes et figées, cadres horizontaux
|
||||
et identifiants uniques. Ancien cadre suspendu en air, nouveau cadre de toiture
|
||||
en obsidienne, tige centrale isolée et soubassement du vitrage confirmés.
|
||||
Reçus : `build/stem185-saved-atlas.json`, `build/stem185-saved-blocks.json`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack :sanctuary-test:exportDuoLaunch`
|
||||
réussi en **7 min 3 s**, **265/265 GameTests**. Journal :
|
||||
`build/stem185-check-build.log`. Le daemon de compilation a été arrêté pour
|
||||
libérer la mémoire avant le client Vulkan.
|
||||
|
||||
Solo Vulkan ouvert le 30 septembre à **07:32:51**. Nouveau monde
|
||||
`visite185/stem/42`, `Sanctuary-Fine-Stem-185-Solo`, créatif, commandes et vol,
|
||||
vue 32 chunks et simulation 12. Départ préparé dans le palais supérieur.
|
||||
Entrée confirmée en (6.5,1280,8.5), vue 32 et simulation 12 dans le journal
|
||||
`build/stem185-solo.log`. Vérification native du frustum réussie à 07:32:53 :
|
||||
couronne cachée depuis le sol, révélée en arrivant à la rosace. Inspection de
|
||||
la fenêtre du jeu : carte au sol présente et vitrages reliés au soubassement.
|
||||
L'ancienne sauvegarde beta.184 reste conservée. Aucun canal public ou instance
|
||||
Prism modifié ; seuls les artefacts locaux et le laboratoire sont livrés.
|
||||
Les contrôles de passage ne
|
||||
remplacent pas un trajet intégral piloté en bateau-poule.
|
||||
|
||||
| Lieu | Téléportation |
|
||||
| --- | --- |
|
||||
| Rosace du Bugrock | `/tp 0.5 639 5.5 180 -20` |
|
||||
| Palais et carte au sol | `/tp 6.5 1280 8.5 140 35` |
|
||||
| Portail dans la toiture | `/tp 0.5 1312 0.5 180 75` |
|
||||
| Liaison fine, vue extérieure | `/tp 24 920 24 135 -20` |
|
||||
| Portail inférieur | `/tp 0.5 177 7.5 180 30` |
|
||||
@@ -0,0 +1,101 @@
|
||||
# WG-GALLERY-198 — création Large interrompue par la galerie centrale
|
||||
|
||||
Branche `codex/gallery-generation-beta198`, depuis beta.197. Minecraft 26.3,
|
||||
Fabric 0.19.5, Java 25. Retour R013 et journal `message-11.txt` fourni par le
|
||||
créateur : arrêt Windows à la création Large, graine `4736390610738281858`.
|
||||
|
||||
## Cause et correction
|
||||
|
||||
L'exception `No supported central gallery floor` dans `Cavern183.create`
|
||||
interrompt la décoration du premier chunk. Le repli beta.197 exige encore de
|
||||
la roche exactement en (0,0), sous la surface de cette même colonne. Sur cette
|
||||
graine, son sommet n'est qu'à Y=168 et presque toute la colonne traverse du
|
||||
vide, alors que les colonnes voisines possèdent des assises. L'erreur a été
|
||||
reproduite sur macOS avec la même graine avant modification. Ce journal ne
|
||||
montre pas une panne du pilote Vulkan ni un manque de mémoire.
|
||||
|
||||
Les nouveaux profils beta.198 conservent d'abord toutes les recherches
|
||||
existantes. Un dernier repli ne s'exécute que lorsqu'elles échouent : il compare
|
||||
l'assise et la couverture sur l'emprise de la chambre, de Y=32 à Y=276, sans
|
||||
condition éliminatoire sur la seule colonne centrale. Au plus 12 250 sondes,
|
||||
une seule planification mise en cache par source de biomes. Même un puits
|
||||
entièrement vide conserve la salle et sa fondation locale existante, plutôt
|
||||
que d'annuler la création du monde. Dans ce cas limite, la chambre peut être
|
||||
ouverte sur le vide : ce correctif ne prétend pas créer une cavité fermée dans
|
||||
une roche absente.
|
||||
|
||||
Le portail reste horizontal en X=0, Z=0. Les dimensions de la chambre,
|
||||
maçonnerie et règles du relief, de l’eau, des biomes et des minerais restent
|
||||
identiques.
|
||||
Aucun calcul régional d'hydrologie ajouté.
|
||||
|
||||
## Sauvegardes et distribution
|
||||
|
||||
`stable_gallery198` est un paramètre de codec additif, faux par défaut.
|
||||
Les trois nouveaux profils `shared_island198_{small,medium,large}` et le preset
|
||||
public Sanctuary l'activent. Les profils 196/197 restent présents et gardent
|
||||
leur comportement ; leur personnalisation conserve leur révision exacte.
|
||||
Les protections de spawn introduites en 197 restent actives en 197 et 198.
|
||||
|
||||
Le pack de test est destiné à un **nouveau monde**. Une création interrompue
|
||||
beta.197 ne migre pas automatiquement en beta.198 : recréer un nouveau monde
|
||||
avec la même graine et la taille souhaitée. Aucun monde personnel modifié,
|
||||
aucun chunk régénéré, aucune publication du canal ni installation Prism.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- Échec beta.197 reproduit dans `build/worldgen-lab/gallery198-baseline/`.
|
||||
- Tests synthétiques réussis : puits vide, appuis autour d'un centre vide,
|
||||
fine couche haute, roche pleine, déterminisme et borne de calcul.
|
||||
- Matrice native réussie : Small/Medium/Large, graines 0, 17, 42,
|
||||
`4736390610738281858` et `-9223372036854775808`. Plans répétés à l'identique ;
|
||||
toutes les galeries admises par beta.197 conservent exactement leur plan.
|
||||
Le nouveau repli s'active pour la graine du journal, dans les trois tailles.
|
||||
- Small, Medium et Large sur la graine fournie : création puis réouverture
|
||||
réussies. Portail en `(0,183,0)`, trois salles accessibles, respectivement
|
||||
1 927, 1 925 et 1 930 cellules accessibles ; géométrie et ornements identiques
|
||||
après reprise. Démarrages serveur à froid : 18,4 / 24,2 / 21,1 secondes ;
|
||||
ces mesures locales ne sont pas un temps de chargement garanti sous Windows.
|
||||
- Client intégré **Vulkan**, vue 8 chunks et mémoire Java 2 Gio : Large sur
|
||||
la graine exacte, arrivée réelle en `(-15.5,254,-15.5)`, huit recherches
|
||||
natives de spawn sur l’île. Parcours FR/EN des trois tailles, annulation,
|
||||
aller-retour disque et maintien des révisions historiques 196 et 197.
|
||||
Réussi en 1 min 2 s, trace `build/gallery198-client.log`.
|
||||
|
||||
Commande du contrôle ciblé, rejouable dans un nouveau dossier :
|
||||
|
||||
```sh
|
||||
python3 scripts/worldgen_lab.py verify --profile large \
|
||||
--seed 4736390610738281858 --run galerie-regression-neuve \
|
||||
--checks gallery198 --memory 2048
|
||||
```
|
||||
|
||||
Les traces finales sont dans `build/worldgen-lab/gallery198-targeted/`.
|
||||
Le mode normal `--checks full` conserve toutes ses assertions. Son premier
|
||||
passage dans `gallery198-fixed/` a démarré correctement puis s'est arrêté sur
|
||||
un **autre contrôle de laboratoire** : le secteur émeraude contient 171 blocs,
|
||||
dont 120 exposés, contre le minimum attendu de 200. Ce seuil est uniquement
|
||||
vérifié par le banc de test, pas pendant une partie ordinaire. Ni l'assertion
|
||||
ni les minerais ne sont modifiés ici. La vérification complète de toutes les
|
||||
décorations sur cette graine reste donc ouverte ; seuls les contrôles ciblés
|
||||
explicitement indiqués sont déclarés réussis.
|
||||
|
||||
Aucun essai effectué directement sous Windows ; le défaut du journal est
|
||||
reproduit avec le même générateur natif et la même graine sur cette machine.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -PsanctuaryFocusedTests=menus`
|
||||
réussi en **4 min 27 s**, 139 tâches : contrôles purs dont le nouveau repli,
|
||||
quatre GameTests menus et assemblages. Journal : `build/gallery198-check-build.log`.
|
||||
Le parcours client utilise une instance de développement et ne produit pas de
|
||||
campagne de captures.
|
||||
|
||||
Export local **Sanctuary-Test-beta.198.mrpack**, **12 282 084 octets**.
|
||||
SHA-256 : `647498a0da3330ce0483e65fe6510ec8d09b6975365261fe59a57d61f3ef0688`.
|
||||
Archive ZIP, versions, dépendances et classes de chaque JAR vérifiées contre
|
||||
le build. Toutes les anciennes ressources de données restent identiques à
|
||||
beta.197, sauf le preset public qui sélectionne maintenant Medium beta.198.
|
||||
Les nouvelles ressources activent explicitement le correctif ; les anciens
|
||||
profils ne le font pas. Les tests natifs finaux correspondent à l'empreinte
|
||||
finale des sources de génération. Reçu : `build/gallery198-artifact.json`.
|
||||
Copie et guide dans `Downloads/`, sans sauvegarde, réglage personnel ni journal
|
||||
inclus dans l'archive.
|
||||
@@ -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,125 @@
|
||||
# ANCHORS-186 — rosaces ornementées et huit refuges
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/hidden-anchors-beta186`.
|
||||
Retour sur beta.185 : retirer les barrières supérieures, conserver la silhouette,
|
||||
ajouter de fins liserés au verre et intégrer huit ancres dans le terrain de leur
|
||||
secteur de couleur, en surface, en grotte et sur les récifs.
|
||||
|
||||
## Contrat
|
||||
|
||||
Nouveau laboratoire `anchors`, preset `sanctuary_test:hidden_anchors_v1`,
|
||||
graine de visite 42, beta.186. Génération déterministe, recherche bornée dans
|
||||
le champ de densité existant et écritures limitées au chunk en décoration.
|
||||
Aucune régénération des visites précédentes ni modification des anciens presets.
|
||||
Les huit couleurs reprennent la palette du Bugrock, dans le même ordre
|
||||
que les rosaces. Les ancres conservent le bloc et ses propriétés existantes.
|
||||
|
||||
Les dépôts distincts restent locaux à ce prototype : ils allument l'ancre et
|
||||
sa gemme sur le Bugrock ; ils ne génèrent pas encore d'expansion. Les coûts
|
||||
sont des matériaux accessibles sur l'île, sans or ni redstone.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le balcon supérieur perd ses murets. Deux liserés alternent calcite et cuivre
|
||||
patiné sur chaque dôme, en remplaçant une mince bande de verre. Huit petits
|
||||
losanges ponctuent les grandes verrières. Le volume, les couleurs dominantes,
|
||||
la tige d'un bloc, le Bugrock à Y=640, le portail de toiture et l'atlas restent
|
||||
ceux de beta.185. Les passages sur les huit axes restent dégagés.
|
||||
|
||||
La recherche échantillonne une grille finie et les grands récifs existants.
|
||||
Elle exige une base rocheuse et un débouché naturel praticable. Deux secteurs
|
||||
aériens sont retenus lorsqu'ils disposent de récifs compatibles ; les autres
|
||||
privilégient alternativement une grotte ou la surface, avec repli sur un autre
|
||||
emplacement naturel admissible du même secteur. Le choix varie avec la graine.
|
||||
Le manque de site sûr fait échouer ce laboratoire explicitement plutôt que de
|
||||
fabriquer une ancre au-dessus du vide. Seule la graine de visite est certifiée.
|
||||
|
||||
Les refuges sont de petites poches irrégulières : sol légèrement ondulé,
|
||||
reliefs dans la roche présente, accès court décalé, crêtes érodées, mousse,
|
||||
azalées et verre de couleur au piédestal. Certains reliefs cachent une source
|
||||
de lumière, d'autres restent sombres. Les réservations évitent la galerie,
|
||||
le donjon, le soufre et les bassins connus. Elles empêchent aussi la décoration
|
||||
d'un chunk voisin de reboucher les volumes, sans bloquer les actions des joueurs.
|
||||
Aucun calcul d'hydrologie régionale ni recherche par tick n'est ajouté.
|
||||
|
||||
| Secteur | Couleur | Dépôt en main |
|
||||
| --- | --- | --- |
|
||||
| Nord | Bleu clair | 32 pierres |
|
||||
| Nord-est | Vert clair | 16 bûches de chêne |
|
||||
| Est | Vert | 16 blés |
|
||||
| Sud-est | Orange | 16 lingots de cuivre |
|
||||
| Sud | Rouge | 16 charbons |
|
||||
| Sud-ouest | Magenta | 8 éclats d'améthyste |
|
||||
| Ouest | Jaune | 8 lingots de fer |
|
||||
| Nord-ouest | Cyan | 16 verres |
|
||||
|
||||
Clic droit sur l'ancre : le serveur valide le site, le matériau et la quantité.
|
||||
Un dépôt exact allume l'ancre et le bit de couleur associé au Bugrock. Les
|
||||
états restent dans les propriétés natives des blocs ; aucune nouvelle base de
|
||||
progression ou migration. Les textes FR/EN existants sont réutilisés. Le
|
||||
profil 173 et ses anciens coûts ne sont pas modifiés.
|
||||
|
||||
## Vérification
|
||||
|
||||
Premier contrôle natif réussi sur `solo186b/anchors/42` : huit ancres, deux
|
||||
récifs aériens, deux grottes et quatre surfaces/flancs. Plan déterministe de
|
||||
8 650 entrées dans 26 chunks, calculé en 2,3 s. Huit dépôts vérifiés dans un
|
||||
ordre non séquentiel ; tous les bits sont indépendants, les mauvais dépôts et
|
||||
les répétitions conservent les objets. Les états initiaux sont restaurés après
|
||||
ce test, avant la sauvegarde de visite.
|
||||
|
||||
Palais : 144 murets retirés, 384 blocs de liserés/losanges incrustés,
|
||||
4 944 blocs de verre conservés, seize approches libres. Le volume et le cadre
|
||||
du portail restent inchangés. Les huit teintes existent dans les deux rosaces.
|
||||
|
||||
La première réouverture a révélé un ancien contrôle de cerisiers basé sur un
|
||||
compteur de génération en mémoire. Le contrôle lit désormais les troncs déjà
|
||||
sauvegardés lorsqu'il n'y a pas de compteur ; aucun arbre n'est régénéré.
|
||||
Validation finale sur `solo186c/anchors/42` réussie à froid puis après
|
||||
réouverture : 567 échantillons de densité conservés et zéro région d'hydrologie.
|
||||
Démarrage serveur 6,9 s à froid / 0,66 s à chaud ; prêt avec l'ensemble des
|
||||
sondages et contrôles en 44,7 s / 6,4 s. Le plan des huit refuges prend 2,28 s
|
||||
à froid. Ces mesures incluent du travail de vérification et ne constituent pas
|
||||
une promesse de temps de chargement du client.
|
||||
|
||||
Lecture de la sauvegarde arrêtée : huit ancres de secteurs 0 à 7, éteintes,
|
||||
Bugrock allumé avec masque de relais nul ; 49 cartes sauvegardées, complètes,
|
||||
figées, et cadres horizontaux uniques. Aucun dépôt de contrôle ne reste activé
|
||||
sur le monde proposé au créateur.
|
||||
|
||||
Reçus ignorés : `build/anchors186-quick-result.json`,
|
||||
`build/anchors186-warm-result.json`, `build/anchors186-saved-blocks.json`,
|
||||
`build/anchors186-saved-atlas.json`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack :sanctuary-test:exportDuoLaunch`
|
||||
réussi en **6 min 49 s**, **265/265 GameTests**. Journal
|
||||
`build/anchors186-check-build.log`. Le daemon de compilation a été arrêté avant
|
||||
le client pour libérer la mémoire. Solo Vulkan ouvert le 30 septembre à **08:08:19**, vue 32 et simulation 12
|
||||
confirmées dans `build/anchors186-solo.log`. Contrôle de visibilité de la couronne
|
||||
réussi à 08:08:20. La fenêtre du jeu confirme le balcon sans murets et les
|
||||
vitrages reliés au soubassement. La visite est laissée au créateur sans téléportations d’inspection supplémentaires. L'aspect des huit
|
||||
refuges reste à apprécier ensemble en jeu. Aucun canal public ni instance
|
||||
Prism n'a été modifié.
|
||||
|
||||
## Visite — graine 42
|
||||
|
||||
Solo neuf `visite186/anchors/42`, `Sanctuary-Hidden-Anchors-186-Solo`,
|
||||
vue 32 chunks, simulation 12, créatif, vol et commandes ; départ dans le
|
||||
palais supérieur. Sauvegarde beta.185 conservée. Les emplacements suivants
|
||||
sont des raccourcis de test, pas des marqueurs visibles dans le jeu.
|
||||
|
||||
| Secteur | Milieu | Lumière du refuge | Accès de visite |
|
||||
| --- | --- | --- | --- |
|
||||
|
||||
|
||||
|
||||
| Nord | Surface / flanc | Lumineux | `/tp -71.5 243 -226.5 0 0` |
|
||||
| Nord-est | Surface / flanc | Sombre | `/tp 96.5 186 -82.5 0 0` |
|
||||
| Est | Récif aérien | Lumineux | `/tp 213.5 444 21.5 180 0` |
|
||||
| Sud-est | Surface / flanc | Sombre | `/tp 156.5 161 193.5 0 0` |
|
||||
| Sud | Grotte | Lumineux | `/tp -83.5 115 215.5 180 0` |
|
||||
| Sud-ouest | Surface / flanc | Sombre | `/tp -107.5 252 181.5 0 0` |
|
||||
| Ouest | Grotte | Lumineux | `/tp -96.5 223 24.5 90 0` |
|
||||
| Nord-ouest | Récif aérien | Sombre | `/tp -102.5 404 -171.5 90 0` |
|
||||
|
||||
Palais : `/tp 6.5 1280 8.5 140 -15` ; Bugrock : `/tp 0.5 639 5.5 180 -20`.
|
||||
@@ -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,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,128 @@
|
||||
# DISCOVERY-183 — lieux et ressources de Sanctuary Island
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/island-discovery-beta183`.
|
||||
Le créateur a visité le solo beta.182 et valide la palette naturelle, la rosace,
|
||||
les étangs et la génération sous l'île. Il demande un portail **horizontal**,
|
||||
les secteurs miniers et le soufre manquants, l'ISS séparée et un exemple d'eau
|
||||
haute qui suit le dénivelé. Son complément relie les vitres de la rosace aux
|
||||
huit couleurs du Bugrock et à une géographie lisible, froide au nord, chaude au sud.
|
||||
|
||||
## Contrat
|
||||
|
||||
Nouveau preset facultatif `sanctuary_test:discovery_v1`, profil `discovery`.
|
||||
Graine de visite : **42**. Anciens presets, sauvegardes et chunks conservés.
|
||||
La source de biomes possède une option `discovery` absente/false pour les
|
||||
anciens profils : seule la nouvelle génération active la poche de soufre.
|
||||
Aucune migration, expansion, activation de portail ou progression ajoutée.
|
||||
Minecraft 26.3, blocs de soufre natifs et minerais Sanctuary existants.
|
||||
|
||||
## Contenu
|
||||
|
||||
- Galerie centrale agrandie, toujours taillée dans la roche, avec ses deux
|
||||
annexes et passages en boucle. Cadre d'obsidienne de **5 × 5**, trou central
|
||||
de **3 × 3**, fond un bloc plus bas, sec. Le gardien viendra ultérieurement.
|
||||
- Rosace : positions, nervures, passages et Bugrock à Y=320 conservés.
|
||||
Seules les vitres changent. Ordre du dessin source, depuis le nord :
|
||||
bleu clair, vert clair, vert, orange, rouge, magenta, jaune, cyan.
|
||||
Le petit sommet blanc conserve le rappel du logo central blanc.
|
||||
Cette rose colorée donne une convention géographique ; elle ne transforme
|
||||
pas encore les biomes de l'île en huit régions ni n'allume les relais.
|
||||
- Trois secteurs de gemmes : saphir au nord (0,-176), émeraude à l'est
|
||||
(176,0), rubis au sud (0,176), chacun de rayon 64 blocs autour des centres
|
||||
de chunks. Petits amas sur les faces de grotte, entre Y=40 et 287,
|
||||
toujours au moins 13 blocs sous la surface, au plus douze blocs par chunk
|
||||
retenu. Les blocs exposés et stocks sont comptés dans
|
||||
le monde effectivement généré. Diamants, cuivre, charbon, fer et lapis
|
||||
gardent leur distribution précédente ; aucun or ni redstone ajouté.
|
||||
- Une caverne de soufre au sud-ouest, altitude choisie dans la roche,
|
||||
soufre/cinabre/calcite, ouverture vers le jour et trois formations de soufre
|
||||
enracinées à la surface pour la repérer. Pas de lave ni de nouveau mob.
|
||||
- Un lac supérieur supplémentaire et son bassin aval, reliés par un chenal
|
||||
en marches sur quatre blocs de dénivelé. La partie aval s'ouvre au jour.
|
||||
Les deux systèmes d'étangs beta.180 restent présents. Recherche locale
|
||||
bornée et plan mémorisé ; aucune hydrologie régionale calculée.
|
||||
- ISS entre Y=575 et 591 : trois modules métalliques reliés et praticables,
|
||||
quatre ailes solaires, poutre centrale, antenne, porche d'arrivée et
|
||||
observation vitrée vers l'île. Architecture originale, sans schematic
|
||||
externe. La salle nord contient une table de cartes dans des cadres lumineux
|
||||
horizontaux. Le rayon du générateur détermine la mosaïque et la salle :
|
||||
5 × 5 pour le petit format (512), 7 × 7 pour le moyen (724), 9 × 9 pour le
|
||||
grand (1024). Ce profil de terrain reste au format moyen ; les autres
|
||||
dimensions de station sont vérifiées géométriquement, sans ajouter de
|
||||
nouveaux profils de terrain à cette livraison. Aucun familier spécial,
|
||||
quête ou portail vers les hauteurs fonctionnel ajouté.
|
||||
|
||||
Toutes les nouvelles écritures appartiennent au chunk en cours de décoration.
|
||||
Plans locaux calculés une fois, aucune reconstruction dans une sauvegarde
|
||||
ouverte. Les cartes sont des objets Minecraft natifs, persistés avec leurs
|
||||
cadres. Une file finie les prépare par tranches de quatre lignes avec un budget
|
||||
cible de 3 ms par tick. Projection topographique de la graine au pas de quatre
|
||||
blocs, palette réelle pour les chunks déjà chargés, biomes pour les autres :
|
||||
c'est une vue d'ensemble du terrain initial, pas une photographie détaillée
|
||||
de toutes les constructions. Aucun chargement de chunks éloignés demandé pour
|
||||
la carte. Les cartes terminées sont figées comme une trace de l'ancienne
|
||||
expédition, et ne sont ni recréées ni regagnées si un joueur retire un cadre.
|
||||
Une interruption reprend une carte inachevée avec son identifiant existant.
|
||||
|
||||
## Vérifications et visite
|
||||
|
||||
Monde neuf final `solo183e/discovery/42`, serveur arrêté et sauvegardé proprement :
|
||||
567 échantillons de densité conservés sous Y=216, zéro région d'hydrologie,
|
||||
trois cerisiers, les deux bassins antérieurs et les cinq spawners/quatre caches
|
||||
conservés. Galerie : 1 939 surfaces reliées sur 1 979 colonnes, cadre 5 × 5 et
|
||||
fosse sèche 3 × 3 vérifiés. Rosace : géométrie inchangée, huit couleurs présentes
|
||||
et huit approches libres.
|
||||
|
||||
Stocks réellement comptés dans les trois districts : 235 saphirs (176 exposés),
|
||||
624 émeraudes (441 exposées), 576 rubis (402 exposés). Nouveau système d'eau :
|
||||
4 311 blocs d'eau reliés et débouché extérieur vérifié. La ventilation du soufre
|
||||
est ouverte jusqu'au terrain local du débouché, plutôt qu'à la hauteur plus
|
||||
basse du centre de la poche.
|
||||
|
||||
ISS : 4 742 entrées de plan, trois modules reliés, quatre ailes solaires,
|
||||
49 cartes distinctes et supportées dans 49 cadres horizontaux. Les géométries
|
||||
5 × 5, 7 × 7 et 9 × 9 couvrent les rayons 288, 395 et 544 avec leurs marges de
|
||||
terrain. La mosaïque courante couvre 896 × 896 blocs. Calcul de son aperçu :
|
||||
2 039 ms au total ; aucun chargement de chunk distant dans ce calcul. Lecture
|
||||
NBT après arrêt : 49 cartes pleines et figées, 49 identifiants uniques, tous
|
||||
les cadres orientés vers le haut, centres jointifs et aucun marqueur de travail
|
||||
inachevé. Assemblage des pixels sauvegardés inspecté visuellement ; il ne s'agit
|
||||
pas d'une capture du jeu. Rapport local `build/discovery183-saved-atlas.json`.
|
||||
|
||||
Démarrage serveur mesuré à 4,4 s ; préparation avec sondages, génération des
|
||||
zones visitées et contrôles terminée à 41,1 s. Ce dernier chiffre inclut les
|
||||
tests synchrones, ce n'est pas un temps de démarrage normal du solo.
|
||||
Rapport `build/discovery183-quick-result.json`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi en 7 min 20 s,
|
||||
**265/265 GameTests** (`build/discovery183-check-build.log`). Après les deux
|
||||
retouches finales limitées au laboratoire (ventilation et lecture des couleurs
|
||||
réelles de la carte), compilation, contrôles du mod de labo, assemblages et
|
||||
export du lanceur ont été refaits ; les checks des trois modules inchangés,
|
||||
déjà réussis, n'ont pas été répétés dans cet assemblage final. Journal
|
||||
`build/discovery183-final-assembly.log`. Le monde `solo183e` vérifie ce code final.
|
||||
|
||||
Solo neuf `visite183/discovery/42`, `Sanctuary-Discovery-183-Solo`, vue 32,
|
||||
simulation 12, créatif, vol et commandes. Départ préparé devant la table de
|
||||
l'ISS. Entrée en jeu confirmée dans le journal le 30 septembre à 03:11:11 :
|
||||
KokaLab en (4.5,580,-12.5), vue 32. Passage en spectateur à 03:11:48.
|
||||
Client Vulkan actif.
|
||||
Aucune sauvegarde antérieure ni instance Prism modifiée.
|
||||
|
||||
| Lieu | Téléportation de visite |
|
||||
| --- | --- |
|
||||
| Salle des cartes ISS | `/tp 4.5 580 -12.5 140 35` |
|
||||
| Porche ISS | `/tp 0.5 577 28.5 180 0` |
|
||||
| Rosace | `/tp 0.5 319 5.5 180 0` |
|
||||
| Portail inférieur | `/tp 0.5 177 7.5 180 30` |
|
||||
| Poche de soufre | `/tp -152 180 152` |
|
||||
| Indice de soufre en surface | `/tp -158 281 149` |
|
||||
| Bassin supérieur | `/tp -96 254 -192` |
|
||||
| Saphirs | `/tp -62 236 -204` |
|
||||
| Émeraudes | `/tp 114 172 -30` |
|
||||
| Rubis | `/tp -60 192 146` |
|
||||
|
||||
Graine 42 testée en génération native ; les autres graines restent à éprouver.
|
||||
Palette, volumes, positionnement des cartes et proportions de l'ISS à valider
|
||||
en visite. Les cartes sont une vue d'ensemble figée de l'île principale,
|
||||
pas encore un atlas vivant des expansions.
|
||||
@@ -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,40 @@
|
||||
# WG-ECO-179 — grottes vivantes et complexes souterrains
|
||||
|
||||
Retour de visite beta.178 : automne validé ; trop de cerisiers bas, bordures
|
||||
rocheuses sur les pentes, grottes devenues trop sèches et trop dépouillées.
|
||||
Nouveau preset `sanctuary_test:living_ecology_v1`, profil `living`, graine 42,
|
||||
branche `codex/living-caves-beta179`, base `db6194c`. Nouveau monde uniquement.
|
||||
|
||||
- Vallées automnales conservées. Sol végétal sur les pentes supérieures ; la
|
||||
roche nue reste sur les flancs profonds sous Y=200.
|
||||
- Cerisiers à partir de Y=286, une tentative tous les quatre chunks du sommet
|
||||
au lieu de dix arbres par chunk. Pas de forêt de cerisiers sur le plateau.
|
||||
- Poches lush avec bassins retenus à plusieurs niveaux, sous-bois de chênes
|
||||
noirs, marais à lucioles, mycélium violet mêlé d’herbe et champignons géants.
|
||||
Mooshrooms et grenouilles placées à la génération dans leurs habitats.
|
||||
- Trois réseaux miniers avec hall, ramifications, rails, passerelles et sorties
|
||||
extérieures ; une cabane de sorcière dans une cavité marécageuse. Première
|
||||
version procédurale, sans butin ni progression. Le retour actuel remplace
|
||||
l’interdiction des mineshafts exprimée avant la visite beta.178.
|
||||
|
||||
La couleur vient des biomes, du feuillage, de l’eau et du brouillard ; aucune
|
||||
nouvelle simulation de lumière colorée. Bruit du terrain, récifs, minerais et
|
||||
espace ISS conservés ; pas d’hydrologie régionale. Structures rendues par chunk
|
||||
selon un plan déterministe borné, aucune expansion activée ni migration.
|
||||
|
||||
Compilation et contrôle natif `solo179c` réussis : 567 densités profondes
|
||||
inchangées, zéro région d’hydrologie, automne conservé (14 colonnes de contrôle),
|
||||
cerisiers uniquement au sommet et pentes supérieures végétalisées. Les témoins
|
||||
natifs contiennent eau, mousse, mycélium, buissons à lucioles, chênes noirs et
|
||||
champignons géants. Sauvegarde des habitants vérifiée : 6 mooshrooms,
|
||||
17 grenouilles, une sorcière et un chat dans les chunks chargés, sans prétendre
|
||||
recenser toute l’île. La suite `check build assemblePack assembleTestPack` a réussi
|
||||
en 36 min 7 s, après la visite.
|
||||
|
||||
Visite préparée : `visite179/living/42`, `Sanctuary-Living-179-Solo`, créatif,
|
||||
commandes activées, difficulté normale pour conserver la sorcière, vue 32 et
|
||||
simulation 12. Points de visite (graine 42) : halls miniers (112,147,72),
|
||||
(-160,185,24), (-72,129,160) ; cabane (-192,114,96) ; mycélium vers
|
||||
(-216,201,48), marais vers (-216,113,96), lush vers (-216,161,120).
|
||||
Client Vulkan ouvert le 29 septembre à 23:16, KokaLab connecté ; distance
|
||||
serveur 32 et simulation 12 confirmées dans le journal.
|
||||
@@ -0,0 +1,78 @@
|
||||
# DETAILS-188 — fonds d'étangs, soufre vivant et palettes des ancres
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/living-details-beta188`.
|
||||
Le créateur valide l'orientation des ancres et les formes des bassins beta.187.
|
||||
Il demande des refuges adaptés au milieu et à la couleur, des fonds sédimentaires
|
||||
végétalisés sans arbres dans l'eau, et les pics/geysers du biome de soufre.
|
||||
|
||||
Nouveau profil `details`, preset `sanctuary_test:living_details_v1`, graine 42.
|
||||
Aucune modification des anciennes sauvegardes ou générations. Même densité,
|
||||
mêmes bassins et mêmes emplacements/orientations d'ancres que beta.187.
|
||||
Livraison locale de laboratoire ; objectif de visite avant 9 h.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Géométrie et orientation des refuges conservées. Terre, herbe et terre stérile
|
||||
pour la surface ; tuff, deepslate et calcite sous roche ; calcite et diorite
|
||||
sur les récifs. Les incrustations reprennent la teinte du secteur. Le verre
|
||||
reste au-dessus de l'améthyste, du côté de l'expansion.
|
||||
|
||||
Les bassins sont remplis avant les arbres. Leur lit existant reçoit des plages
|
||||
cohérentes d'argile, boue, sable et gravier, sans agrandissement ni coque ajoutée.
|
||||
Les colonnes de plantation immergées sont réservées pendant la décoration.
|
||||
Herbes marines et quelques touffes de kelp occupent les fonds ; les matériaux
|
||||
meubles exigent un support rocheux. Contours et niveaux restent ceux de 187.
|
||||
|
||||
Le soufre reçoit des stalagmites et stalactites natives, avec des longueurs
|
||||
variables, et des sources périodiques : magma, soufre puissant, eau source.
|
||||
Les volumes humides réservés, le donjon et les refuges sont évités. Les entités
|
||||
de bloc sont explicitement créées et sauvegardées pour activer les minuteries
|
||||
natives des geysers. Le contrôle vérifie l'entité et le ticker après réouverture.
|
||||
|
||||
Table de butin propre à ce nouveau preset : une ou deux pièces en fer par
|
||||
wagonnet, parmi quatre armures, épée, pioche, hache et pelle. Enchantement par
|
||||
la fonction native, puissance de table 12 à 20, sans enchantement trésor.
|
||||
Les gemmes et les ressources déjà présentes restent dans le butin. Les anciens
|
||||
wagonnets et tables ne changent pas. La progression du futur monde minéral
|
||||
reste une intention, hors de cette livraison.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Premier monde : les huit refuges gardent exactement leurs positions et axes.
|
||||
Quatre sédiments, 611 colonnes végétalisées et zéro tronc dans les bassins.
|
||||
Plan de 496 spires et 39 geysers ; 138 blocs de pic et huit sources contrôlés.
|
||||
32 tirages de butin : toujours une ou deux pièces enchantées en fer, les huit
|
||||
types d'équipement observés. Génération finale `solo188c/details/42` puis réouverture réussies, avec contrôle
|
||||
des entités de geyser et de leurs tickers natifs. Les blocs et entités sont
|
||||
relus dans la sauvegarde arrêtée : huit ancres éteintes, Bugrock allumé sans
|
||||
relais, 49 cartes complètes et 49 cadres horizontaux. Les quatre wagonnets sauvegardés portent tous la nouvelle table
|
||||
`sanctuary_test:chests/mine_cache_188` ; reçu `build/details188-saved-loot.json`.
|
||||
`./gradlew check build assemblePack assembleTestPack :sanctuary-test:exportDuoLaunch`
|
||||
réussi en 7 min 54 s : 265 GameTests réussis. Les contrôles natifs finaux passent
|
||||
après la correction des entités de geyser, à froid et après réouverture.
|
||||
Le JAR du labo a ensuite été reconstruit et le pack optionnel resynchronisé
|
||||
avec `scripts/test_pack.py` : ses 146 classes correspondent aux classes compilées,
|
||||
sa table de butin correspond à la source et le JAR distribué est identique.
|
||||
Lors des 32 tirages après réouverture, 51 pièces enchantées couvrent les huit
|
||||
types d'équipement. Reçu de la suite générale : `build/details188-check-build.log`.
|
||||
|
||||
Solo `visite188c/details/42`, monde `Sanctuary-Living-Details-188-Solo`, ouvert
|
||||
sous Vulkan : connexion à 09:00:41, vue 32 et simulation 12 confirmées dans le
|
||||
journal, puis passage du joueur en spectateur à 09:00:58. L'objectif avant 9 h
|
||||
a donc été dépassé. Le contrôle visuel des décors et des éruptions reste à faire
|
||||
pendant la visite ; la présence des blocs, entités et tickers est vérifiée.
|
||||
|
||||
## Raccourcis de visite
|
||||
|
||||
- Bassin ouest : `/tp -104.5 269 119.5 180 35`
|
||||
- Soufre et geysers : `/tp -217.5 159 -16.5`
|
||||
- Autre source : `/tp -201.5 150 8.5`
|
||||
- Wagonnet du donjon : `/tp 45.5 144 60.5`
|
||||
- Ancre nord : `/tp 36.5 250 -168.5 180 0`
|
||||
- Ancre sud, grotte : `/tp -71.5 176 205.5 0 0`
|
||||
- Ancre est, récif : `/tp 214.5 444 16.5 -90 0`
|
||||
|
||||
Nouveau solo uniquement, vue 32, simulation 12, créatif avec vol et commandes.
|
||||
Aucune publication du canal public ni modification de Prism.
|
||||
|
||||
Mesures du labo final : démarrage 12.839 s à froid / 0.912 s après réouverture ; prêt avec tous les contrôles en 71.525 s / 14.114 s. Mesurées pendant la suite générale, hors chargement graphique. Zéro région d’hydrologie. Reçus ignorés `build/details188-final-cold-result.json` et `build/details188-final-warm-result.json`.
|
||||
@@ -0,0 +1,50 @@
|
||||
# UI-194 — retirer Amis du menu principal
|
||||
|
||||
Branche `codex/main-menu-beta194`, depuis beta.193, Minecraft 26.3.
|
||||
|
||||
Le créateur suspend les retouches du terrain et les tests de screenshots,
|
||||
puis reprend le cadrage des interfaces. Le retrait d’Amis est la première
|
||||
modification demandée, déjà prévue dans [Storyquest](storyquest-beta173.md).
|
||||
|
||||
## Résultat
|
||||
|
||||
Le menu principal propose Solo, Multijoueur, puis Options et Quitter sur une
|
||||
même ligne. Cette dernière suit Multijoueur avec l’espacement natif de 24 unités
|
||||
GUI. Le bouton Amis n’est plus créé à la place de Realms ; l’entrée Realms et
|
||||
la petite icône sociale restent retirées. Les commandes restantes gardent
|
||||
leur ordre clavier et la version Sanctuary reste affichée.
|
||||
|
||||
Le parcours de démonstration, dépourvu de Multijoueur, conserve sa disposition
|
||||
antérieure. Les autres accès sociaux et paramètres du compte ne sont pas
|
||||
modifiés. Aucun nouveau libellé : les commandes natives restent traduites FR/EN,
|
||||
les anciennes clés `sanctuary.menu.friends` sont conservées pour compatibilité.
|
||||
|
||||
Le menu pause, Habitant, Combat, Factions et Storyquest restent des suites
|
||||
à concevoir. Aucune source de génération, donnée de biome ou sauvegarde n’est
|
||||
modifiée par ce lot. Aucune nouvelle série de captures n’est lancée.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Le parcours natif existant `Title045ClientChecks` est actualisé pour vérifier
|
||||
l’absence d’Amis et de Realms, la disposition sans ligne vide, l’ordre clavier
|
||||
et l’ouverture d’Options en FR/EN aux quatre réglages GUI. Ses captures et son
|
||||
ancien parcours vers la liste d’amis sont retirés.
|
||||
|
||||
Validation réussie : `./gradlew check build assemblePack assembleTestPack
|
||||
:sanctuary:runClientGameTest -PsanctuaryFocusedTests=menus
|
||||
-PsanctuaryClientTests=true -PsanctuaryTitleClientTests=true
|
||||
-PsanctuaryClientGraphicsBackend=vulkan`.
|
||||
Journal : `build/menu194-check-build.log`. Les contrôles purs et les quatre
|
||||
GameTests serveur ciblés passent. Le parcours client Vulkan passe en FR/EN,
|
||||
sans capture, avec ordre clavier, absence des boutons retirés et ouverture
|
||||
d’Options vérifiés. Dans la fenêtre 854 × 480 du test, les réglages GUI 3 et 4
|
||||
sont plafonnés par Minecraft à l’échelle effective 2.
|
||||
|
||||
Les packs normal et de test sont assemblés. La comparaison des JAR beta.193 et
|
||||
beta.194 confirme que seule `TitleMenuMixin.class` change dans le code livré ;
|
||||
les données de génération sont identiques. Le module Demeure embarqué ne change
|
||||
que de version dans ses métadonnées. Reçu : `build/menu194-artifact.json`.
|
||||
Le labo beta.193 déjà ouvert n’est ni arrêté ni mis à jour par cette livraison.
|
||||
|
||||
Livraison locale uniquement ; publication et mise à jour du jeu personnel
|
||||
ne sont pas demandées.
|
||||
@@ -0,0 +1,115 @@
|
||||
# ASCENT-184 — rosace à 640 et palais des hauteurs
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/natural-ascent-beta184`.
|
||||
Le retour sur beta.183 rejette l'ISS métallique visible depuis le sol et les
|
||||
nouveaux bassins supérieurs ovales en marches. La direction retenue est une
|
||||
machine naturelle : rosace, longue tige parcourable, couronne de palais comme
|
||||
un parapluie, carte au sol et portail horizontal au-dessus. Dernière correction
|
||||
explicite du créateur : **la rosace est à Y=640**, pas à 320.
|
||||
|
||||
## Contrat de génération
|
||||
|
||||
Nouveau profil de laboratoire `ascent`, preset `sanctuary_test:ascent_v1`,
|
||||
réglages `sanctuary_test:ascent_v1_10`, graine de visite **42**. Nouvelle dimension
|
||||
`sanctuary_test:ascent_1600`, constructible de Y=0 à 1599. Minecraft 26.3 accepte
|
||||
ce type de dimension dans la génération native vérifiée. Le bruit du terrain
|
||||
reste calculé entre 0 et 639 ; la décoration écrit les structures plus haut
|
||||
dans les sections du nouveau monde. Aucun bruit supplémentaire dans le ciel
|
||||
vide, aucune hydrologie régionale, aucun changement des sauvegardes précédentes.
|
||||
|
||||
Les presets 180–183 restent distincts. Le nouveau profil reprend l'île, ses
|
||||
minerais, biomes, cavernes, soufre et donjon, mais ne place ni l'ISS métallique
|
||||
ni les bassins `Water183`. Les étangs souterrains beta.180 précédemment validés
|
||||
restent présents. Pas d'expansion, migration, nouveau mob ou portail fonctionnel.
|
||||
|
||||
## Construction et découverte
|
||||
|
||||
- Rosace et Bugrock translatés à **(0,640,0)**, sol à 638. Les huit couleurs et
|
||||
approches de la rosace sont conservées ; la sortie est rejoint l'escalier.
|
||||
- Tige de tuf, deepslate, calcite et mousse, nervures enroulées, haltes latérales.
|
||||
Escalier continu avec marches natives, de 638 à 1278 : 1 923 positions de
|
||||
parcours, environ 1,9 km de chemin pour 640 blocs gagnés. Largeur généralement
|
||||
de trois blocs, resserrée aux virages. Le parcours depuis l'île jusqu'à la
|
||||
rosace reste à concevoir ; le test permet le vol et le spectateur.
|
||||
- Palais : nervures déployées à partir de 1160, sol à **1278**, voûte à **1310**.
|
||||
Huit pétales vitrés, passages minéraux, cuivre oxydé, balcons. Pas de panneaux
|
||||
solaires ni de modules métalliques. Le Bugrock reste uniquement sur la rosace.
|
||||
- Atlas natif au sol, 49 cartes sur ce terrain moyen, dans des cadres horizontaux.
|
||||
Dimensions 5×5, 7×7 et 9×9 pour les rayons déjà pris en charge. Identifiants et
|
||||
marqueurs propres à ce nouvel atlas ; l'atlas 183 reste inchangé.
|
||||
- Portail supérieur : cadre horizontal 7×7 d'obsidienne, coins en obsidienne
|
||||
pleureuse, vide central 5×5, à 1287 au-dessus des cartes. Le portail inférieur
|
||||
conserve son cadre 5×5 et son creux 3×3 à Y=175. Ce sont des infrastructures.
|
||||
|
||||
À 32 chunks, la caméra native peut garder un plan lointain de 2 048 blocs :
|
||||
la hauteur seule ne suffit pas à dissimuler la couronne. Dans cette dimension
|
||||
uniquement, le frustum refuse les volumes entièrement au-dessus de 1152 lorsque
|
||||
la caméra est sous 560. La couronne devient donc éligible à l'affichage pendant
|
||||
l'approche de la rosace ; les règles normales de visibilité restent applicables.
|
||||
C'est une révélation côté rendu, pas une génération conditionnelle à un joueur.
|
||||
Un contrôle à l'entrée du client teste les deux API natives de frustum, depuis
|
||||
le sol et depuis 640. La couronne est aussi au-delà du volume maximal des ombres
|
||||
Sanctuary depuis le plateau (distance 256, extrusion solaire comprise). Cela
|
||||
ne supprime pas l'éclairage Minecraft inhérent aux blocs placés dans une colonne.
|
||||
|
||||
## Eau dans les creux existants
|
||||
|
||||
Recherche locale et bornée dans le champ réel de densité. Chaque candidat est
|
||||
rempli par parcours des cellules d'air adjacentes sous un niveau horizontal ;
|
||||
un chemin sortant du volume de recherche, vers le vide ou une zone réservée
|
||||
fait rejeter le candidat. On monte le niveau par essais bornés tant que la roche
|
||||
retient l'eau. Aucun mur de retenue, fond d'argile artificiel, marche ou excavation
|
||||
n'est dessiné. Seules les cellules encore en air reçoivent de l'eau à la décoration.
|
||||
Les cavités existantes, roches saillantes et végétaux conservés dictent le contour.
|
||||
|
||||
Première génération native `solo184a`, graine 42 : 45 essais, plan en 852 ms,
|
||||
10 402 blocs d'eau dans la grande poche (surface de 1 045 cellules à Y=234),
|
||||
739 blocs dans la seconde (290 cellules à Y=242). Aucun débouché latéral/inférieur
|
||||
non retenu détecté après la décoration. Ce sont des eaux retenues ; aucune
|
||||
simulation de bassin versant ou de rivière à longue distance n'est introduite.
|
||||
|
||||
## Vérification et visite
|
||||
|
||||
Premier contrôle natif réussi : 567 échantillons profonds identiques, zéro région
|
||||
d'hydrologie, trois cerisiers, cinq spawners, quatre caches à butin, galerie,
|
||||
soufre et trois secteurs de gemmes présents. Les 56 246 entrées du plan du palais
|
||||
correspondent aux blocs générés ; toutes les positions de montée ont un support
|
||||
et deux blocs libres. Les 49 cartes sont uniques, remplies, figées et supportées.
|
||||
Serveur démarré en 5,3 s ; prêt en 43,6 s avec génération des sondages et contrôles.
|
||||
Ce dernier temps inclut les tests et ne mesure pas un démarrage normal du solo.
|
||||
|
||||
Validation finale : `./gradlew check build assemblePack assembleTestPack
|
||||
:sanctuary-test:exportDuoLaunch` réussi en 6 min 54 s, **265/265 GameTests**.
|
||||
Journal `build/ascent184-check-build.log`. Nouveau monde final `solo184b/ascent/42`
|
||||
contrôlé et arrêté proprement : serveur en 5,25 s, prêt avec contrôles en 41,63 s ;
|
||||
plan d'eau en 806 ms, 10 401 et 739 blocs d'eau retenus sans fuite. Le nombre de blocs d'eau diffère d'une cellule entre les deux essais ;
|
||||
les niveaux et surfaces des plans restent identiques.
|
||||
Rapport `build/ascent184-quick-result-final.json`.
|
||||
|
||||
Lecture NBT du monde arrêté : 49 cartes complètes, figées, identifiants uniques,
|
||||
49 cadres orientés vers le haut à Y=1279. Air confirmé aux anciens centres
|
||||
(0,320,0) et (0,576,0), bloc originel à (0,640,0), plancher d'atlas en calcite
|
||||
et portail supérieur creux. Reçus `build/ascent184-saved-atlas-final.json` et
|
||||
`build/ascent184-saved-blocks-final.json`.
|
||||
|
||||
Solo neuf `visite184/ascent/42`, `Sanctuary-Ascent-184-Solo`, commandes, créatif,
|
||||
vol, vue 32 et simulation 12. Entrée client Vulkan confirmée le 30 septembre,
|
||||
contrôle natif du frustum réussi à **03:45:07** : couronne cachée à Y=250,
|
||||
rosace visible et couronne révélée à Y=640. Journal `build/ascent184-solo.log`.
|
||||
Le profil local KokaLab de ce monde utilise la couleur Ciel et le familier de
|
||||
labo par défaut. Le daemon de compilation a été arrêté pour libérer la mémoire.
|
||||
Aucune sauvegarde antérieure ni instance Prism modifiée. Les archives locales
|
||||
sont assemblées ; aucun canal public n'est avancé.
|
||||
|
||||
| Lieu | Téléportation de visite |
|
||||
| --- | --- |
|
||||
| Rosace et départ de la montée | `/tp 0.5 639 5.5 180 -20` |
|
||||
| Palais et carte au sol | `/tp 6.5 1280 8.5 140 35` |
|
||||
| Vue extérieure de la couronne | `/tp 45 1295 45 135 10` |
|
||||
| Grande cavité en eau | `/tp -144 236 -96` |
|
||||
| Seconde cavité en eau | `/tp -16 245 128` |
|
||||
| Portail inférieur | `/tp 0.5 177 7.5 180 30` |
|
||||
|
||||
Cette première silhouette reste à évaluer en visite, notamment le rythme de
|
||||
l'ascension et les proportions du palais. La graine 42 est la référence vérifiée ;
|
||||
la disponibilité de grandes cavités sur d'autres graines reste à éprouver.
|
||||
@@ -0,0 +1,100 @@
|
||||
# CAVES-187 — soufre naturel, ancres orientées et bassins visibles
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/natural-caves-beta187`.
|
||||
Retour beta.186 : coque de soufre artificielle, pylônes mal orientés et verre
|
||||
latéral, bassins de surface invisibles. Le relief et les trois secteurs de
|
||||
gemmes sont appréciés et conservés.
|
||||
|
||||
Nouveau profil `natural`, preset `sanctuary_test:natural_caves_v1`, graine 42.
|
||||
Les anciens presets et sauvegardes restent inchangés. Le laboratoire ajoute
|
||||
un climat de soufre en trois dimensions dans la roche existante, une approche
|
||||
en mousse tournée vers le centre pour chaque ancre et des pylônes opposés,
|
||||
vers l'expansion, portant le verre au-dessus de l'améthyste. L'eau de surface
|
||||
est recherchée dans des dépressions réellement ouvertes au ciel ; toute fuite
|
||||
vers le vide fait rejeter le bassin. Aucun calcul régional d'hydrologie.
|
||||
|
||||
Le climat de soufre utilise un bruit volumique et respecte la priorité des
|
||||
marais humides. La palette forme des dépôts cohérents de soufre, cinabre et
|
||||
calcite dans les blocs rocheux existants. Musique et couleurs du biome natif
|
||||
26.3 sont conservées. Les carvers, minerais et structures vanilla ne sont pas
|
||||
importés : l'île conserve ses cavités et sa distribution de ressources.
|
||||
L'ancienne coque et ses cheminées ne sont plus placées dans ce preset.
|
||||
|
||||
Les pylônes portent améthyste puis verre, sur la même verticale. Leur porte
|
||||
pointe vers la direction exacte du secteur, y compris les diagonales. Le
|
||||
chemin en mousse est du côté opposé. La sélection vérifie le support naturel
|
||||
de la porte et du chemin ; les huit emplacements changent pour respecter
|
||||
cette orientation. La galerie, les rosaces et le palais restent conservés.
|
||||
|
||||
L'eau de surface monte dans un creux par parcours prioritaire : chaque voxel
|
||||
est échantillonné une fois à la hauteur minimale qui le rend accessible.
|
||||
Une fuite, une structure réservée ou la limite de recherche arrête la montée.
|
||||
Les surfaces retenues doivent être majoritairement ouvertes au ciel. Aucun
|
||||
fond ajouté ni découpage elliptique ; les arbres voisins ne peuvent reboucher
|
||||
les cellules d'eau réservées. Les étangs souterrains appréciés sont conservés.
|
||||
L'atlas projette aussi les nouvelles eaux et perd le faux repère jaune de la
|
||||
cheminée supprimée. Les recherches sont bornées, en génération uniquement.
|
||||
|
||||
## Contrôles natifs
|
||||
|
||||
Graine 42, `solo187e/natural/42` : génération à froid réussie. Recherche des
|
||||
bassins en 2,674 s, contre 21,546 s au premier essai ; mêmes trois positions,
|
||||
niveaux et volumes retenus. Démarrage serveur 9,881 s, prêt après l'ensemble
|
||||
des contrôles en 50,190 s. Ces durées ne mesurent pas le démarrage graphique.
|
||||
Zéro région d'hydrologie et 567 échantillons de densité profonde conservés.
|
||||
|
||||
| Bassin | Niveau | Surface totale | Surface sous ciel ouvert | Blocs d'eau |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| Ouest, près de (-104, 88) | 248 | 1 994 | 1 475 | 11 935 |
|
||||
| Nord-est, près de (120, -136) | 244 | 839 | 536 | 2 329 |
|
||||
| Sud, près de (8, 136) | 254 | 1 321 | 904 | 9 446 |
|
||||
|
||||
Aucune fuite native détectée. Quatre points de visite confirment le biome
|
||||
sulfureux, de la roche minéralisée et des cavités déjà présentes dans la densité.
|
||||
Les huit refuges passent le contrôle d'orientation et de verre vertical.
|
||||
Les dépôts distincts activent les huit gemmes ; les tests restaurent ensuite
|
||||
les ancres éteintes et le Bugrock allumé sans relais. Les secteurs de gemmes,
|
||||
les cinq spawners et quatre wagonnets à butin restent vérifiés.
|
||||
|
||||
Réouverture réussie : démarrage serveur 0,618 s, prêt avec vérifications en
|
||||
8,226 s. Les huit ancres sont relues dans la sauvegarde éteintes et le masque
|
||||
du Bugrock vaut zéro ; 49 cartes complètes, figées, avec 49 cadres horizontaux
|
||||
uniques. Reçus ignorés : `build/natural187-final-cold-result.json`,
|
||||
`build/natural187-final-warm-result.json`, `build/natural187-saved-blocks.json`,
|
||||
`build/natural187-saved-atlas.json`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack :sanctuary-test:exportDuoLaunch`
|
||||
réussi en **7 min 10 s**, **265/265 GameTests**. Journal :
|
||||
`build/natural187-check-build.log`. Daemon Gradle arrêté avant le client.
|
||||
Solo Vulkan rejoint à **08:40:37**, départ (-104,5 ; 269 ; 119,5), vue 32
|
||||
et simulation 12 confirmées dans `build/natural187-solo.log`. Contrôle client
|
||||
de visibilité verticale réussi à 08:40:39. Le créateur a pris la main et est
|
||||
passé en spectateur ; aucune téléportation d'inspection supplémentaire.
|
||||
L'appréciation visuelle des nouveaux bassins et du soufre reste celle de
|
||||
cette visite ; les mesures ci-dessus sont des vérifications natives.
|
||||
Seule la graine de visite est certifiée ; pas de migration des anciennes
|
||||
sauvegardes ni de modification du canal public ou de Prism.
|
||||
|
||||
|
||||
## Visite — graine 42
|
||||
|
||||
Nouveau solo `visite187/natural/42`, `Sanctuary-Natural-Caves-187-Solo`.
|
||||
Vue 32, simulation 12, créatif, vol et commandes. Départ au-dessus du bassin
|
||||
ouest ; les anciennes visites restent conservées.
|
||||
|
||||
| Lieu | Raccourci de visite |
|
||||
| --- | --- |
|
||||
| Bassin ouest | `/tp -104.5 269 119.5 180 35` |
|
||||
| Bassin nord-est | `/tp 120.5 258 -116.5 180 35` |
|
||||
| Bassin sud | `/tp 8.5 270 156.5 180 35` |
|
||||
| Soufre, cavité haute | `/tp -215.5 219 -35.5` |
|
||||
| Soufre, cavité profonde | `/tp -203.5 150 -11.5` |
|
||||
| Soufre, cavité nord-ouest | `/tp -191.5 152 -179.5` |
|
||||
| Ancre nord, approche intérieure | `/tp 36.5 250 -168.5 180 0` |
|
||||
| Ancre nord-est | `/tp 193.5 251 -84.5 -135 0` |
|
||||
| Ancre est, récif | `/tp 214.5 444 16.5 -90 0` |
|
||||
| Ancre sud-est | `/tp 157.5 226 145.5 -45 0` |
|
||||
| Ancre sud, grotte | `/tp -71.5 176 205.5 0 0` |
|
||||
| Ancre sud-ouest | `/tp -96.5 252 181.5 45 0` |
|
||||
| Ancre ouest | `/tp -96.5 230 0.5 90 0` |
|
||||
| Ancre nord-ouest, récif | `/tp -102.5 404 -160.5 135 0` |
|
||||
@@ -0,0 +1,77 @@
|
||||
# WG-NATURE-190 — relief organique, déversoirs et butin des ruines
|
||||
|
||||
Branche `codex/natural-refinement-beta190`, graine de visite **42**, Minecraft 26.3.
|
||||
Nouveau profil de laboratoire `refined`, preset `sanctuary_test:natural_refinement_v1`.
|
||||
Les sauvegardes précédentes restent des témoins ; aucune régénération ni migration.
|
||||
|
||||
## Contrat de cet essai
|
||||
|
||||
Après la visite beta.189 : supprimer les terrasses systématiques du relief principal,
|
||||
conserver quelques inflexions locales douces, des affleurements suivant la pente et
|
||||
sans masque en cases de quatre blocs. Les captures du 30 septembre à 20:44 et 20:45
|
||||
montrent le relief principal ; les îlots aériens ne sont pas la cause des paliers.
|
||||
Comparer les débouchés voisins des étangs au lieu de retenir le premier rayon ;
|
||||
privilégier une sortie courte, large de quatre ou cinq blocs et peu creusée.
|
||||
Conserver les cavités, le portail et les ruines appréciés. Remplacer la table de
|
||||
craft de chaque ruine par un coffre de matériel en fer enchanté.
|
||||
Aucun calcul d’hydrologie régionale ajouté ; travail local borné, décoration existante.
|
||||
|
||||
## Réserve de conception, non implémentée
|
||||
|
||||
- Expansion à grandes surfaces étagées et falaises ciselées : conserver la variante
|
||||
beta.189 comme référence, avec une profondeur et une verticalité adaptées au thème.
|
||||
- Portails horizontaux : remplissage progressif avec une ressource renouvelable.
|
||||
Perles d’Ender, source Endermen ou butin sont des pistes ; ressource, nombre,
|
||||
recette et destination ne sont pas décidés. Aucun mécanisme d’activation ajouté ici.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Compilation et génération/réouverture natives réussies, graine 42 :
|
||||
`build/refined190b-cold.json` et `build/refined190b-warm.json`.
|
||||
|
||||
- 704 des 745 sommets échantillonnés gardent exactement la hauteur naturelle
|
||||
antérieure aux terrasses ; 41 épaules retouchées, écart maximum 3 blocs.
|
||||
30 246 densités des profondeurs et des îlots aériens identiques. Les corniches
|
||||
fines sont conservées ; une première interpolation qui pouvait les supprimer
|
||||
a été corrigée avant la visite.
|
||||
- Une sortie d’étang de cinq blocs de large, depuis X=150, Y=244, Z=-157 à -153
|
||||
vers X=154–155. Chenal de 4–5 blocs, dont une partie dans l’étang ; huit blocs
|
||||
retirés. Bord de chute irrégulier, 64 blocs préremplis puis écoulement natif.
|
||||
La recherche compare les ouvertures proches, reste bornée à dix blocs et
|
||||
deux cascades, et renonce si aucune bouche assez large ne convient.
|
||||
- 1 052 blocs de plage sèche, 434 plantes aquatiques, zéro tronc dans les
|
||||
colonnes contrôlées des plages et étangs. Fonds : 1 337 sable, 249 gravier,
|
||||
2 832 argile. Le choix du chenal ne crée pas de fond ou barrage artificiel.
|
||||
- Quatre ruines conservées, coffres en (25,164,-63), (-7,148,-79),
|
||||
(65,164,-79), (57,121,-79). Couvercles dégagés, table de butin conservée
|
||||
après réouverture. Une ou deux pièces en fer par coffre, enchantements
|
||||
de table de niveau 12–20 : armure, épée, pioche, hache ou pelle.
|
||||
32 tirages vérifiés à froid, puis 32 à chaud ; aucune pièce non enchantée.
|
||||
- Huit ancres, cartes du palais, gemmes, soufre, donjon et galerie contrôlés.
|
||||
Ni nouvelle dimension ni activation des portails ajoutée.
|
||||
|
||||
Démarrage natif : 12,331 s à froid / 0,673 s à chaud ; avec toute la batterie
|
||||
et le remplissage intégral des cartes : 81,505 s / 26,656 s. Plans locaux
|
||||
berges/déversoirs : 174 / 170 ms. Zéro région d’hydrologie calculée.
|
||||
Mesures obtenues pendant la suite de tests générale, sans garantie de durée
|
||||
identique sur chaque machine. Le solo ne lance pas ces contrôles lourds.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi en **9 min 22 s**,
|
||||
avec **265 GameTests réussis**. Après les derniers correctifs validés par le labo
|
||||
natif, le JAR optionnel a été réassemblé et le pack de test resynchronisé.
|
||||
Ses classes et ressources correspondent aux fichiers compilés, et sa copie
|
||||
assemblée est identique : `build/refined190-pack-receipt.json`.
|
||||
Aucune publication de canal ni mise à jour Prism. La validation esthétique reste
|
||||
celle de la prochaine visite ; les contrôles structurels ne la remplacent pas.
|
||||
|
||||
|
||||
## Visite
|
||||
|
||||
Solo `Sanctuary-Natural-Refinement-190-Solo`, run `visite190`, profil `refined`,
|
||||
graine 42. Copie neuve du monde de diagnostic arrêté, sauvegardes antérieures
|
||||
préservées. Spectateur et commandes activés, vue 32, simulation 12, Vulkan.
|
||||
Connexion confirmée à **21:01:12**, position (178,260,-146), face à la cascade.
|
||||
Les 49 cartes et cadres horizontaux sont présents, complets et verrouillés ;
|
||||
les huit ancres et le Bugrock sont revenus à l’état initial après les contrôles.
|
||||
`build/refined190-saved-atlas.json`, `build/refined190-saved-blocks.json` et
|
||||
`build/refined190-solo.log` conservent ces vérifications.
|
||||
@@ -0,0 +1,88 @@
|
||||
# EXP-202 — étagement du Nord et village enneigé
|
||||
|
||||
Branche `codex/north-ecology-beta202`, depuis beta.201. Retour R021 et trois
|
||||
captures du labo 201 examinées (12:11:22, 12:11:46 et 12:12:57, 4 octobre 2026).
|
||||
|
||||
Conserver les volumes, les cavités et les deux bassins 201, appréciés par le
|
||||
joueur. Refaire uniquement leur écologie et leurs sols : taïga, vallées ouvertes,
|
||||
podzol, épicéas géants natifs, neige progressive avec l'altitude, glaciers sur
|
||||
les hauteurs. Fin des aplats indépendants et du camouflage glace/glace bleue.
|
||||
Glace de cave limitée aux cavités protégées par une épaisseur de roche dans
|
||||
chaque direction ; aucune couche glacée appliquée aux falaises ou au dessous.
|
||||
Les cinq igloos doivent trouver une plaine réellement enneigée avant placement.
|
||||
Les pics de glace restent vanilla, comme demandé en R020.
|
||||
|
||||
Nouveaux profils 202 et journal distinct, diamètre nordique 1024. Aucune migration
|
||||
ni régénération d'un monde existant ; 200 et 201 gardent leurs générateurs.
|
||||
L'île de départ et les autres directions restent inchangées. Le chantier est
|
||||
isolé de la visite 201 encore ouverte. Validation native neuve Small/42, contrôles
|
||||
numériques sur 0, 42 et 4736390610738281858 ; pas de campagne de captures.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le champ de densité et les deux bassins délèguent exactement à la génération
|
||||
201. Seuls les sols, la végétation, la couverture neigeuse et le placement du
|
||||
village changent. La limite de neige suit l'altitude, avec un versant nord plus
|
||||
froid. Son épaisseur progresse de quelques couches à un bloc après la végétation.
|
||||
Les glaciers sont réservés aux hauts versants ; aucune glace bleue ne peint
|
||||
la surface. Les vallées restent en herbe, les grands boisements en podzol.
|
||||
|
||||
La taïga ordinaire et les épicéas géants réemploient les features 26.3 de taïga
|
||||
et `old_growth_spruce_taiga`. Aucun arbre géant custom ajouté. Les pics utilisent
|
||||
toujours `minecraft:ice_spike` et `minecraft:ice_patch`, sans remplacement des
|
||||
features natives. Les matériaux de cave glacée nécessitent du recouvrement et
|
||||
une assise rocheuse autour de la colonne ; les marges et dessous restent rocheux.
|
||||
Les cinq igloos cherchent une zone sèche, entièrement enneigée et peu pentue,
|
||||
avec classement par dénivelé. Aucun disque blanc ajouté sous le village.
|
||||
Caches numériques bornés ; pas de simulation hydrologique supplémentaire.
|
||||
|
||||
## Vérifications du 4 octobre 2026
|
||||
|
||||
- `north202Smoke` : neuf couples graine/taille (0, 42, 4736390610738281858 ;
|
||||
512/724/1024), égalité des colonnes et densités avec 201, présence de taïga,
|
||||
vallées, neige progressive et glacier minoritaire, protection du dessous.
|
||||
Recherche du village vérifiée aussi sur les graines 1 à 100. Toutes passent.
|
||||
- Ressources historiques de génération inchangées, sauf le preset public qui
|
||||
sélectionne désormais 202 et l'ajout du biome 202 au tag des ours polaires.
|
||||
Les trois noise settings de l'île de départ sont identiques aux fichiers 201.
|
||||
- Essai natif final : `build/worldgen-lab/north202-native-b`, Small, graine 42,
|
||||
diamètre de l'expansion 1024. Offrande réelle, interruption à 0/58 chunks,
|
||||
reprise, cinq igloos, une cave de guérison, relais de continuation orienté Nord.
|
||||
- Échantillon de taïga : 1517 blocs de troncs, 7677 blocs de feuilles,
|
||||
troncs atteignant 27 blocs, 13 empreintes de troncs larges, 3339 blocs de podzol.
|
||||
Pics natifs : 2910 blocs de glace au-dessus du terrain, maximum sondé 43 blocs.
|
||||
Ces nombres décrivent des échantillons, pas toute l'expansion.
|
||||
- Village : 333 des 339 colonnes sondées hors maisons portent de la neige.
|
||||
Lac : 8995 blocs de glace et 41795 blocs d'eau, sans fuite dans l'air.
|
||||
Caverne sondée : 49 blocs glacés et 2585 blocs d'air. Bordure sondée sur
|
||||
16 directions : 340 blocs rocheux, aucune glace compacte/bleue.
|
||||
|
||||
La validation esthétique reste celle de la visite. Pas de nouvelle campagne de
|
||||
captures. Visite native Small/42 uniquement : Windows et Medium/Large natifs
|
||||
ne sont pas vérifiés ici. Les sept autres directions restent provisoires.
|
||||
|
||||
## Livraison locale
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack
|
||||
-PsanctuaryFocusedTests=operator,realtime -PsanctuaryAtlasOnly=true` : succès,
|
||||
143 tâches, dont 126 exécutées ; sept GameTests ciblés et les smokes du projet.
|
||||
La suite native historique complète n'a pas été relancée (limite documentée
|
||||
pour beta.200). Logs : `build/north202-check-build.log` et
|
||||
`build/north202-native-final.log`.
|
||||
|
||||
MRpack : `build/Sanctuary-Test-beta.202.mrpack`, copie identique dans Downloads
|
||||
avec `Sanctuary-Test-beta.202-Guide.txt`. ZIP, versions 26.3 / Loader 0.19.5,
|
||||
hashes Fabric API, deux JAR et leurs 2258 classes compilées vérifiés.
|
||||
Reçu local : `build/north202-artifact.json`.
|
||||
|
||||
Taille : 12407853 octets ; SHA-256 :
|
||||
`b5ecdfcb2c4b1988f9402653a16c823b50f0661e8b1db0f0e7d6c21e9841f6f8`.
|
||||
|
||||
Aucun monde inclus dans le MRpack. Aucun canal public ni instance Prism modifié.
|
||||
Visite neuve `Sanctuary-North-202-42`, issue de l'essai natif final, créatif,
|
||||
commandes activées, Vulkan, vue 32, simulation 4. Départ en vol près du village.
|
||||
Village `(-212,191,-1292)`, taïga géante `(144,215,-976)`, lac `(-247,258,-765)`,
|
||||
pics vers `(-208,281,-880)`, relais `(-128,202,-1240)`.
|
||||
|
||||
Ouverture confirmée à 12:41:46 par `NORTH202_VISIT_OPEN`, joueur
|
||||
`(-160,228,-1247)` ; log `build/north202-visit-client.log`.
|
||||
@@ -0,0 +1,120 @@
|
||||
# EXP-200 — premier Nord glacial
|
||||
|
||||
Branche `codex/north-expansion-beta200`, depuis beta.199. Retours R015–R017.
|
||||
|
||||
## Résultat visé
|
||||
|
||||
Un continent nordique de diamètre nominal 2048 : crêtes, pics, vallées de taïga
|
||||
neigeuse, neige poudreuse localisée, cavernes de glace et lacs gelés fermés.
|
||||
Chèvres et ours polaires natifs de Minecraft 26.3 (choix confirmé).
|
||||
Un village de plusieurs igloos ; un seul contient le laboratoire souterrain
|
||||
vanilla avec villageois zombie. Pas d'autre famille de structures dans cette
|
||||
première tranche. Le Sud, l'Est, l'Ouest et les diagonales seront travaillés à part.
|
||||
|
||||
## Placement et ouverture
|
||||
|
||||
L'identité du relais reste celle de son secteur. Recherche proche dans un
|
||||
éventail de ±15° devant l'ancre, sans basculer dans une autre direction.
|
||||
La première version 200 porte le Nord à 2048 ; les sept autres reliefs gardent
|
||||
leur taille/profil provisoire de 199. Le relais de continuation du Nord conserve
|
||||
la direction, le climat et la taille du Nord.
|
||||
|
||||
Toute l'emprise est vérifiée vierge, réservée durablement et publiée avant les
|
||||
travaux. À cette échelle, `ready` signifie accès et relais préparés ; le reste
|
||||
est généré nativement pendant l'exploration. L'annonce SGA décrit cette ouverture,
|
||||
pas une pré-génération intégrale. Préparation bornée, sans simulation hydrologique
|
||||
globale ni réutilisation silencieuse de chunks déjà explorés.
|
||||
|
||||
## Contrat des sauvegardes
|
||||
|
||||
Nouveaux profils `shared_island200_small/medium/large`, journal distinct
|
||||
`sanctuary-lab-expansions200`. Aucun monde existant modifié ou migré ; les profils
|
||||
et ressources 196–199 restent inchangés. IDs historiques conservés. Les essais
|
||||
activent uniquement des expansions dans des mondes de développement neufs,
|
||||
graine 42 puis relevés déterministes sur 0 et 4736390610738281858.
|
||||
|
||||
## Vérifications prévues
|
||||
|
||||
Direction et diamètre, déterminisme, cavités, confinement des eaux, village
|
||||
et laboratoire habitable, vraie activation, sauvegarde et reprise. Compilation,
|
||||
`check build` et assemblages. Ne pas confondre vérification technique et validation
|
||||
esthétique : le rendu reste une première version à visiter.
|
||||
|
||||
## Implantation et contrôles intermédiaires
|
||||
|
||||
Le champ nordique compose des crêtes déformées, des sommets secondaires et des
|
||||
vallées, puis soustrait des passages et chambres 3D. Cache numérique borné par
|
||||
worker ; aucune lecture de chunks voisins pour dessiner le terrain. Six biomes
|
||||
nordiques reprennent l'écologie native de 26.3 ; les sources d'eau/lave dispersées
|
||||
et salles de monstres vanilla sont retirées de ces seuls profils. Les bassins
|
||||
ont un fond rocheux continu et une couverture de glace, sans hydrologie globale.
|
||||
|
||||
Village : cinq igloos natifs orientés et ajustés individuellement au sol. Un
|
||||
seul laboratoire, avec échelle, coffre vanilla, potion de faiblesse, villageois
|
||||
et villageois zombie persistants. Les arbres évitent les maisons et les lacs.
|
||||
La faune initiale utilise les attributs du biome à la surface réelle, car la
|
||||
sélection vanilla à Y=1600 échantillonnait le vide ; règles de spawn natives
|
||||
conservées. Ours polaires également admis sur la glace des lacs.
|
||||
|
||||
- `north200Smoke` : 9 couples graine/taille, huit éventails, relief/cavités,
|
||||
bassins analytiquement fermés, journal 2048 et refus de 2048 en génération 199.
|
||||
- Essai intermédiaire Small/42, 2 Gio : vraie offrande nord, interruption à
|
||||
0/58 chunks, puis reprise. Village de cinq igloos et un laboratoire contrôlés ;
|
||||
lac avec 3475 blocs de glace et 10525 blocs d'eau, sans voisin d'air sous l'eau ;
|
||||
136 blocs de glace et 1926 blocs d'air dans les colonnes de caverne sondées.
|
||||
Ce sont des sondages locaux, pas les totaux du continent.
|
||||
- Le premier passage a révélé un barreau manquant sous la trappe : raccord
|
||||
corrigé et vérifié lors du second passage. Les essais antérieurs restent
|
||||
conservés sous `build/worldgen-lab/north200-native-a` et `north200-native-b`.
|
||||
- Les 148 ressources historiques de génération du module Test restent identiques
|
||||
à beta.199 (hors preset public, qui sélectionne maintenant 200).
|
||||
|
||||
## Essai final et livraison locale — 4 octobre 2026
|
||||
|
||||
Le parcours serveur final `north200-native-final/small/42` répète l'activation
|
||||
réelle et l'interruption/reprise sur les sources livrées. Les 58 chunks d'accès
|
||||
et de relais passent à FULL ; cinq igloos, un laboratoire, le lac et la cavité
|
||||
glacée sont vérifiés. Les entités sauvegardées comprennent six ours polaires,
|
||||
trois renards, huit lapins, un villageois et un villageois zombie. Aucune chèvre
|
||||
observée dans ce petit échantillon : leurs règles natives sont présentes dans
|
||||
les sommets et pentes, sans validation d'une rencontre dans ce parcours.
|
||||
|
||||
Le client Vulkan a ouvert une copie de visite neuve, `Sanctuary-North-200-42`,
|
||||
avec confirmation `NORTH200_VISIT_OPEN` à `(-200,224,-968)`. Créatif, commandes,
|
||||
vue 32 chunks et simulation 4 ; shaders désactivés. Le village se trouve autour
|
||||
de `(-200,212,-988)`, la trappe spéciale vers `(-193,210,-961)`, le lac sondé en
|
||||
`(-64,187,-1204)` et le relais suivant en `(-176,215,-2121)`.
|
||||
|
||||
Archive locale : `build/Sanctuary-Test-beta.200.mrpack`, également copiée dans
|
||||
`/Users/koka/Downloads/`, avec `Sanctuary-Test-beta.200-Guide.txt`.
|
||||
12 366 771 octets ; SHA-256 :
|
||||
`2a29b35613f95540ec9d910e043a1f6f26b2dfe7d6d95c50997ba4bbde203cdc`.
|
||||
Intégrité ZIP, version, dépendances Minecraft 26.3 / Fabric Loader 0.19.5,
|
||||
empreintes Fabric API, deux JAR identiques au build et 2246 classes compilées
|
||||
vérifiés. Les trois tailles sont présentes ; aucune sauvegarde embarquée.
|
||||
Reçu : `build/north200-artifact.json`. Aucun canal public ni Prism synchronisé.
|
||||
|
||||
La commande complète `check build assemblePack assembleTestPack` a été lancée,
|
||||
puis interrompue volontairement pendant
|
||||
`retainedHydrologySurvivesNativeDecoration` (ancienne génération 24, préparation
|
||||
de 112 chunks FULL). Les captures de threads montrent l'attente de génération ;
|
||||
elles ne démontrent pas un interblocage. Cette suite générale n'est pas validée.
|
||||
Logs : `build/north200-check-build.log`, `north200-fullcheck-threads*.txt`.
|
||||
Les essais serveur nordiques sont distincts de cette suite historique.
|
||||
|
||||
Les sept GameTests `operator,realtime` passent avec le fixture plat
|
||||
`-PsanctuaryAtlasOnly=true`, indépendamment de l'essai natif nordique.
|
||||
Cette sélection ne remplace pas les 265 tests de la suite générale.
|
||||
La commande `./gradlew check build assemblePack assembleTestPack
|
||||
-PsanctuaryFocusedTests=operator,realtime -PsanctuaryAtlasOnly=true` termine avec
|
||||
succès : 141 tâches, dont 110 exécutées et 31 à jour. Elle inclut les tests
|
||||
numériques, notamment `north200Smoke`. Log complet :
|
||||
`build/north200-check-build-focused.log`. Archive revérifiée après l'assemblage
|
||||
final, identique aux JAR et classes finaux ; daemon de build arrêté ensuite.
|
||||
|
||||
Limites : validation native détaillée sur Small/42, neuf couples graine/taille
|
||||
pour les contrôles numériques, pas de lancement Windows ni de visite native
|
||||
Medium/Large sur cette version. Les 2048 blocs désignent le diamètre nominal,
|
||||
pas une pré-génération intégrale. Le démarrage à 32 chunks a produit des retards
|
||||
de ticks avec les contrôles et un autre client simultanés sur le Mac 8 Gio :
|
||||
aucune conclusion de fluidité ni correction des saccades n'est revendiquée.
|
||||
@@ -0,0 +1,87 @@
|
||||
# EXP-203 — fractures, lisières et taïga géante
|
||||
|
||||
Branche `codex/north-fracture-beta203`, depuis beta.202. Retour R022.
|
||||
Le rendu 202 est globalement validé. Passe ciblée demandée : ajouter de fortes
|
||||
variations locales de volume et des trous par bruit 3D, retirer la neige ajoutée
|
||||
après la végétation, brouiller les limites du podzol, donner aux grands épicéas
|
||||
une hauteur pouvant atteindre 64 et varier les matières des sommets glacés.
|
||||
Aucun nouveau village : la proposition supplémentaire est retirée par le joueur.
|
||||
Les cinq igloos existants restent présents.
|
||||
|
||||
Nouveaux profils 203 seulement, Nord 1024. Aucun monde existant régénéré ni
|
||||
migré ; profils 200–202 inchangés. Bassins et basses plaines d'accueil protégés
|
||||
de la déformation. Pics de glace vanilla conservés. Arbres avec moteur natif,
|
||||
configuration propre au biome géant ; aucune modification globale des épicéas.
|
||||
Pas de simulation d'érosion ou d'hydrologie supplémentaire, caches bornés.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le champ 203 combine des déplacements verticaux locaux (deux échelles de bruit)
|
||||
et un bruit volumique négatif de plus grande portée. Les nouvelles poches
|
||||
peuvent traverser le toit ou le dessous, contrairement aux caves fermées 202.
|
||||
Le bruit est atténué sur les basses plaines, les bords et tout autour des lacs.
|
||||
Les seuils de sommet restent lisibles ; le détail 3D module les roches et glaces.
|
||||
|
||||
La passe de neige après décoration n'est pas appelée pour 203. Les prairies
|
||||
froides gardent leur herbe ; la neige demeure comme matériau de sommet,
|
||||
substrat nécessaire aux pics vanilla et matériau des igloos existants.
|
||||
Le champ de podzol comporte deux fréquences de détail aux lisières. Le
|
||||
`alter_ground` circulaire des nouveaux grands arbres est supprimé.
|
||||
|
||||
Les arbres réemploient `giant_trunk_placer` et `mega_pine_foliage_placer` de
|
||||
Minecraft 26.3. Configuration locale : hauteur 32 + aléa 0–16 + aléa 0–16,
|
||||
soit 32 à 64 blocs, couronne de 18–24 ; mélange de géants et de petits épicéas.
|
||||
Les features `ice_spike` / `ice_patch` restent natives et inchangées.
|
||||
|
||||
## Contrôles
|
||||
|
||||
`north203Smoke` passe sur neuf couples graine/taille (0, 42,
|
||||
4736390610738281858 ; Small/Medium/Large). Les déplacements locaux sondés vont
|
||||
jusqu'à environ −62 / +56 blocs selon le cas. Ouvertures de surface présentes,
|
||||
densité bornée, lacs et leurs coques inchangés, lisières de podzol irrégulières.
|
||||
Recherche des cinq igloos vérifiée également sur les graines 1 à 32.
|
||||
Les ressources worldgen historiques sont inchangées, hors sélection du nouveau
|
||||
preset public et ajout du biome 203 au tag des ours polaires. Les noise settings
|
||||
de l'île de départ restent identiques à 202. Aucun test visuel Windows ni
|
||||
nouvelle campagne de captures : le rendu est à juger dans le labo.
|
||||
|
||||
|
||||
Essai natif final : `build/worldgen-lab/north203-native-b`, Small/graine 42.
|
||||
Vraie offrande, interruption à 0/58 chunks, reprise et relais prêts. Cinq igloos
|
||||
et un laboratoire conservés. Recherche de cave orientée vers un intérieur
|
||||
suffisamment épais, plutôt que le sommet le plus haut désormais fracturé.
|
||||
|
||||
Dans les échantillons : 4218 blocs de troncs, 10450 de feuilles, 21 empreintes
|
||||
de troncs larges ; tronc le plus haut mesuré 59 blocs (plafond configuré 64).
|
||||
Sommet : 309 blocs de glace bleue, 63 de glace ordinaire, 2154 de glace compacte,
|
||||
1219 de neige pleine et 1694 de roche. Pics vanilla : maximum sondé 13 blocs
|
||||
au-dessus du terrain. Prairie : zéro couche de neige parmi 339 colonnes.
|
||||
Lac : 8995 blocs de glace et 41795 d'eau, sans fuite ; caverne sondée : 226 blocs
|
||||
glacés et 1878 d'air. Bordure : 339 blocs rocheux, aucune glace compacte/bleue.
|
||||
Il s'agit de contrôles locaux, pas d'un comptage de toute l'expansion.
|
||||
|
||||
|
||||
## Livraison
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack
|
||||
-PsanctuaryFocusedTests=operator,realtime -PsanctuaryAtlasOnly=true` : succès,
|
||||
144 tâches dont 120 exécutées, sept GameTests ciblés et les smokes du projet.
|
||||
La suite native historique complète n'a pas été relancée (limite beta.200).
|
||||
Logs : `build/north203-check-build.log` et `build/north203-native-final.log`.
|
||||
|
||||
MRpack vérifié : `build/Sanctuary-Test-beta.203.mrpack`, copie identique dans
|
||||
Downloads avec le guide. Archives, versions, dépendances, empreintes Fabric API,
|
||||
JAR et 2265 classes compilées vérifiés ; reçu `build/north203-artifact.json`.
|
||||
|
||||
12433661 octets ; SHA-256 :
|
||||
`ba160b4134c0b9dbe402e1834bdbec4f5561bfd6bd9dc147590dd76ee30dbf4f`.
|
||||
|
||||
Visite neuve `Sanctuary-North-203-42`, issue de l'essai natif final. Créatif,
|
||||
commandes activées, Vulkan, vue 32, simulation 4 ; départ au-dessus de la taïga.
|
||||
Village `(-212,191,-1292)`, forêt `(144,215,-976)`, pics `(-208,278,-944)`,
|
||||
premier lac `(-247,258,-765)`. Aucun canal public ni instance Prism modifié.
|
||||
Pas de sauvegarde embarquée dans le pack ni de revendication sur les saccades.
|
||||
Essai natif Small/42 seulement ; Medium/Large sont contrôlés numériquement.
|
||||
|
||||
Ouverture confirmée à 13:12:20 par `NORTH203_VISIT_OPEN`, départ
|
||||
`(196,292,-931)`, backend Vulkan confirmé dans `build/north203-visit-client.log`.
|
||||
@@ -0,0 +1,90 @@
|
||||
# EXP-201 — Nord compact, glacier et bosquets
|
||||
|
||||
Branche `codex/north-glacier-beta201`, depuis beta.200 ; retours joueur R019–R020.
|
||||
|
||||
## Résultat demandé
|
||||
|
||||
Le diamètre du Nord passe de 2048 à 1024 blocs. Remplacer la nappe blanche perçue
|
||||
par un paysage de glace lisible : pics de glace, glacier, deux lacs gelés visibles,
|
||||
bosquets localisés et contraste roche/glace/neige. Conserver les cinq igloos et
|
||||
le laboratoire avec villageois zombie. Les lacs doivent contenir de l'eau sous
|
||||
leur glace ; la végétation doit être réellement générée, pas seulement déclarée.
|
||||
Les pics utilisent explicitement `minecraft:ice_spike` et `minecraft:ice_patch`
|
||||
de Minecraft 26.3, avec leurs placements natifs. Aucune forme de pic recodée.
|
||||
|
||||
Le Nord ne doit pas devenir le catalogue de tous les biomes froids. Son identité
|
||||
est le glacier habité ; les grandes forêts boréales, côtes froides et autres
|
||||
paysages restent des pistes pour NO/NE. Leur répartition n'est pas décidée ici.
|
||||
|
||||
## Contrat de génération
|
||||
|
||||
Nouveaux profils 201 et journal `sanctuary-lab-expansions201`, destinés uniquement
|
||||
aux nouveaux mondes. Les profils et le champ nordique 200 restent disponibles
|
||||
pour les sauvegardes historiques ; aucune conversion des expansions 2048 déjà
|
||||
créées, ni régénération de chunks. Île de départ Small/Medium/Large inchangée.
|
||||
Placement dans l'éventail existant de ±15°. Préparation bornée de l'arrivée et du
|
||||
relais, puis exploration. Les sept autres directions restent provisoires.
|
||||
|
||||
## Vérifications attendues
|
||||
|
||||
Contrôle de taille et de direction, stabilité de la génération 200, proportions
|
||||
et visibilité des surfaces glaciaires, lacs fermés, relief vertical, bosquets et
|
||||
village. Vérification native de vrais troncs/feuilles, glace de surface non
|
||||
recouverte et pics élevés. Graines numériques 0, 42 et 4736390610738281858 ; visite
|
||||
native neuve Small/42. Pas de campagne de screenshots ni d'hydrologie globale.
|
||||
|
||||
Les contrôles techniques ne remplacent pas le retour esthétique du joueur.
|
||||
|
||||
## Intégration
|
||||
|
||||
La 200 posait une couche de neige pendant le traitement du sol, avant les
|
||||
features végétales ; les arbres natifs de taïga passent par les saplings
|
||||
`spruce_checked` / `pine_checked`, qui exigent un support viable. La 201 laisse
|
||||
le sol des bosquets en herbe jusqu'à leur plantation. Les lacs et le glacier ne
|
||||
reçoivent plus la feature `freeze_top_layer`, afin que leur glace reste visible.
|
||||
Les zones de pics conservent au contraire le bloc de neige requis par la feature
|
||||
native `minecraft:spike`. Ses références et sa fréquence restent vanilla.
|
||||
|
||||
Le substrat compact associe une crête, des épaules glaciaires, deux champs de
|
||||
pics natifs, deux bosquets et deux bassins fermés. Caches numériques bornés ;
|
||||
aucune simulation hydrologique globale. Le village est rapproché du premier lac.
|
||||
Le relief, les matériaux et le choix de biomes de la 200 gardent leur branche
|
||||
de génération historique.
|
||||
|
||||
## Résultats — 4 octobre 2026
|
||||
|
||||
- `north201Smoke` : neuf couples graine/taille, Nord 1024, relief, parts de glace,
|
||||
bosquets, lacs et champs de pics, confinement des bassins, déterminisme et
|
||||
routage historique 200. `north200Smoke` reste passant. Les 155 ressources
|
||||
historiques worldgen du module Test restent identiques, hors preset public.
|
||||
- Serveur neuf Small/42, `build/worldgen-lab/north201-native-a` : vraie offrande,
|
||||
interruption à 0/58 chunks, reprise et accès préparés. Cinq igloos, une cave
|
||||
avec trappe/échelle/coffre/potion ; relais de continuation orienté Nord.
|
||||
- Échantillon du bosquet : 395 blocs de troncs et 3294 blocs de feuilles
|
||||
d'épicéa, répartis sur 56 colonnes de troncs (pas un comptage d'arbres entiers).
|
||||
Champ de pics natifs : 1838 blocs de glace compacte au-dessus du terrain,
|
||||
maximum sondé de 11 blocs. Lac : 8995 blocs de couverture gelée et 41795 blocs
|
||||
d'eau ; 7602 surfaces de glace visibles contrôlées, zéro couche de neige dessus.
|
||||
Caverne : 561 blocs de glace et 2585 blocs d'air sondés. Totaux locaux uniquement.
|
||||
- `./gradlew check build assemblePack assembleTestPack
|
||||
-PsanctuaryFocusedTests=operator,realtime -PsanctuaryAtlasOnly=true` : succès,
|
||||
sept GameTests ciblés et tous les smokes associés, 142 tâches dont 118 exécutées.
|
||||
La suite native historique complète n'a pas été relancée (limite beta.200).
|
||||
Logs : `build/north201-check-build.log` et `build/north201-native-a.log`.
|
||||
|
||||
Livraison locale : `build/Sanctuary-Test-beta.201.mrpack`, copie dans Downloads
|
||||
avec le guide. 12 386 623 octets ; SHA-256 :
|
||||
`37925f012e4096c253f25e573305237e71114448d866a57f3fbad6e3da6b3d89`.
|
||||
ZIP, dépendances 26.3 / Loader 0.19.5, empreintes Fabric API, JAR et 2252 classes
|
||||
compilées vérifiés. Les trois tailles sont incluses, aucune sauvegarde embarquée.
|
||||
Reçu : `build/north201-artifact.json`. Aucun canal public ni Prism modifié.
|
||||
|
||||
Visite préparée : `Sanctuary-North-201-42`, nouveau monde issu de l'essai natif.
|
||||
Départ près du lac en `(-142,323,-705)`, créatif, commandes, Vulkan, vue 32 et
|
||||
simulation 4. Ouverture confirmée par `NORTH201_VISIT_OPEN` dans
|
||||
`build/north201-visit-client.log`. Village `(-128,205,-637)`, lac `(-247,258,-765)`, bosquet
|
||||
`(-324,258,-780)`, champ de pics vers `(-273,-1059)`, relais `(-128,202,-1240)`.
|
||||
|
||||
Limites : visite native Small/42 seulement, pas d'essai Windows ou Medium/Large
|
||||
natif sur cette version. Pas de revendication sur les saccades. Les paysages
|
||||
NO/NE ne sont pas implémentés ; les anciens mondes ne changent pas de taille.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,68 @@
|
||||
# ORIGIN-182 — galerie inférieure et rosace
|
||||
|
||||
Suite du retour en jeu sur beta.181, le 30 septembre 2026. Branche
|
||||
`codex/origin-gallery-beta182`, dans le checkout de développement réutilisé
|
||||
`rocky-ecology-beta178`. La palette naturelle de l’origine est validée par le
|
||||
créateur. Les élévations gênent les huit directions ; elles doivent être
|
||||
placées entre les axes. Essayer une cloche vitrée en rosace et développer
|
||||
la scène souterraine. L’origine reste en l’air.
|
||||
|
||||
La mention « le truc dans les airs » est interprétée ici comme la rosace,
|
||||
en attendant une éventuelle précision ; l’ISS reste un chantier séparé.
|
||||
|
||||
## Contrat du nouveau monde
|
||||
|
||||
Nouveau preset facultatif `sanctuary_test:gallery_v1`, profil `gallery`,
|
||||
graine 42. Les profils précédents et la génération normale restent disponibles
|
||||
avec leur génération propre. Aucun ancien monde, chunk ou format de sauvegarde
|
||||
n’est modifié. Aucune expansion ni dimension n’est activée par ce prototype.
|
||||
|
||||
La galerie sélectionne une cavité centrale sous X=0/Z=0 par un échantillonnage
|
||||
borné du champ de densité existant. Deux chambres voisines forment une boucle
|
||||
avec elle ; les passages suivent des failles inclinées, les sols ondulent et
|
||||
les plafonds sont voûtés. Roche dominante, calcite par veines, mousse ponctuelle,
|
||||
quelques reliefs de tuf. Pas de murs rectangulaires plaqués sur toute la caverne.
|
||||
Un socle d’obsidienne central et deux montants sombres interrompus donnent un
|
||||
premier emplacement au portail inférieur. C’est une scène architecturale :
|
||||
ni portail actif, ni recette d’allumage ou téléportation ne sont implémentés.
|
||||
|
||||
L’origine conserve la palette, le sol à huit directions et le bloc en
|
||||
(0,320,0), lumineux et monochrome. Une cloche de verre clair et blanc avec
|
||||
huit nervures de cuivre patiné remplace les trois élévations. Les supports
|
||||
sont entre les axes ; huit ouvertures basses conservent vues et passages.
|
||||
L’ISS, les huit salles d’ancres et les trois secteurs de gemmes ne sont pas
|
||||
ajoutés dans ce lot.
|
||||
|
||||
Les deux lieux sont posés par chunk à la génération, sans ticker, sans
|
||||
recherche globale d’eau et sans journal de progression. Le plan souterrain
|
||||
est calculé une fois pour la source de biomes puis réutilisé. Une sauvegarde
|
||||
recharge les blocs, elle ne reconstruit pas les lieux.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Compilation et export du lanceur réussis. Monde neuf `solo182a/gallery/42` :
|
||||
contrôles de la beta.180 conservés (567 échantillons de densité, trois cerisiers,
|
||||
bassins de 1 232 et 1 221 blocs, cinq spawners et quatre caches avec les gemmes).
|
||||
Zéro région d’hydrologie calculée.
|
||||
|
||||
La galerie contient 1 558 colonnes sculptées ; 1 514 surfaces praticables
|
||||
appartiennent au même réseau, connecté par marches d’un bloc maximum.
|
||||
Les trois centres de salle sont en (0,175,0), (-34,169,-26), (-38,146,24).
|
||||
Le socle porte l’obsidienne en (0,177,0). La rosace compte 1 841 blocs avec
|
||||
le fragment rocheux : huit axes libres à hauteur du joueur et noyau lumineux
|
||||
sans relais activé. Rapport local `build/gallery182-quick-result.json`.
|
||||
Arrêt du serveur de préparation et sauvegarde terminés proprement.
|
||||
|
||||
Solo neuf préparé sous `visite182/gallery/42`, `Sanctuary-Gallery-182-Solo`,
|
||||
vue 32, créatif et commandes, première vue sur la galerie.
|
||||
Visites : galerie `/tp 0.5 177 7.5 180 0` ; rosace
|
||||
`/tp 0.5 319 5.5 180 0`. Rendu et proportions à juger en jeu sur la graine 42 ;
|
||||
aucune autre graine ni capture du rendu n’est revendiquée.
|
||||
Suite générale `check build assemblePack assembleTestPack` réussie en 8 min 1 s,
|
||||
265/265 GameTests ; journal `build/gallery182-check-build.log`.
|
||||
|
||||
Entrée dans le solo confirmée depuis par le créateur. Le journal
|
||||
`build/gallery182-solo.log` enregistre KokaLab connecté le 30 septembre à
|
||||
02:25:12, vue 32 chunks. Son retour valide la palette, la rosace et les étangs ;
|
||||
la correction du portail et les lieux manquants sont suivis dans
|
||||
[DISCOVERY-183](island-discovery-beta183.md).
|
||||
@@ -0,0 +1,96 @@
|
||||
# ORIGIN-181 — repère central dans le ciel
|
||||
|
||||
Retour du 30 septembre 2026 sur beta.180. Branche `codex/origin-landmark-beta181`.
|
||||
Le créateur apprécie le relief global et le donjon minier. Le prochain résultat
|
||||
à visiter est une petite construction ouverte qui signale le centre du monde,
|
||||
avec le bloc originel exactement en **X=0, Y=320, Z=0**. Cette décision remplace
|
||||
les positions antérieures envisagées pour ce bloc ; elle ne rétablit pas le palais.
|
||||
|
||||
## Premier résultat
|
||||
|
||||
Nouveau preset facultatif `sanctuary_test:origin_v1`, profil `origin`, graine 42.
|
||||
Terrain, écologie, bassins, cerisiers et donjon de beta.180 réutilisés. Un fragment
|
||||
rocheux porte une rose des vents à huit directions, en tuf, calcite et deepslate,
|
||||
avec cuivre patiné, quelques mousses et un croissant brisé. Le sol principal est
|
||||
à Y=318, le piédestal à Y=319 et le Bug Rock à Y=320. La silhouette culmine à 328.
|
||||
Circulation ouverte, pas de toiture ni de conteneur. Environ 19 blocs de large.
|
||||
|
||||
Le bloc réutilise `sanctuary:origin_block` et sa texture existante : allumé en
|
||||
noir et blanc, lumière 15, masque des huit relais à zéro. Aucune ancre n’est
|
||||
activée. Ce premier lieu est un repère visuel ; pas encore d’interaction, de
|
||||
quête ni de trajet d’accès en survie. Le créatif et les commandes du labo
|
||||
permettent de juger le volume avant de définir le parcours.
|
||||
|
||||
La pose se fait une seule fois par chunk, pendant la décoration native, sur
|
||||
quatre chunks autour de l’origine. Aucune recherche régionale, aucun calcul
|
||||
par tick, aucune entité ni hydrologie supplémentaire. La patine et la base
|
||||
rocheuse varient avec la graine ; la silhouette reste celle de ce premier essai.
|
||||
|
||||
## Sauvegardes et validation
|
||||
|
||||
Nouveau monde uniquement. Le preset beta.180 et la génération normale ne
|
||||
reçoivent pas ce lieu. Aucun ancien chunk ni format de sauvegarde n’est migré.
|
||||
La reprise recharge les blocs sauvegardés sans reconstruction. Pas de publication
|
||||
du canal, de mise à jour Prism ni d’installation serveur personnel.
|
||||
|
||||
Le contrôle natif vérifie l’origine exacte, la présence du plan dans les quatre
|
||||
chunks, la circulation, l’ouverture vers le ciel et l’état monochrome lumineux.
|
||||
Les vérifications de beta.180 restent appliquées au nouveau profil.
|
||||
|
||||
Essai natif `solo181a`, graine 42, réussi : 1 224 blocs du repère présents,
|
||||
quatre chunks, origine et passages conformes, 567 comparaisons de densité
|
||||
conservées et zéro région d’hydrologie. Trois cerisiers, surfaces de bassins
|
||||
de 1 232 et 1 221 blocs, cinq spawners et quatre caches avec les quatre gemmes
|
||||
validés. La lecture des chunks enregistrés confirme le bloc à (0,320,0),
|
||||
`lit=true`, `relays=0`. Rapport : `build/origin181-quick-result.json`.
|
||||
|
||||
Compilation et export du lanceur réussis. Le solo neuf
|
||||
`visite181/origin/42`, nommé `Sanctuary-Origin-181-Solo`, est ouvert le
|
||||
30 septembre à 01:59 : Vulkan Apple M1, KokaLab connecté en (18.5,333,23.5),
|
||||
vue 32 chunks. Approche à pied pour la visite : `/tp 0.5 319 5.5 180 0`.
|
||||
L’appréciation esthétique reste à faire en jeu ; aucune capture du rendu
|
||||
n’est revendiquée. Seule la graine 42 est validée dans cette itération.
|
||||
Suite générale `check build assemblePack assembleTestPack` réussie en
|
||||
12 min 53 s, journal `build/origin181-check-build.log` dans le checkout
|
||||
`storyquest-beta173`. Contrôles et assemblages terminés avant la suite beta.182.
|
||||
|
||||
Retour en jeu du créateur : palette et caractère naturel validés. Les trois
|
||||
élévations empiètent sur les directions ; prochaine variante avec supports
|
||||
entre les huit axes et piste d’une cloche vitrée en rosace. Garder l’origine
|
||||
en l’air pendant l’essai. Développer d’abord la galerie souterraine, puis
|
||||
le lieu aérien ; précision sur ce dernier demandée pendant la séance.
|
||||
|
||||
## Suite de la séance : décisions et pistes, pas encore implémentées
|
||||
|
||||
- **Gemmes :** trois secteurs souterrains distincts, émeraude, rubis et saphir,
|
||||
avec veines exposées sur les surfaces rocheuses. Un stock limité comparable
|
||||
aux diamants à l’échelle de l’île, pas par échantillon de chunks. Le chiffre
|
||||
exact par gemme reste à régler. Proposition : compter séparément le stock
|
||||
généré de chaque gemme ; densité locale lisible, rareté globale préservée.
|
||||
- **Soufre :** intégrer une poche de sulfur caves sur l’île principale. Le
|
||||
profil beta.180 ne contient pas ce biome, même si le socle historique connaît
|
||||
le soufre. Choisir une poche dédiée plutôt que repeindre les cavités humides.
|
||||
- **Huit ancres :** salles naturelles sculptées dans leur matière locale,
|
||||
creux, reliefs irréguliers, bas-reliefs, végétation et piédestal portant le
|
||||
bloc existant. Chaque lieu a sa géographie ; pas de ponts imposés vers le centre.
|
||||
- **Galerie inférieure :** caverne centrale sous X=0/Z=0, salles reliées par
|
||||
des failles, dénivelés et emplacement de portail en obsidienne. Altitude,
|
||||
emprise et lien exact avec les dimensions à préciser. Le palais abandonné
|
||||
n’est pas réintroduit. Proposition technique : sonder un nombre borné de
|
||||
points du champ de densité existant, sélectionner une cavité et ne retoucher
|
||||
que ses passages ; calcul à la génération, aucun raycast permanent.
|
||||
- **Activation :** envie de remplacer le briquet par un objet fabriqué au
|
||||
métabli, reliant préparation et découverte. Recette, composants et effet
|
||||
encore ouverts ; ne pas modifier le portail vanilla avant ce contrat.
|
||||
- **Expansions :** privilégier l’expérience de surface, avec des masses tantôt
|
||||
horizontales, tantôt verticales. La profondeur dépendra de chaque île ; ne
|
||||
pas généraliser automatiquement toutes les cavités de Sanctuary à chaque île.
|
||||
- **ISS :** chantier architectural séparé dans la bande Y=512–640. Des
|
||||
schematics pourront servir à analyser échelle, modules, circulation et
|
||||
palette ; construire ensuite un plan original. Aucun fichier ISS reçu ici.
|
||||
Fonction et quête restent à définir.
|
||||
|
||||
Ordre proposé pour les essais suivants : origine visible → une salle d’ancre
|
||||
dans une cavité existante → trois secteurs de gemmes et une poche de soufre →
|
||||
déclinaison des autres ancres et galerie inférieure. Chaque essai garde son
|
||||
monde témoin et se juge en solo ; pas de grande étude scientifique imposée.
|
||||
@@ -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,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,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,61 @@
|
||||
# WG-RESTORE-191 — revenir avant les plateaux
|
||||
|
||||
Branche `codex/restore-relief-beta191`, Minecraft 26.3, graine de visite 42.
|
||||
Nouveau profil `original`, preset `sanctuary_test:pre_terraces_v1`.
|
||||
|
||||
## Contrat
|
||||
|
||||
Le retour beta.190 rejette toujours le relief et les affleurements ajoutés depuis
|
||||
les plateaux. Restaurer exactement le champ de densité **beta.188**, avant les
|
||||
terrasses beta.189 et les épaules beta.190. Retirer leurs passes d’affleurements
|
||||
et leur exclusion d’arbres sur ces affleurements. La géologie d’origine retrouve
|
||||
sa responsabilité : pierre, strates, gisements, cavités et pointes existantes.
|
||||
|
||||
Conserver les corrections indépendantes appréciées : ancres au sol, berges,
|
||||
cascade large, galerie éclairée et coffres de fer enchanté dans les ruines.
|
||||
Cela restaure le relief et la géologie de base, pas tous les blocs d’un monde
|
||||
beta.188 puisque ces aménagements récents restent présents.
|
||||
|
||||
L’intention pour la suite est un récif avec creux, cavités et pointes, avec
|
||||
quelques formations singulières par endroits. Aucun nouveau lissage, plateau
|
||||
ni déformation généralisée dans cette remise à plat du travail.
|
||||
Les variantes 189/190 restent des références conservées, hors de ce nouveau profil.
|
||||
Nouveaux mondes seulement ; aucune sauvegarde existante modifiée ou régénérée.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Génération et réouverture natives réussies, graine 42 :
|
||||
`build/original191a-cold.json` et `build/original191a-warm.json`.
|
||||
|
||||
- **134 480 densités identiques** aux paramètres enregistrés de beta.188, de
|
||||
Y=0 à Y=632 sur l’île et ses îlots ; aucun remappage de hauteur activé.
|
||||
- Passes d’affleurements 189/190 court-circuitées dans ce profil. Leur masque
|
||||
est inactif sur les 1 681 colonnes contrôlées ; la règle de géologie d’origine
|
||||
et les plantations natives reprennent la surface hors des aménagements réservés.
|
||||
- Comparaison au commit beta.188 `658aaf8` : corps du calcul de densité, paramètres
|
||||
de bruit, îlots aériens et règle de géologie identiques. Preuve locale :
|
||||
`build/original191-baseline-receipt.json`.
|
||||
- Huit ancres, 49 cartes et cadres du palais, salle inférieure et éclairage,
|
||||
quatre ruines avec coffres enchantés, donjon, gemmes et soufre contrôlés.
|
||||
- Cascade de cinq blocs de large conservée, chenal de 4–5 blocs et huit blocs
|
||||
de roche retirés ; aucune nouvelle déformation de montagne pour la produire.
|
||||
Zéro tronc dans les colonnes contrôlées des bassins et berges.
|
||||
|
||||
Démarrage natif : 12,281 s à froid et 0,738 s à chaud ; avec tous les contrôles
|
||||
et le remplissage des cartes : 79,283 s / 22,798 s. Zéro région d’hydrologie.
|
||||
Ces contrôles de laboratoire ne sont pas lancés au démarrage du solo.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi en **7 min 40 s**,
|
||||
**265 GameTests réussis**. Le JAR est identique aux classes et ressources compilées
|
||||
et à sa copie distribuée dans le pack de test : `build/original191-pack-receipt.json`.
|
||||
Aucune publication ni modification de l’installation Prism. Le client Vulkan est lancé avec la nouvelle
|
||||
sauvegarde `Sanctuary-Original-Relief-191-Solo`, run `visite191`, profil `original`,
|
||||
vue 32 et simulation 12, spectateur, commandes autorisées, position préparée
|
||||
(94,316,153) face au relief. Connexion confirmée à **21:46:39**, vue 32 et simulation 12 ; Vulkan confirmé.
|
||||
Les cartes et l’état initial des ancres ont été relus dans cette copie neuve.
|
||||
Les sauvegardes 188/189/190 restent conservées.
|
||||
|
||||
|
||||
Le créateur a confirmé l’ouverture ; la connexion est vérifiée dans
|
||||
`build/original191-solo.log`. Cette confirmation porte sur l’entrée dans le solo,
|
||||
pas encore sur l’appréciation esthétique du terrain restauré.
|
||||
@@ -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,143 @@
|
||||
# EXP-199 — affiner les expansions et les raccorder au laboratoire
|
||||
|
||||
Branche `codex/runtime-expansions-beta199`, depuis beta.198. Retour R014.
|
||||
Implémentation et validation locales ; aucune publication du canal packwiz ni mise à jour de Prism.
|
||||
|
||||
## Contrat retenu
|
||||
|
||||
Réutiliser `ExpansionJournal`, `ExpansionRuntime`, les contrôles d'occupation,
|
||||
les contextes de bruit natifs et les biomes d'expansion existants. Les ancres
|
||||
actuelles du laboratoire doivent désormais demander une vraie expansion.
|
||||
Recherche automatique sur 360°, priorité à la proximité du parent parmi les
|
||||
sites admissibles, une région immuable par relais, et un nouveau relais sur
|
||||
chaque région prête. Relief fin : surface large et ondulée, quelques plateaux
|
||||
souples, volumes amincis en conservant les cavités du bruit 3D.
|
||||
|
||||
Les lectures d'admission et la préparation native sont échelonnées entre les
|
||||
ticks. Annonce SGA aux joueurs seulement après les chunks FULL et le relais.
|
||||
Les doubles activations ne créent ni second continent ni double prélèvement.
|
||||
La sauvegarde reprend une région réservée après interruption.
|
||||
|
||||
## Contrat de nouveaux mondes
|
||||
|
||||
Les profils 196–198 gardent leurs paramètres et leurs ancres de démonstration.
|
||||
Les nouveaux profils 199 initialisent un journal d'expansions propre. Aucun
|
||||
ancien monde n'est migré, aucun chunk existant régénéré, aucun ancien format
|
||||
réécrit. Première admission limitée aux régions réellement vierges ; même du
|
||||
vide déjà généré reste protégé. Cette contrainte peut éloigner une expansion
|
||||
si le joueur a déjà exploré les alentours ; ce n'est pas une promesse de
|
||||
contact bord à bord. La réutilisation du vide certifié reste le mécanisme
|
||||
historique distinct, à ne pas activer implicitement dans une partie.
|
||||
|
||||
## Ciel et performance
|
||||
|
||||
Rotation stellaire : sept journées Minecraft, sur l'horloge du monde faisant
|
||||
autorité ; conserver la même transformation pour la visée et les constellations.
|
||||
Les saccades sont également signalées à l'arrêt, y compris shader Sanctuary désactivé (confirmation du créateur). Aucune cause n'est attribuée
|
||||
au worldgen sans mesure ; préserver cette enquête séparément du réglage des
|
||||
expansions. Les débordements de mares restent consignés sans nouvelle refonte.
|
||||
|
||||
## Première tranche jouable
|
||||
|
||||
Le relais demande les mêmes huit offrandes distinctes que le laboratoire. Le
|
||||
joueur reste à proximité, offrande en main, pendant la recherche. Les matériaux
|
||||
ne sont retirés qu'après réservation durable. Une seule préparation est active
|
||||
à la fois ; chaque relais ouvre une région de diamètre nominal 256 blocs, avec
|
||||
un relais de continuation. Le climat suit la couleur du relais d'origine ; la
|
||||
position réelle est choisie automatiquement parmi 64 angles, par distances
|
||||
croissantes sur la grille de chunks. Le relief réutilise la densité 3D historique,
|
||||
comprimée verticalement autour de Y=220 et déformée par son bruit cohérent.
|
||||
|
||||
Les biomes naturels décorent ces nappes. Les villages et forteresses vanilla ne
|
||||
sont pas réactivés par l'ajout de ces biomes à la palette : leurs structures
|
||||
seront une tranche distincte. Les structures et reliefs de l'île centrale sont
|
||||
conservés. Pour Small/42, une recherche locale plus précise peut retrouver les
|
||||
sols d'ancres manqués par la grille historique ; les réservations d'eau suivent
|
||||
alors leurs emprises réelles avec marge au lieu d'exclure un secteur entier.
|
||||
|
||||
Le moteur conserve son journal et sa file native, avec un adaptateur réservé aux
|
||||
profils 199. Admission : 32 lectures asynchrones au maximum par lot, contrôle des
|
||||
chunks chargés et des sauvegardes concurrentes, mémorisation des refus
|
||||
pour éliminer les candidats qui recouvrent un chunk déjà trouvé occupé, puis réservation et publication
|
||||
sur le même tour serveur. Le relais suivant est placé après les chunks FULL ;
|
||||
l'annonce SGA possède une infobulle lisible donnant ses coordonnées.
|
||||
|
||||
Limites : pas de régénération du vide déjà exploré, pas de choix de direction,
|
||||
pas encore de variation du prix/taille pour les relais successifs. La limite
|
||||
historique reste de 64 régions, Sanctuary incluse. Le journal et l'inventaire
|
||||
joueur sont deux fichiers Minecraft distincts : la protection contre le double
|
||||
clic ne constitue pas une transaction atomique entre ces fichiers lors d'une
|
||||
coupure brutale au milieu d'une sauvegarde.
|
||||
|
||||
## Périmètre de validation
|
||||
|
||||
Placement proche sur tous les angles et refus des emprises occupées, densités
|
||||
bornées et nappes fines, horloge stellaire cyclique, activation réelle, deuxième
|
||||
relais, double interaction, interruption/reprise et notification après FULL.
|
||||
`check build`, assemblages et essai client Vulkan avant livraison locale.
|
||||
|
||||
## Relevés de validation
|
||||
|
||||
- Compilation et assemblages : `check build assemblePack assembleTestPack`,
|
||||
avec les suites natives `operator,realtime`, réussis (7 GameTests et contrôles
|
||||
purs). La vérification du placement couvre 9 couples graine/taille : 0, 42 et
|
||||
4736390610738281858 × Small/Medium/Large, les huit secteurs, l'ordre de distance,
|
||||
les conflits et la réouverture du journal.
|
||||
Dernier passage complet : `build/expansion199-check-build-delivery.log`,
|
||||
réussi en 3 min 40 s. Compilation finale des tests client réussie également.
|
||||
- Nouveau Small, graine 42, 2 Gio : serveur prêt vers 14,2 s ; première expansion
|
||||
au centre (368, -368), relais Y=231, après 418 chunks FULL. Le deuxième relais
|
||||
réserve son enfant environ 11 s après l'annonce du premier. L'arrêt volontaire
|
||||
a lieu avec cet enfant encore réservé. Cette durée inclut les essais et n'est
|
||||
pas le temps normal de création d'un monde.
|
||||
- Journaux finaux : `build/expansion199-check-build-final.log` et
|
||||
`build/worldgen-lab/expansion199-native-final/small/42/`. Les premiers essais
|
||||
interrompus restent conservés et ne sont pas des validations.
|
||||
- Réouverture du même Small/42 : enfant repris, 418 chunks FULL, relais
|
||||
(912, 236, -368), une seule annonce pour cet enfant et aucune répétition de
|
||||
celle du parent. Sur 81 colonnes échantillonnées, 1380 blocs non vides, entre
|
||||
Y=191 et Y=275 (végétation comprise) : ce relevé local ne mesure pas le volume
|
||||
total de toutes les expansions.
|
||||
- Client Vulkan, Small/42 : arrivée sur l'île, synchronisation native des étoiles
|
||||
et pause de l'horloge, vraie interaction du joueur avec le relais, double clic
|
||||
sans double prélèvement. Un bloc d'or placé dans le premier candidat reste
|
||||
intact et force l'admission d'un autre emplacement. Les interfaces FR/EN,
|
||||
trois tailles, profils historiques et aller-retour disque passent également.
|
||||
- Deux essais client consécutifs terminent l'expansion puis sauvegardent et
|
||||
ferment normalement (`build/expansion199-client-pass-a.log` et
|
||||
`build/expansion199-client.log`). Le tout premier essai avait réussi les
|
||||
assertions puis rencontré une exception dans `ChunkMap.tick`, lors du suivi
|
||||
des entités. Cause non isolée : un contrôle de réentrance et 100 ticks
|
||||
d'observation supplémentaires n'ont pas reproduit le défaut. Ce résultat
|
||||
n'est pas un correctif prouvé du crash initial. Pas d'essai direct Windows.
|
||||
|
||||
### Saccades : capture locale, cause non isolée
|
||||
|
||||
Première capture client macOS Vulkan, Small/42, vue 8 chunks, shader désactivé, joueur immobile :
|
||||
1241 images relevées, médiane 31,15 ms (limitation de cadence de l'environnement
|
||||
de test), P95 33,12 ms, P99 33,71 ms, maximum 98,01 ms, 7 images au-delà de 50 ms.
|
||||
258 sections ont encore été compilées pendant la capture. Le JFR contient 4152
|
||||
échantillons de workers dans la génération, et des pauses GC jusqu'à 88,45 ms.
|
||||
Le joueur immobile ne signifiait donc pas que le chargement avait terminé.
|
||||
Ce relevé n'isole pas les saccades d'une partie complètement stabilisée, et ne
|
||||
prouve pas leur cause chez le créateur. Le shader n'est pas nécessaire pour
|
||||
reproduire quelques images longues dans cet environnement. Aucun correctif
|
||||
général des saccades n'est revendiqué dans cette livraison.
|
||||
|
||||
Mesures ignorées par Git :
|
||||
`build/expansion199-stationary-first/stationary.json` et `stationary.jfr`.
|
||||
Les captures suivantes restent sous
|
||||
`mods/sanctuary/build/run/clientGameTest/diagnostics/expansion199/`.
|
||||
Pas de nouvelle campagne de screenshots.
|
||||
|
||||
## Livraison locale
|
||||
|
||||
`Sanctuary-Test-beta.199.mrpack`, 12 322 965 octets, copié dans
|
||||
`/Users/koka/Downloads/` avec `Sanctuary-Test-beta.199-Guide.txt`.
|
||||
SHA-256 : `fd2ebe4f43bf0d455377a2f456106165ff5e3519e5c3dce4126818c03f4ae44f`.
|
||||
|
||||
Intégrité ZIP, manifeste Minecraft 26.3/Fabric Loader 0.19.5, dépendances externes,
|
||||
JAR intégrés comparés aux classes compilées et anciennes ressources vérifiés.
|
||||
Les 142 ressources historiques de génération restent identiques à beta.198 ;
|
||||
le preset public vise désormais 199. Reçu : `build/expansion199-artifact.json`.
|
||||
Le canal packwiz et l'installation Prism n'ont pas été modifiés.
|
||||
@@ -0,0 +1,126 @@
|
||||
# WG-SHARE-196 — laboratoire partageable, trois tailles
|
||||
|
||||
Branche `codex/lab-mrpack-sizes-beta196`, depuis beta.195, Minecraft 26.3,
|
||||
Fabric Loader 0.19.5, Java 25. Demande : donner le dernier labo à un ami sous
|
||||
forme de `.mrpack`, avec Small, Medium et Large, puis diagnostiquer la lenteur.
|
||||
|
||||
## Parcours et périmètre
|
||||
|
||||
Dans le pack **Sanctuary Test**, Solo → Créer un monde → Monde → type
|
||||
**Sanctuary** → **Personnaliser** propose Small (512), Medium (724, défaut),
|
||||
Large (1024). Les types vanilla restent disponibles. Le module Test fournit le
|
||||
preset public ; le pack ordinaire sans ce module conserve sa génération.
|
||||
L’interface ne propose plus les anciens laboratoires. Annuler et Échap ne
|
||||
modifient rien ; la taille est conservée lors du rechargement des registres et
|
||||
dans les réglages enregistrés. Libellés FR/EN.
|
||||
|
||||
Les nouveaux réglages sont `sanctuary_test:shared_island196_small`, `medium`,
|
||||
`large`. Les anciens codecs et presets restent présents. Aucun monde existant
|
||||
n’a été migré, modifié ou régénéré. Le preset caché `production_reference196`
|
||||
conserve le témoin de l’ancienne génération pour le lanceur scientifique.
|
||||
|
||||
Le terrain reprend le profil `coherent` : rayon natif selon la capacité, sans
|
||||
étirer les voxels. Les bornes des biomes, l’écartement des récifs, la recherche
|
||||
des bassins et ancres, les secteurs de gemmes et la sortie du donjon suivent
|
||||
la taille. Les pièces conservent leurs dimensions physiques. Atlas de 25/49/81
|
||||
cartes ; rosace à Y=640 et palais vers Y=1280 dans la dimension haute de 1600.
|
||||
Aucune nouvelle direction esthétique ni campagne de screenshots.
|
||||
|
||||
En Small/Large, la réservation des ancres tient compte de la hauteur : un
|
||||
refuge sec peut surplomber une autre installation, sans empiéter sur son
|
||||
volume. C’est nécessaire pour que le donjon ne condamne pas tout un secteur
|
||||
d’une petite île. Les poches d’eau naturelles restent conditionnées à un creux
|
||||
fermé : l’absence d’une poche supplémentaire ne crée pas un bassin artificiel.
|
||||
Les grands bassins souterrains et les étangs de surface sont vérifiés séparément.
|
||||
|
||||
La graine 0 en Large a révélé une poche native de dripstone dans le sol d’un
|
||||
couloir, puis des pointes sèches dans un recoin immergé d’un étang. Les nouveaux
|
||||
profils protègent les sols de circulation via un index par colonne et toutes
|
||||
les cellules d’eau retenues, y compris celles sous un surplomb. Les protections
|
||||
historiques restent utilisées pour les anciens profils.
|
||||
|
||||
## Diagnostic du chargement beta.195
|
||||
|
||||
Le créateur a quitté la visite puis lancé un nouveau monde Large depuis le
|
||||
menu. Les journaux identifient la graine `3466058668915588406` et **l’ancien
|
||||
générateur**, pas le labo `coherent`. Du démarrage du serveur à la sélection
|
||||
du spawn : 18:09:26 → 18:11:05, soit environ 99 secondes.
|
||||
Les chronos internes indiquent notamment l’hydrologie régionale (plusieurs
|
||||
millions de sondes par région), Transit24 (23,4 s), le sanctuaire minéral
|
||||
(33,7 s), Heritage24 (15,5 s) et les anciennes expéditions. Certains chronos
|
||||
s’imbriquent : ne pas les additionner.
|
||||
|
||||
Un JFR de 43 secondes a capturé ensuite la préparation des chunks de cette
|
||||
ancienne génération : 2509 échantillons Java, nombreuses piles TransitPlan21
|
||||
et HeritagePlan19, bruit natif et allocations de streams/maps. Les huit GC
|
||||
mesurés totalisent environ 2,16 s (pas uniquement des pauses du jeu). Le nouveau
|
||||
Voronoï n’apparaît pas dans ce relevé, car ce générateur ne l’utilise pas.
|
||||
|
||||
Un second relevé intermédiaire sur **Small, graine 0**, avant les diagnostics détaillés,
|
||||
contient 1578 échantillons CPU : 1151 (72,9 %) incluent les bruits natifs,
|
||||
118 (7,5 %) les recherches d’eau et 11 (0,7 %) l’érosion Erosion192, qui contient
|
||||
les joints Voronoï locaux. Ces ensembles peuvent se recouvrir ; ce n’est pas
|
||||
une décomposition additive du temps mural. La piste prioritaire serait de
|
||||
réduire les évaluations répétées du relief et les sondages de bassins, en
|
||||
comparant exactement les blocs produits. Cette optimisation n’est pas livrée
|
||||
ici : le raccordement au bon générateur élimine déjà les anciens planificateurs.
|
||||
|
||||
Le lanceur accepte maintenant `--jfr CHEMIN` pour produire des enregistrements
|
||||
séparés à froid et à chaud. Exemple :
|
||||
|
||||
```sh
|
||||
python3 scripts/worldgen_lab.py verify --profile large --seed 0 \
|
||||
--run mesure-nouvelle --memory 2048 --jfr build/profils/large.jfr
|
||||
```
|
||||
|
||||
`serverStartedMs` mesure l’arrivée au spawn serveur. `readyForVisitMs` inclut
|
||||
ensuite les diagnostics qui chargent d’autres chunks, inspectent les structures
|
||||
et calculent l’atlas. Ne pas présenter cette deuxième durée comme l’attente
|
||||
normale du joueur. Ces essais ne mesurent pas la fluidité Vulkan à 32 chunks.
|
||||
Les JFR bruts restent ignorés et ne sont pas intégrés au pack.
|
||||
|
||||
## Vérifications et distribution
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -PsanctuaryFocusedTests=menus`
|
||||
réussi : suite des contrôles purs, quatre GameTests menus, modules et pack.
|
||||
Parcours natif client Vulkan final réussi en FR/EN, sans captures : entrée
|
||||
publique du labo, trois tailles, Annuler/Échap, rechargement des registres,
|
||||
aller-retour des réglages sur disque et personnalisation vanilla.
|
||||
|
||||
Création et réouverture de la graine 0 réussies pour les trois tailles :
|
||||
|
||||
| Taille | Diamètre | Spawn à froid | Spawn après réouverture | Froid avec diagnostics | Cartes |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| Small | 512 | 14.34 s | 0.72 s | 62.0 s | 25 |
|
||||
| Medium | 724 | 19.86 s | 0.64 s | 120.2 s | 49 |
|
||||
| Large | 1024 | 23.96 s | 1.26 s | 144.5 s | 81 |
|
||||
|
||||
Huit ancres et offres distinctes, donjon et butin, étangs sans cellules perdues,
|
||||
portail inférieur, palais, soufre, ruines et secteurs de gemmes contrôlés.
|
||||
Zéro région d’hydrologie lourde dans les six passages. Les chronos ont été pris
|
||||
avec JFR, parfois pendant d’autres contrôles : **pas un benchmark isolé**, ni
|
||||
une garantie de chargement graphique à 32 chunks. Les essais antérieurs Small
|
||||
sans concurrence atteignaient environ 11 s au spawn serveur. Les autres graines
|
||||
et le matériel de l’ami restent à éprouver.
|
||||
|
||||
Les rapports Medium/Large sont dans `shared196f`, Small dans `shared196g`.
|
||||
Entre les deux, seule la résolution des identifiants de taille a été mise en
|
||||
cache pour éviter les allocations dans les protections : mêmes chaînes,
|
||||
capacités, données et algorithmes de génération. Après ce dernier ajustement,
|
||||
contrôle/construction du module Test et réassemblage réussis ; contrôles du
|
||||
mod principal déjà passés réutilisés. Une relance redondante de leur suite est
|
||||
interrompue, sans être comptée comme une seconde réussite complète.
|
||||
|
||||
Export final `Sanctuary-Test-beta.196.mrpack`, 12 268 171 octets, copié dans
|
||||
`/Users/koka/Downloads/`, avec guide séparé. SHA-256 :
|
||||
`f73db69953641237399d4d3f0210ba0252e1ea45377b5ff57d6e231d67aa5988`.
|
||||
Archive ZIP et manifeste vérifiés ; classes identiques aux classes construites,
|
||||
trois capacités dans les ressources, dépendance Fabric API téléchargée et
|
||||
vérifiée par SHA-512/SHA-1. Aucun monde, log, secret ou réglage personnel inclus.
|
||||
Demeure et JEI sont imbriqués dans Sanctuary. Export local uniquement, sans
|
||||
publication du canal packwiz, synchronisation Prism ou envoi à un tiers.
|
||||
|
||||
Traces ignorées : `build/shared196-check-build.log`,
|
||||
`build/shared196-final-assemble.log`, `build/shared196-client-final.log`,
|
||||
`build/shared196-artifact.json`, `build/worldgen196-profile/` et les rapports
|
||||
`build/worldgen-lab/shared196{f,g}/.../{cold,warm}.json`.
|
||||
@@ -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,316 @@
|
||||
# Sanctuary Island — fil rouge de la refonte
|
||||
|
||||
## Retour au laboratoire — 1er octobre 2026
|
||||
|
||||
Le créateur demande une nouvelle visite avec la graine 0 pour décider si le
|
||||
worldgen reste en pause. [WG-OPTIONS-195](worldgen-options-beta195.md) conserve
|
||||
le terrain beta.193 et nettoie la liste de création : Sanctuary et les types
|
||||
vanilla, sans variantes de labo visibles. Le menu sans Amis sera examiné à la
|
||||
sortie du solo. Aucune nouvelle retouche de relief ni série de captures décidée.
|
||||
Le choix d'intégrer le profil de labo à la génération publique reste ouvert.
|
||||
|
||||
## Reprise du 1er octobre 2026 — interfaces et usages
|
||||
|
||||
**Décision du créateur :** arrêter les séries de screenshots et les retouches
|
||||
de génération, biomes et chunks. Le laboratoire beta.193 reste le dernier
|
||||
état de comparaison, avec ses réserves visuelles documentées ; cette pause
|
||||
ne vaut pas validation esthétique complète. Les priorités worldgen ci-dessous
|
||||
sont désormais historiques.
|
||||
|
||||
**Premier lot demandé : [UI-194](main-menu-beta194.md).** Retirer Amis du menu
|
||||
principal, rapprocher Options et Quitter de Solo/Multijoueur et conserver la
|
||||
navigation clavier. Ce retrait figurait déjà dans le cadrage Storyquest initial.
|
||||
|
||||
**Proposition de suite à discuter, sans lancement implicite :**
|
||||
|
||||
| Sujet déjà envisagé | Premier résultat utile |
|
||||
| --- | --- |
|
||||
| Habitant | Une page claire pour l’identité, la progression et le familier, à partir des données réellement disponibles |
|
||||
| Combat et Factions | Clarifier les accès aux épreuves, aux membres et aux projets communs ; distinguer les fonctions présentes des projets futurs |
|
||||
| Storyquest | Choisir une première découverte reliée au SGA et à une activité existante, puis afficher sa piste et sa conséquence personnelle ou collective |
|
||||
| Familiers et cuisine | Revoir l’utilité d’un passif existant selon le port sur la tête et choisir un goût à découvrir ; leur lien avec la préparation d’une sortie reste à concevoir |
|
||||
|
||||
Le prochain travail peut utiliser l’île et ses lieux actuels. Les expansions,
|
||||
la nouvelle hydrologie et de nouvelles structures ne sont pas des dépendances
|
||||
de cette reprise des menus. Aucun passif, nouvelle quête ou écran supplémentaire
|
||||
n’est annoncé comme implémenté par UI-194.
|
||||
|
||||
## Historique du chantier terrain
|
||||
|
||||
**Après la visite beta.190 :** le créateur demande de revenir au relief d’avant
|
||||
les plateaux et de retirer aussi leurs affleurements. [WG-RESTORE-191](restore-relief-beta191.md)
|
||||
restaure la base beta.188 ; cavités, creux et pointes restent la direction,
|
||||
avec des formations singulières ponctuelles plutôt qu’un traitement uniforme.
|
||||
|
||||
**Après la visite beta.189, 30 septembre :** [WG-NATURE-190](natural-refinement-beta190.md)
|
||||
reprend le relief naturel et les cascades. Ruines et salle inférieure appréciées ;
|
||||
le choix de la ressource renouvelable d’activation du portail reste ouvert.
|
||||
Les terrasses ciselées sont réservées à une expansion future dans le backlog.
|
||||
La rosace actuelle est à Y=640 et le palais supérieur à Y=1278 ; les paragraphes
|
||||
antérieurs ci-dessous conservent l’historique de conception.
|
||||
|
||||
**Après la visite beta.181 :** palette naturelle validée ; dégager les huit
|
||||
axes. Le [lot ORIGIN-182](origin-gallery-beta182.md) prépare la galerie
|
||||
souterraine et la variante aérienne en rosace. L’origine reste à Y=320.
|
||||
|
||||
**Actualisation du 30 septembre :** relief et donjon beta.180 appréciés en
|
||||
visite. Le [lot ORIGIN-181](origin-landmark-beta181.md) fixe le bloc originel
|
||||
en **(0,320,0)** dans un petit repère aérien ouvert. Il conserve les demandes
|
||||
de trois secteurs de gemmes, d’une poche de soufre et de huit salles d’ancres
|
||||
naturelles, ainsi que les pistes de galerie inférieure et d’ISS. Ces suites
|
||||
ne sont pas encore implémentées. Les hésitations de position plus bas sont
|
||||
l’historique antérieur à cette décision.
|
||||
|
||||
**É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 est traité dans [UI-194](main-menu-beta194.md), 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é.
|
||||
@@ -0,0 +1,100 @@
|
||||
# FINISH-189 — refuges au sol, berges et repères souterrains
|
||||
|
||||
Ticket du 30 septembre 2026, branche `codex/terrain-finish-beta189`.
|
||||
Retour de visite beta.188 à partir des captures 19:55:33 et 19:57:00.
|
||||
|
||||
## Contrat
|
||||
|
||||
Nouveau preset `sanctuary_test:terrain_finish_v1`, profil `finish`, graine 42.
|
||||
La partie beta.188 reste ouverte et préservée. Aucune migration ou régénération.
|
||||
Corrections demandées : refuges qui suivent le sol, sédiments suivant la profondeur,
|
||||
sable au-delà des berges, rejet des arbres dès leur plantation, reliefs étagés et
|
||||
roche apparente. Accent sur le portail inférieur par le sol et la lumière.
|
||||
|
||||
Essais bornés : déversoirs vers une ouverture proche si le terrain l'autorise ;
|
||||
petites ruines dans un volume de grotte sec déjà présent, sans excavation.
|
||||
Pas de nouveau biome de glace. Une autre variante de lush cave reste à concevoir.
|
||||
|
||||
## Réalisation
|
||||
|
||||
Le relief n'est transformé qu'entre Y=252 et Y=316 par une fonction analytique,
|
||||
sans nouveau bruit ni nouvelle passe d'hydrologie. Les grottes profondes et récifs
|
||||
aériens gardent leur densité. Les affleurements suivent escarpements et épaules.
|
||||
Les refuges de surface choisissent leur sol dans la densité, sans feuilles.
|
||||
|
||||
Les fonds passent du sable peu profond à l'argile des zones calmes, avec gravier
|
||||
sur les ruptures de pente. Berges sableuses sur trois blocs au maximum et jusqu'à
|
||||
deux blocs au-dessus de l'eau. Les plantations natives de type arbre et système
|
||||
racinaire sont rejetées avant toute écriture près des étangs.
|
||||
|
||||
Le portail horizontal 5×5 avec vide 3×3 reste en place. Sol taillé et éclairage
|
||||
en cuivre/pierres lumineuses soulignent le centre et les accès ; chandelier
|
||||
suspendu au toit existant. Les petites ruines sont un prototype, pas une ville
|
||||
avec habitants, commerce ou nouvelles quêtes.
|
||||
|
||||
## Vérifications
|
||||
|
||||
Les résultats finaux et leurs limites sont détaillés ci-dessous.
|
||||
|
||||
## Résultat vérifié — graine 42
|
||||
|
||||
- Huit ancres aux mêmes emplacements et orientations qu'en beta.188, offres
|
||||
vérifiées et remises à leur état initial après le contrôle. Les surfaces des
|
||||
refuges suivent la densité rocheuse, sans consulter la hauteur des feuilles.
|
||||
- 1 120 blocs de sable sur les berges sèches ; fonds de 1 146 blocs de sable,
|
||||
376 de gravier et 2 896 d'argile ; 408 plantations aquatiques. Zéro tronc dans
|
||||
les colonnes des étangs et des plages contrôlées, de 28 blocs sous l'eau à
|
||||
28 blocs au-dessus. Les arbres voisins peuvent conserver un surplomb de feuilles.
|
||||
- Une cascade : départ (143,244,-147), déversoir (153,244,-147). Chenal de dix
|
||||
blocs, entaille locale de treize blocs au total. Soixante-quatre blocs de chute
|
||||
sont préremplis ; les fluides natifs poursuivent la chute vers le vide.
|
||||
- Quatre ruines de cinq blocs de côté, aux sols (24,163,-64), (-8,147,-80),
|
||||
(64,163,-80), (56,120,-80). Appuis rocheux contrôlés, zéro bloc de cavité
|
||||
excavé. C'est un petit ensemble de vestiges étagés, pas une ville complète
|
||||
ni un nouveau réseau de couloirs.
|
||||
- Portail conservé en (0,175,0), bordure 5×5 et ouverture sèche 3×3. Sol de
|
||||
pierre taillée, accès lumineux et chandelier : vingt sources lumineuses
|
||||
supplémentaires dans ce plan, sans creuser de nouvelle salle.
|
||||
- Sur le relevé du relief : 616 hauteurs de surface modifiées, quatre blocs
|
||||
d'écart maximum ; 337 points d'affleurement. Les 25 205 échantillons de densité
|
||||
des profondeurs et des récifs restent identiques. La compression conserve les
|
||||
fines crêtes au lieu de les supprimer entre deux échantillons.
|
||||
- Palais, 49 cartes verrouillées dans leurs 49 cadres horizontaux, gemmes,
|
||||
soufre, butin en fer enchanté et passages du donjon contrôlés. Cartes et état
|
||||
initial des ancres relus dans la copie destinée au solo.
|
||||
|
||||
Génération puis réouverture réussies : `build/finish189g-cold.json` et
|
||||
`build/finish189g-warm.json`. Démarrage du serveur : 11,369 s / 0,581 s ; processus
|
||||
prêt après l'ensemble des contrôles : 67,717 s / 19,973 s. Zéro région d'hydrologie.
|
||||
Plans locaux des berges/déversoirs : 137 ms ; ruines : 459 ms à froid. Ce sont des
|
||||
mesures de laboratoire, pas une garantie de durée identique sur chaque machine.
|
||||
|
||||
Le serveur de diagnostic désactive son watchdog : la vérification charge les
|
||||
zones et remplit toutes les cartes dans un seul appel. Un premier contrôle a
|
||||
atteint les 60 s pendant l'atlas. Le solo garde le remplissage progressif habituel
|
||||
et ne lance pas cette batterie de mesures au démarrage.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi en 9 min 1 s,
|
||||
avec 265 GameTests réussis. Après les derniers correctifs du module de labo,
|
||||
compilation/JAR ciblés, génération et réouverture natives réussies ; pack
|
||||
optionnel resynchronisé. Ses 165 classes correspondent aux classes compilées
|
||||
et le JAR distribué est identique : `build/finish189-pack-receipt.json`.
|
||||
Aucune publication de canal, installation Prism ou ancienne sauvegarde modifiée.
|
||||
|
||||
## Visite
|
||||
|
||||
Nouvelle sauvegarde `Sanctuary-Terrain-Finish-189-Solo`, profil `finish`, run
|
||||
`visite189`, graine 42. Vue 32, simulation 12, spectateur, commandes activées.
|
||||
Solo Vulkan ouvert : connexion confirmée à 20:29:55, distances 32/12 et
|
||||
masquage du palais depuis le sol confirmés dans `build/finish189-solo.log`.
|
||||
|
||||
- Étang ouest : `/tp -104.5 269 119.5 180 35`
|
||||
- Ancre nord : `/tp 36.5 252 -168.5 180 0`
|
||||
- Cascade : `/tp 158.5 248 -146.5 90 25`
|
||||
- Portail inférieur : `/tp 0.5 179 10.5 180 20`
|
||||
- Ruines : `/tp 24.5 167 -71.5 0 20`
|
||||
|
||||
Le jugement esthétique reste à faire pendant la nouvelle visite. La glace et
|
||||
un biome supplémentaire ne sont pas ajoutés. Piste proposée pour plus tard :
|
||||
une cavité claire de calcite et d'améthyste, plus calme que les jardins lush,
|
||||
les marais et le soufre ; aucune décision d'implémentation à ce stade.
|
||||
@@ -1,5 +1,9 @@
|
||||
# UI-045 — menu principal
|
||||
|
||||
Contrat historique : [UI-194](main-menu-beta194.md) retire ensuite la ligne
|
||||
Friends / Amis et rapproche les commandes restantes. Les essais ci-dessous
|
||||
décrivent beta.045, pas le menu courant.
|
||||
|
||||
Branche `codex/title-menu-beta045`, à partir de beta.044.
|
||||
|
||||
## Contrat
|
||||
|
||||
+63
-2
@@ -1,5 +1,42 @@
|
||||
# Sanctuary — vision du projet
|
||||
|
||||
**Cap du 1er octobre 2026 :** le créateur suspend les retouches du worldgen,
|
||||
des biomes et des chunks ainsi que les tests de screenshots après beta.193.
|
||||
La reprise porte sur les interfaces et les idées du Storyquest, avec le retrait
|
||||
d’Amis du menu principal en premier. Voir le [fil rouge courant](storyquest-fil-rouge.md)
|
||||
et [UI-194](main-menu-beta194.md). Les priorités de septembre ci-dessous
|
||||
conservent l’historique ; elles ne relancent pas le chantier terrain.
|
||||
|
||||
**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.
|
||||
Le 30 septembre, le [premier repère de l’origine](origin-landmark-beta181.md)
|
||||
fixe le bloc en (0,320,0), sur une petite construction ouverte. Le plan des huit
|
||||
salles d’ancres naturelles et leur esthétique 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 +436,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 +462,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 +580,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 +603,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,167 @@
|
||||
# WG-CONTINUITY-193 — raccordements et visites sur plusieurs graines
|
||||
|
||||
Branche `codex/worldgen-continuity-beta193`, Minecraft 26.3.
|
||||
Nouveau profil de laboratoire `coherent`, mondes neufs uniquement.
|
||||
Graines vérifiées : 42 (référence des retours), 2026 et 8675309.
|
||||
|
||||
## Demande
|
||||
|
||||
Conserver la direction des montagnes appréciée en beta.192 et corriger les
|
||||
raccordements d’eau, le damier de mousse des ancres et les longues marches.
|
||||
Vérifier plusieurs graines et examiner des captures réelles du jeu avant de
|
||||
préparer la prochaine visite. La quantité globale de roche n’est pas à réduire
|
||||
systématiquement ; la réserve précédente du créateur demeure.
|
||||
|
||||
## Contrat de cette itération
|
||||
|
||||
- Propriété commune des espaces remplis d’eau : éviter les nappes indépendantes
|
||||
qui se recoupent à des hauteurs différentes dans une même cavité.
|
||||
- Ancres de surface et aériennes adaptées à la densité du terrain, sans fond
|
||||
ondulé artificiel ni damier de mousse ; conserver secteurs et orientation.
|
||||
- Éviter les sols de grotte en plein air et interrompre localement les longues
|
||||
marches du massif sans remettre des niveaux périodiques sur toute l’île.
|
||||
- Ajouter des contrôles de raccordement, pas seulement de nombre de blocs.
|
||||
- Produire des captures Vulkan de bassins, d’ancres et du relief dans des mondes
|
||||
de diagnostic neufs, avec graine, position et version identifiées.
|
||||
|
||||
Aucune modification, régénération ou migration des sauvegardes précédentes.
|
||||
Le profil beta.192 reste disponible avec son comportement antérieur.
|
||||
|
||||
## Changements de génération
|
||||
|
||||
Les volumes des étangs de surface évitent désormais les volumes déjà retenus
|
||||
dans les cavités. L’eau est posée avant la décoration et ses colonnes sont
|
||||
protégées contre les arbres et systèmes de racines. Les contrôles comptent
|
||||
chaque cellule humide prévue, y compris celles que l’ancien contrôle ignorait
|
||||
quand un autre bloc avait remplacé l’eau. Les berges sableuses débordent moins
|
||||
haut et moins loin sur les flancs raides.
|
||||
|
||||
Les ancres aériennes utilisent le même relevé de sol que les ancres de surface.
|
||||
Le chemin suit les colonnes réelles ; son admission refuse les sauts de hauteur.
|
||||
L’herbe reprend la couleur du biome et la terre remplace le damier de mousse.
|
||||
Les grottes gardent leurs palettes ; au-dessus de Y=240, une couverture rocheuse
|
||||
locale doit réellement exister pour attribuer une palette de grotte.
|
||||
|
||||
Le massif 192 reste la base. De petites entailles irrégulières interrompent ses
|
||||
contours, sans paliers périodiques ni modification des densités profondes et
|
||||
aériennes. Les cerisiers cherchent un emplacement sec et dégagé près des
|
||||
sommets choisis ; leur altitude suit le relief de la graine.
|
||||
|
||||
Les essais ont aussi révélé des collisions anciennes : la galerie choisit ses
|
||||
salles secondaires dans des cavités voisines séparées de la pièce centrale.
|
||||
Elle conserve seulement les salles admises, avec leurs liaisons praticables.
|
||||
Les bassins évitent cette galerie et peuvent être moins nombreux si l’espace
|
||||
manque. Les ruines évitent son volume et les geysers évitent les ruines.
|
||||
Ces recherches restent bornées et leurs plans sont mis en cache ; aucun solveur
|
||||
d’hydrologie régionale ni simulation permanente n’est ajouté.
|
||||
|
||||
## Méthode de comparaison
|
||||
|
||||
Le script `scripts/worldgen_lab.py verify --profile coherent --seed 42 --run NOM`
|
||||
crée un laboratoire neuf, vérifie les blocs natifs, le ferme puis le rouvre.
|
||||
Répéter avec 2026 et 8675309 ; les journaux et JSON restent dans leurs dossiers.
|
||||
Une modification du code ou des données impose un nouveau nom d’expérience.
|
||||
|
||||
`scripts/worldgen_photos.py` prépare ensuite une copie neuve du monde vérifié,
|
||||
un plan de caméras et un fichier de provenance. Il requiert `nbtlib`, `--source-run`,
|
||||
`--seed`, `--run` et `--previous-client` (dossier client d’un ancien labo fermé).
|
||||
La commande `worldgen_lab.py solo --profile coherent --seed GRAINE --run PHOTOS
|
||||
--photo-plan CHEMIN/cameras.json` prend les images avec le moteur Vulkan du jeu.
|
||||
Le mode photo refuse les sauvegardes qui ne portent pas le préfixe réservé
|
||||
`Sanctuary-Photo-193-`. Il masque l’interface, attend le rendu des sections,
|
||||
conserve une heure de midi fixe, capture puis ferme proprement le client.
|
||||
Sans cette option, le solo reste une visite manuelle normale.
|
||||
|
||||
Les positions viennent des aménagements réellement générés : chaque bassin,
|
||||
deux côtés du massif, les ancres aériennes et une ancre de surface. Une capture
|
||||
réussie doit être examinée visuellement ; un nombre de blocs correct ne valide
|
||||
ni la beauté des rives ni la silhouette du relief.
|
||||
|
||||
## Résultats
|
||||
|
||||
Génération et réouverture natives réussies pour les trois graines dans les
|
||||
runs `coherent193k42`, `coherent193k2026` et `coherent193k8675309`.
|
||||
Les plans d’eau, emplacements des ancres et relevés de relief sont identiques
|
||||
après rechargement (`build/coherent193-three-seeds.json`).
|
||||
|
||||
| Graine | Volumes d’eau contrôlés | Cellules humides | Salles de galerie | Coffres de ruines |
|
||||
| --- | ---: | ---: | ---: | ---: |
|
||||
| 42 | 5 | 28212 | 3 | 4 |
|
||||
| 2026 | 5 | 21542 | 3 | 3 |
|
||||
| 8675309 | 4 | 8929 | 2 | 4 |
|
||||
|
||||
Sur chaque graine : zéro contact entre volumes indépendants, toutes les cellules
|
||||
prévues encore humides, huit ancres contrôlées en blocs réels, aucun damier de
|
||||
mousse dans leurs plans exposés, trois cerisiers réellement présents et 39 366
|
||||
échantillons profonds/aériens inchangés. Le donjon, les gemmes, la galerie, le
|
||||
soufre, les ruines, la rosace et les 49 cartes/cadres de l’ISS passent aussi les
|
||||
contrôles natifs. La graine 42 admet un déversoir large de cinq colonnes ; les
|
||||
deux autres gardent leurs bassins fermés quand aucun déversoir proche n’est admis.
|
||||
|
||||
Démarrages à froid : 15,2 / 18,9 / 19,1 s ; réouvertures : 0,6 / 0,7 / 1,0 s.
|
||||
Zéro région d’hydrologie lourde. Avec l’ensemble des diagnostics et de l’atlas,
|
||||
les essais à froid durent 112 / 188 / 106 s. Les processus tournaient en partie
|
||||
en parallèle ; ce ne sont pas des benchmarks isolés et le solo ne rejoue pas
|
||||
ces contrôles exhaustifs à chaque ouverture.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack` réussi, 265 GameTests.
|
||||
Le premier passage avait échoué sur le déplacement d’un familier volant, sans
|
||||
modification de ce système ; la relance a passé les 265 tests. Après les derniers
|
||||
ajustements du laboratoire, `:sanctuary-test:check :sanctuary-test:build` et le
|
||||
réassemblage du pack de test ont réussi. 179 classes et 124 ressources du JAR
|
||||
sont identiques aux sorties courantes et le JAR distribué correspond octet pour
|
||||
octet (`build/coherent193-pack-receipt.json`). Pas de publication ni de mise à
|
||||
jour de Prism.
|
||||
|
||||
## Examen des captures
|
||||
|
||||
La série retenue rassemble 29 PNG natifs 2560 × 1440 : 10 pour 42 dans
|
||||
`photos193d`, 10 pour 2026 dans `photos193c` et 9 pour 8675309 dans `photos193e`.
|
||||
Les dossiers sont sous `build/worldgen-lab/RUN/coherent/GRAINE/` ; les positions,
|
||||
l’empreinte des sources et les empreintes des PNG sont conservées dans
|
||||
`photo-provenance.json`. Galerie locale : `build/worldgen193-gallery/index.html`.
|
||||
|
||||
Les premières images nocturnes de 42 et pluvieuses de 8675309 sont conservées
|
||||
comme diagnostics, hors de la galerie finale. Les services de temps réel et de météo quotidienne sont désactivés dans les
|
||||
seuls clients photo jetables ; le script prépare également une longue période
|
||||
de temps clair natif. La météo quotidienne avait rétabli la pluie après un
|
||||
premier essai avec seulement les durées natives modifiées. L’automate ne remplit l’identité
|
||||
« Labo » et ne passe l’avertissement expérimental que dans ces copies réservées.
|
||||
|
||||
Constats de l’assistant, sans validation esthétique du créateur :
|
||||
|
||||
- Les nappes retenues sont séparées et l’eau reste présente après décoration.
|
||||
Cela ne suffit pas à rendre les rives naturelles : 42 conserve notamment une
|
||||
longue retenue sableuse contre le massif ; les liserés restent trop réguliers.
|
||||
- Les abords des ancres aériennes suivent le sol, sans l’ancien tapis de mousse
|
||||
surélevé. Le motif alterné terre/herbe et pierre/herbe reste très quadrillé,
|
||||
particulièrement sur l’ancre automnale de 42 et celle de surface de 2026.
|
||||
- Certains fonds de bassins de cavité restent herbeux. Leur sédimentation n’est
|
||||
pas harmonisée avec celle des étangs de surface dans cette itération.
|
||||
- Les trois massifs diffèrent, mais de grandes bandes et marches rocheuses
|
||||
persistent, surtout sur 42 et 2026. Les petites entailles n’ont pas suffi à
|
||||
résoudre cette réserve esthétique. Aucune disparition générale revendiquée.
|
||||
- Une vue de l’ancre de surface de 42 est masquée par le feuillage ; les autres
|
||||
points de vue et contrôles de blocs ne remplacent pas sa visite rapprochée.
|
||||
De larges ombres sont aussi visibles sur 42 : leur cause n’est pas établie.
|
||||
|
||||
Pour la prochaine retouche, comparer les mêmes caméras, une modification à la
|
||||
fois, en commençant par la retenue sur le massif et les chemins quadrillés.
|
||||
Les trois graines constituent une régression reproductible, pas une preuve
|
||||
pour toutes les graines ou une validation esthétique de toute la carte.
|
||||
|
||||
## Visite manuelle livrée
|
||||
|
||||
`build/worldgen-lab/visite193/coherent/42/client/saves/` contient trois copies
|
||||
neuves : `Sanctuary-Coherent-193-42-Solo`, `Sanctuary-Coherent-193-2026-Solo`
|
||||
et `Sanctuary-Coherent-193-8675309-Solo`. Elles proviennent des mondes photo
|
||||
fermés, dont les huit ancres sont inactives et le Bugrock allumé à zéro relais.
|
||||
|
||||
Le client manuel est entré sur 42 le 1er octobre 2026 à 01:26:55 Europe/Paris,
|
||||
vue 32 chunks, simulation 12, joueur spectateur placé devant le massif.
|
||||
L’automate photo n’est pas activé. Les deux autres mondes sont disponibles
|
||||
par la liste Solo ; ils n’ont pas été revisités manuellement après leur copie.
|
||||
Les services d’heure et météo réelle restent désactivés dans ce labo de
|
||||
comparaison ; midi et temps clair sont préparés dans ces copies seulement.
|
||||
Quelques avertissements de retard de ticks apparaissent pendant le chargement
|
||||
à 32 chunks : aucune mesure de fluidité stable n’est revendiquée.
|
||||
@@ -0,0 +1,89 @@
|
||||
# WG-FIX-197 — arrivée, cascades et détails du donjon
|
||||
|
||||
Branche `codex/worldgen-fixes-beta197`, depuis beta.196. Retours R012 :
|
||||
arrivée sur l’ISS, cascades souterraines, diagonales de terre dans la mine,
|
||||
toit de cabane cassé, arrêt chez un ami pendant la création Large sans rapport.
|
||||
Graine de cet arrêt inconnue. Minecraft 26.3, Fabric 0.19.5, Java 25.
|
||||
|
||||
## Changements
|
||||
|
||||
Les nouveaux profils `shared_island197_{small,medium,large}` conservent les
|
||||
densités et biomes du terrain beta.196. Le paramètre additif `polish197`, faux
|
||||
par défaut, active uniquement les corrections de décoration. Les identifiants
|
||||
beta.196, données et comportements historiques restent présents. Personnaliser
|
||||
un profil historique conserve sa révision. Aucune migration ni retouche de
|
||||
sauvegarde personnelle.
|
||||
|
||||
La hauteur du point global ne suffisait pas : `PlayerSpawnFinder` recommence
|
||||
une recherche depuis la surface la plus haute de chaque colonne. Le toit de
|
||||
l’ISS pouvait donc être retenu. Sur les nouveaux profils, cette recherche
|
||||
reste bornée au terrain principal (Y=198–316), avec sol solide, deux blocs
|
||||
d’air et exclusion des feuilles et troncs. Le rayon aléatoire natif et les
|
||||
réapparitions personnelles sur un lit restent gérés par Minecraft.
|
||||
|
||||
Une poche d’air sous une berge ne suffit plus pour accepter une cascade.
|
||||
Chaque sortie doit être ouverte au ciel et posséder une face extérieure libre
|
||||
sur les huit premiers blocs de chute. Les recherches conservent leurs bornes :
|
||||
dix blocs jusqu’à la sortie, au plus deux bouches de quatre ou cinq blocs.
|
||||
Une cascade n’est pas forcée si le terrain ne s’y prête pas. Les blocs de chute
|
||||
ne remplacent jamais un bloc solide réel ; une corniche peut arrêter l’eau.
|
||||
|
||||
Le sol des salles minières remplace le modulo 7 par des plaques de terre
|
||||
cohérentes, dépendantes de la graine. La cabane utilise deux pans d’escaliers
|
||||
continus, un faîtage, des pignons fermés et des pilotis rejoignant un support.
|
||||
Son volume est vérifié avant placement et réservé contre les décorations
|
||||
natives voisines. Aucun grand terrassement ajouté pour imposer une cabane.
|
||||
|
||||
## Erreur de création reproduite
|
||||
|
||||
La graine **17 en Medium** a ensuite reproduit une exception réelle pendant
|
||||
la génération du premier chunk : `No supported central gallery floor`, dans
|
||||
`Cavern183.create`. Aucune cavité admissible n’existait exactement sous (0,0).
|
||||
Sur beta.197, une recherche locale de roche porteuse sert désormais de repli
|
||||
pour sculpter la même chambre centrale ; elle ne déplace pas le portail ni les
|
||||
salles déjà admises sur les autres graines. Création et réouverture de cette
|
||||
graine réussies en Medium et Large. Ce défaut identifié ne prouve pas encore
|
||||
la cause de l’arrêt sans rapport chez l’ami. `latest.log` reste utile ; une
|
||||
ligne indique maintenant taille, graine et plafond mémoire au démarrage.
|
||||
|
||||
## Vérifications
|
||||
|
||||
- Témoin beta.196 Large, graine 42 : création et réouverture réussies.
|
||||
- beta.197 : Small 0, Large 42, Medium 17 et Large 17 créés puis rouverts avec
|
||||
huit ancres, donjon, gemmes, atlas, portail et contrôles d’eau. Les quatre
|
||||
cabanes ont leurs 99 blocs de toiture intacts, avant et après réouverture.
|
||||
- Dans ces quatre essais, aucune bouche de cascade extérieure admissible :
|
||||
les anciennes chutes enfouies sont rejetées. Le contrôle reste conditionnel,
|
||||
il ne constitue pas une validation visuelle d’une nouvelle cascade acceptée.
|
||||
- Lecture des blocs sauvegardés des salles Large 42 : ancien motif, 75 blocs
|
||||
de terre tous isolés ; nouveau sol, 160 blocs dont un seul isolé. La terre
|
||||
forme donc des plaques au lieu de diagonales pointillées.
|
||||
- Parcours client natif Vulkan réussi, vue 8 chunks, mémoire Java 2 Gio :
|
||||
menus FR/EN, trois tailles, Annuler/Échap, aller-retour disque, ancien profil
|
||||
conservé, types vanilla. Création Large 0 puis arrivée réelle à Y=252 et
|
||||
huit recherches de spawn aléatoires restées sur l’île. Pas de campagne de
|
||||
captures, ni de revendication de fluidité à 32 chunks sur le matériel ami.
|
||||
|
||||
Les essais Small 0/Large 42 et client ont précédé le dernier repli de galerie,
|
||||
qui ne s’exécute que lorsqu’aucun des anciens sols n’a été trouvé. Les scénarios
|
||||
17 vérifient ce changement final. Une première tentative du test client a été
|
||||
arrêtée pendant l’attente inutile de tous les chunks de la vue ; une fixture
|
||||
de menu historique a ensuite été corrigée pour conserver la clé publique.
|
||||
Seul le passage final réussi est retenu.
|
||||
|
||||
Traces ignorées : `build/faults197-client-verified.log`,
|
||||
`build/worldgen-lab/faults197a/{small/0,large/42}/{cold,warm}.json`,
|
||||
`build/worldgen-lab/faults197b/{medium,large}/17/{cold,warm}.json`.
|
||||
La panne initiale est conservée dans `faults197a/medium/17/cold.log`.
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack -PsanctuaryFocusedTests=menus`
|
||||
réussi en 5 min 18 s : contrôles purs, quatre GameTests menus et assemblages.
|
||||
Export ZIP, versions, dépendances, six presets anciens/nouveaux et toutes les
|
||||
classes des deux JAR comparés aux classes construites. La dépendance Fabric API
|
||||
conserve exactement le téléchargement et les hashes vérifiés en beta.196.
|
||||
|
||||
Export local `Downloads/Sanctuary-Test-beta.197.mrpack`, **12 276 176 octets**.
|
||||
SHA-256 : `90b0e0495c7fbdee5ceb1b3c87f177274d89d505a6eea7189358aab3c7fff5b2`.
|
||||
Guide séparé ; aucun monde, réglage personnel, journal ou secret dans l’archive.
|
||||
Ni publication du canal ni installation dans Prism. Reçu :
|
||||
`build/faults197-artifact.json` ; journal général : `build/faults197-check-build.log`.
|
||||
@@ -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,79 @@
|
||||
# WG-OPTIONS-195 — Sanctuary et les types vanilla
|
||||
|
||||
Branche `codex/worldgen-options-beta195`, Minecraft 26.3, depuis beta.194.
|
||||
|
||||
## Demande et périmètre
|
||||
|
||||
Le créateur reprend la visite de Sanctuary Island pour décider si la génération
|
||||
peut rester en pause. Il demande une nouvelle île avec la graine 0 et souhaite
|
||||
examiner le menu principal en quittant le solo. Il précise que la création de
|
||||
monde doit conserver Sanctuary et les types vanilla, sans les variantes de labo.
|
||||
|
||||
Le module Sanctuary Test cesse d'ajouter ses onze anciens presets au tag de
|
||||
sélection normal. Les types vanilla et `sanctuary:sanctuary` restent disponibles.
|
||||
Les presets et leurs codecs sont conservés pour les sauvegardes et les lanceurs
|
||||
de développement ; aucune suppression, migration ou régénération de monde.
|
||||
Les sélections historiques des GameTests restent dans leurs seules ressources
|
||||
de test, avec références optionnelles au module Sanctuary Test.
|
||||
|
||||
Ce nettoyage ne remplace pas la génération publique Sanctuary par le profil
|
||||
de laboratoire : leur consolidation reste à décider après la visite. Les options
|
||||
de taille de Sanctuary sont conservées. Les traductions et identifiants existants
|
||||
restent identiques ; aucun nouveau libellé d'interface.
|
||||
|
||||
## Correction révélée par la graine 0
|
||||
|
||||
Le premier démarrage a échoué : `No supported central gallery floor`. La
|
||||
recherche historique ne sondait que Y=148–212 au centre. En cas d’échec dans le
|
||||
profil `coherent` seulement, une seconde recherche bornée examine Y=48–240,
|
||||
avec les mêmes exigences de sol rocheux et de couverture. Les sols déjà admis
|
||||
sont conservés. Ce correctif concerne le placement de la galerie, pas la forme
|
||||
de l’île. Le monde avorté est conservé ; le nouvel essai utilise un autre dossier.
|
||||
|
||||
## Visite préparée
|
||||
|
||||
Nouveau dossier ignoré `build/worldgen-lab/visite195d/coherent/0/`, profil
|
||||
`sanctuary_test:coherent_landforms_v1`, graine 0, diamètre 724. Le terrain et
|
||||
les aménagements sont ceux de beta.193, avec le repli de placement décrit
|
||||
ci-dessus ; beta.194 fournit le menu sans Amis.
|
||||
Solo spectateur, commandes autorisées, distance de rendu 32, simulation 12,
|
||||
Vulkan, journée claire pour comparer le terrain. Aucune série de screenshots.
|
||||
|
||||
## Vérifications
|
||||
|
||||
`./gradlew check build assemblePack assembleTestPack
|
||||
:sanctuary-test:exportDuoLaunch -PsanctuaryFocusedTests=menus` réussi en 3 min 45 s,
|
||||
avec les quatre GameTests menus. Après le correctif de galerie, reconstruction
|
||||
et contrôle du module de laboratoire réussis. Une passe générale supplémentaire
|
||||
qui avait aussi lancé les tests de capacité est interrompue pour libérer la
|
||||
machine lors de la visite : elle ne constitue pas un résultat de 265 tests.
|
||||
Le réassemblage final réussit en 4 s en reprenant les vérifications déjà
|
||||
réussies, sans les relancer.
|
||||
|
||||
Création native de la graine 0 réussie dans `visite195d` : démarrage serveur
|
||||
14,8 s, contrôle des lieux terminé à 108,1 s, zéro région d’hydrologie lourde.
|
||||
Trois salles reliées, portail horizontal en (0,131,0), huit ancres avec huit
|
||||
matériaux distincts, cinq volumes d’eau séparés et 20 033 cellules humides
|
||||
contrôlées. Les 49 cartes/cadres du palais et les autres contrôles de donjon,
|
||||
soufre, ruines et gemmes passent. Ces durées incluent des diagnostics et une
|
||||
construction concurrente ; elles ne sont pas un benchmark de fluidité.
|
||||
|
||||
Une copie neuve `Sanctuary-Labo-195-Seed-0` est préparée pour la visite, avec le
|
||||
visiteur de développement KokaLab déjà inscrit et les ancres dans leur état
|
||||
initial (le contrôle des offres restaure ses modifications). La graine 0 est
|
||||
relue dans les données enregistrées. Le client beta.195 a démarré sous Vulkan. Après l’avertissement expérimental,
|
||||
les journaux confirment KokaLab connecté le 1er octobre à 18:00:35 Europe/Paris,
|
||||
en (100.5,335,150.5), rendu 32 chunks et simulation 12. Le contrôle natif de
|
||||
visibilité de la couronne passe. L’examen esthétique et celui du menu principal
|
||||
sont laissés au créateur.
|
||||
|
||||
Journaux : `build/worldgen195-check-build.log`, `build/worldgen195-floor-build.log`,
|
||||
`build/worldgen195-final-pack.log` (passe interrompue),
|
||||
`build/worldgen195-assemble.log`, `build/visit195-solo.log` et le rapport natif
|
||||
`build/worldgen-lab/visite195d/coherent/0/server/worldgen-lab-metrics.json`.
|
||||
Le reçu `build/worldgen195-artifact.json` confirme que les classes du mod principal
|
||||
sont identiques à beta.194. Dans le mod de laboratoire, seul `Cavern183.class`
|
||||
change ; les 22 presets et toutes les ressources du terrain restent identiques,
|
||||
hormis le tag de sélection. Les anciennes sauvegardes n’ont pas été ouvertes.
|
||||
Aucune capture ni validation esthétique de cette nouvelle graine revendiquée.
|
||||
Pas de publication du canal packwiz ni de mise à jour de Prism.
|
||||
@@ -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.161
|
||||
pack_version=beta.161
|
||||
mod_version=beta.203
|
||||
pack_version=beta.203
|
||||
resource_pack_version=beta.121
|
||||
maven_group=fr.koka.sanctuary
|
||||
jei_version=30.32.0-sanctuary.3
|
||||
|
||||
@@ -51,4 +51,10 @@ tasks.register('exportDuoLaunch') {
|
||||
}
|
||||
}
|
||||
tasks.named('test') { failOnNoDiscoveredTests=false }
|
||||
tasks.register('gallery198Smoke', JavaExec) {
|
||||
dependsOn('testClasses')
|
||||
classpath=sourceSets.test.runtimeClasspath
|
||||
mainClass='fr.koka.sanctuarytest.GalleryFloor198Test'
|
||||
}
|
||||
tasks.named('check') { dependsOn('gallery198Smoke') }
|
||||
tasks.named('jar') { from(rootProject.file('LICENSE')) { rename { 'LICENSE_sanctuary_test' } } }
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
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 AdventureBiomes180 extends BiomeSource {
|
||||
public static final int AUTUMN_MIN=200, AUTUMN_MAX=232;
|
||||
public static final MapCodec<AdventureBiomes180> CODEC=RecordCodecBuilder.mapCodec(i->i.group(
|
||||
Biome.CODEC.listOf().fieldOf("biomes").forGetter(s->s.biomes),
|
||||
com.mojang.serialization.Codec.BOOL.optionalFieldOf("discovery",false).forGetter(s->s.discovery),
|
||||
com.mojang.serialization.Codec.BOOL.optionalFieldOf("natural_sulfur",false).forGetter(s->s.naturalSulfur),
|
||||
com.mojang.serialization.Codec.BOOL.optionalFieldOf("original_relief",false).forGetter(s->s.originalRelief),
|
||||
com.mojang.serialization.Codec.BOOL.optionalFieldOf("polish197",false).forGetter(s->s.polish197),
|
||||
com.mojang.serialization.Codec.BOOL.optionalFieldOf("stable_gallery198",false).forGetter(s->s.stableGallery198)
|
||||
).apply(i,AdventureBiomes180::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 boolean discovery,naturalSulfur,originalRelief,polish197,stableGallery198;
|
||||
private final Map<String,Holder<Biome>> named;
|
||||
private volatile State state;
|
||||
volatile LabExpansions199 expansions;
|
||||
private static final int[][] DIRECTIONS={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
|
||||
public AdventureBiomes180(List<Holder<Biome>> biomes,boolean discovery){this(biomes,discovery,false);}
|
||||
public AdventureBiomes180(List<Holder<Biome>> biomes,boolean discovery,boolean naturalSulfur){this(biomes,discovery,naturalSulfur,false);}
|
||||
public AdventureBiomes180(List<Holder<Biome>> biomes,boolean discovery,boolean naturalSulfur,boolean originalRelief){
|
||||
this(biomes,discovery,naturalSulfur,originalRelief,false);
|
||||
}
|
||||
public AdventureBiomes180(List<Holder<Biome>> biomes,boolean discovery,boolean naturalSulfur,boolean originalRelief,boolean polish197){
|
||||
this(biomes,discovery,naturalSulfur,originalRelief,polish197,false);
|
||||
}
|
||||
public AdventureBiomes180(List<Holder<Biome>> biomes,boolean discovery,boolean naturalSulfur,boolean originalRelief,boolean polish197,boolean stableGallery198){
|
||||
this.stableGallery198=stableGallery198;
|
||||
this.polish197=polish197;
|
||||
this.originalRelief=originalRelief;
|
||||
this.naturalSulfur=naturalSulfur;
|
||||
if(naturalSulfur&&biomes.stream().noneMatch(b->b.unwrapKey().orElseThrow().identifier().getPath().equals("natural_sulfur_187")))throw new IllegalArgumentException("Missing natural sulfur biome");
|
||||
this.discovery=discovery;
|
||||
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);
|
||||
if(discovery&&!named.containsKey("discovery_sulfur"))throw new IllegalArgumentException("Missing discovery sulfur biome");
|
||||
for(var n:List.of("forest","flowers","autumn","cherry","jungle","swamp","bare","cliffs","caves","grove","lush","marsh","mycelium","summit","void"))
|
||||
if(!named.containsKey("adventure_"+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<AdventureBiomes180> 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)>s.field().base().terrainBound())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);
|
||||
});
|
||||
}
|
||||
/** Decoration policy only; the density function and biome selection stay identical to beta.188. */
|
||||
public boolean stableGallery198(){return stableGallery198;}
|
||||
public boolean polish197(){return polish197;}
|
||||
public boolean originalRelief(){return originalRelief;}
|
||||
public boolean naturalSulfur(){return naturalSulfur;}
|
||||
public long seed(){return state.seed();}
|
||||
public ReliefEcology177.Field field(){return state.field();}
|
||||
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 extra=expansions;
|
||||
if(extra!=null&&extra.regionAt(x,z)!=null)return extra.source().biomeAt(x,y,z);
|
||||
var s=state;if(s==null)throw new IllegalStateException("Unbound rocky ecology");
|
||||
if(y<0 || y>=512 || Math.hypot(x,z)>s.field().base().terrainBound())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->"cherry";case 1->"autumn";default->"bare";});
|
||||
}
|
||||
if(discovery&&!naturalSulfur&&x>=-184&&x<=-120&&z>=124&&z<=180&&Sulfur183.biome(this,x,y,z))return named.get("discovery_sulfur");
|
||||
var t=terrain(x,z);if(t.top()<0)return get("void");
|
||||
if(y<t.top()-12 && (!s.field().coherent()||y<240||Continuity193.covered(s.field(),x,y,z))){
|
||||
if(naturalSulfur&&Sulfur187.biome(s.seed(),x,y,z,t.top()))return named.get("natural_sulfur_187");
|
||||
double wet=moisture(s.seed(),x,y,z);
|
||||
double kind=moisture(s.seed()^0x179B10L,x+71,y,z-93);
|
||||
if(wet<-.30)return get("caves");
|
||||
if(kind<-.25)return get("mycelium");
|
||||
if(kind>.25)return get("marsh");
|
||||
return get(wet>.15?"lush":"grove");
|
||||
}
|
||||
if(t.top()<AUTUMN_MIN)return get("cliffs");
|
||||
if(t.valley())return get("autumn");
|
||||
if(t.top()>=270)return get("summit");
|
||||
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("adventure_"+name);}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.*;
|
||||
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;
|
||||
|
||||
/** Local cave gardens and retained terrace pools; no region planner or chunk loads. */
|
||||
public final class AdventureCaves180 {
|
||||
private AdventureCaves180(){}
|
||||
static boolean natural(BlockState b){return b.is(net.minecraft.tags.BlockTags.BASE_STONE_OVERWORLD)
|
||||
|| b.is(Blocks.CALCITE)||b.is(Blocks.DIRT)||b.is(Blocks.GRASS_BLOCK)||b.is(Blocks.PODZOL)
|
||||
||b.is(Blocks.MYCELIUM)||b.is(Blocks.MOSS_BLOCK)||b.is(Blocks.MUD)||b.is(Blocks.CLAY);}
|
||||
static String biome(AdventureBiomes180 source,int x,int y,int z){return source.resolve(x,y,z).unwrapKey().orElseThrow().identifier().getPath();}
|
||||
static boolean covered(ChunkAccess c,int x,int y,int z){
|
||||
for(int dy=4;dy<=72 && y+dy<316;dy++)if(natural(c.getBlockState(new BlockPos(x,y+dy,z))))return true;
|
||||
return false;
|
||||
}
|
||||
public static void decorate(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
int bx=c.getPos().getMinBlockX(),bz=c.getPos().getMinBlockZ();boolean animal=false;
|
||||
var random=RandomSource.create(level.getSeed()^c.getPos().pack()^0x179DEC0L);
|
||||
for(int x=bx+1;x<bx+15;x++)for(int z=bz+1;z<bz+15;z++){
|
||||
int floors=0;
|
||||
for(int y=Math.min(246,source.terrain(x,z).top()-16);y>=72;y--){
|
||||
var floor=new BlockPos(x,y,z);var at=floor.above();
|
||||
if(!c.getBlockState(at).isAir())continue;
|
||||
var ground=c.getBlockState(floor);boolean water=ground.is(Blocks.WATER);
|
||||
if(!water&&!natural(ground))continue;
|
||||
if(Dungeon180.protectedAt(source,x,y,z))continue;
|
||||
String b=biome(source,x,y,z);if(b.equals("adventure_caves")||!covered(c,x,y,z))continue;
|
||||
if(!b.equals("adventure_grove")&&!b.equals("adventure_lush")&&!b.equals("adventure_marsh")&&!b.equals("adventure_mycelium"))continue;
|
||||
if(water){if(random.nextInt(7)==0)plant(level,at,Blocks.LILY_PAD);continue;}
|
||||
// Native trees get proper soil only on already existing rock, never over void.
|
||||
boolean mycelium=b.equals("adventure_mycelium"),marsh=b.equals("adventure_marsh");
|
||||
if(c.getBlockState(at).isAir()){
|
||||
int roll=random.nextInt(28);
|
||||
if(roll<2)plant(level,at,Blocks.FIREFLY_BUSH);
|
||||
else if(roll<5&&mycelium)plant(level,at,random.nextBoolean()?Blocks.RED_MUSHROOM:Blocks.BROWN_MUSHROOM);
|
||||
else if(roll==5)plant(level,at,Blocks.FLOWERING_AZALEA);
|
||||
else if(roll<10)plant(level,at,Blocks.SHORT_GRASS);
|
||||
|
||||
}
|
||||
if(!animal && random.nextInt(70)==0 && c.getBlockState(at).isAir()&&c.getBlockState(at.above()).isAir()){
|
||||
if(mycelium&&ground.is(Blocks.MYCELIUM))animal=spawn(level,at,EntityTypes.MOOSHROOM,random);
|
||||
else if(marsh)animal=spawn(level,at,EntityTypes.FROG,random);
|
||||
}
|
||||
if(++floors>=3)break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static void groves(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var random=RandomSource.create(level.getSeed()^c.getPos().pack()^0x179600DL);int placed=0;
|
||||
for(int attempt=0;attempt<12&&placed<3;attempt++){
|
||||
int x=c.getPos().getMinBlockX()+5+random.nextInt(6),z=c.getPos().getMinBlockZ()+5+random.nextInt(6);
|
||||
for(int y=Math.min(246,source.terrain(x,z).top()-16);y>=72;y--){
|
||||
var floor=new BlockPos(x,y,z);var at=floor.above();
|
||||
if(!natural(c.getBlockState(floor))||!c.getBlockState(at).isAir()||!covered(c,x,y,z))continue;
|
||||
if(Dungeon180.protectedAt(source,x,y,z))continue;
|
||||
String b=biome(source,x,y,z);boolean mushroom=b.equals("adventure_mycelium"),oak=b.equals("adventure_grove"),marsh=b.equals("adventure_marsh");
|
||||
if(!mushroom&&!oak&&!marsh&&!b.equals("adventure_lush"))continue;
|
||||
boolean room=true;
|
||||
for(int dx=0;dx<2;dx++)for(int dz=0;dz<2;dz++){
|
||||
var p=floor.offset(dx,0,dz);
|
||||
if(!natural(c.getBlockState(p))||!c.getBlockState(p.above(7)).isAir()){room=false;break;}
|
||||
for(int dy=1;dy<=6;dy++)if(!c.getBlockState(p.above(dy)).isAir()){room=false;break;}
|
||||
}
|
||||
if(!room)continue;
|
||||
for(int dx=0;dx<2;dx++)for(int dz=0;dz<2;dz++)c.setBlockState(floor.offset(dx,0,dz),(mushroom?Blocks.MYCELIUM:Blocks.GRASS_BLOCK).defaultBlockState());
|
||||
String id=mushroom?(random.nextBoolean()?"huge_red_mushroom":"huge_brown_mushroom"):oak?"dark_oak":marsh?"swamp_oak":random.nextBoolean()?"huge_brown_mushroom":"azalea_tree";
|
||||
var feature=level.registryAccess().lookupOrThrow(Registries.FEATURE).getValue(net.minecraft.resources.Identifier.withDefaultNamespace(id));
|
||||
if(feature!=null&&feature.place(level,generator,random,at)){placed++;break;}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void plant(WorldGenLevel level,BlockPos at,Block block){var b=block.defaultBlockState();if(level.getBlockState(at).isAir()&&b.canSurvive(level,at))level.setBlock(at,b,2);}
|
||||
static <T extends Mob> boolean spawn(WorldGenLevel level,BlockPos at,EntityType<T> type,RandomSource random){
|
||||
var mob=type.create(level.getLevel(),EntitySpawnReason.CHUNK_GENERATION);if(mob==null)return false;
|
||||
mob.snapTo(at.getX()+.5,at.getY(),at.getZ()+.5,random.nextFloat()*360,0);
|
||||
mob.finalizeSpawn(level,level.getCurrentDifficultyAt(at),EntitySpawnReason.CHUNK_GENERATION,null);
|
||||
mob.setPersistenceRequired();return level.addFreshEntity(mob);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** Verify real trees, connected large water footprints and native dungeon contents before visiting. */
|
||||
final class AdventureChecks180 {
|
||||
private AdventureChecks180(){}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).getBiomeSource();
|
||||
for(var p:Cherry180.sites(source)){level.getChunk(p.getX()>>4,p.getZ()>>4);}
|
||||
var trees=Cherry180.trees(source);
|
||||
if(trees.isEmpty()){
|
||||
// On reload the placement counter is empty; inspect the saved trunks without regenerating them.
|
||||
var saved=new ArrayList<BlockPos>();
|
||||
int start=source.field().eroded()?0:5,end=source.field().eroded()?15:11;
|
||||
for(var site:Cherry180.sites(source))for(int x=(site.getX()>>4<<4)+start;x<=(site.getX()>>4<<4)+end;x++)
|
||||
for(int z=(site.getZ()>>4<<4)+start;z<=(site.getZ()>>4<<4)+end;z++)for(int y=source.field().coherent()?251:271;y<=316;y++){
|
||||
var p=new BlockPos(x,y,z);
|
||||
if(level.getBlockState(p).is(Blocks.CHERRY_LOG)&&AdventureCaves180.natural(level.getBlockState(p.below())))saved.add(p);
|
||||
}
|
||||
trees=List.copyOf(saved);
|
||||
}
|
||||
int expected=source.field().coherent()?Cherry180.sites(source).size():3;
|
||||
if(expected<2||expected>3||trees.size()!=expected)throw new IllegalStateException("Expected "+expected+" main-island cherry trees: "+trees);
|
||||
for(var p:trees)if(!level.getBlockState(p).is(Blocks.CHERRY_LOG))throw new IllegalStateException("Missing actual cherry trunk at "+p);
|
||||
var reefs=source.field().base().reefs();
|
||||
if(!AdventureCaves180.biome(source,reefs.get(0).x(),reefs.get(0).y(),reefs.get(0).z()).equals("adventure_cherry")||!AdventureCaves180.biome(source,reefs.get(1).x(),reefs.get(1).y(),reefs.get(1).z()).equals("adventure_autumn"))throw new IllegalStateException("Wrong themed reefs");
|
||||
var counts=new ArrayList<Integer>();
|
||||
for(var b:Basins180.sites(source)){
|
||||
int count=0;var visited=new HashSet<Long>();var queue=new ArrayDeque<BlockPos>();queue.add(new BlockPos(b.x(),b.y(),b.z()));
|
||||
while(!queue.isEmpty()){
|
||||
var p=queue.removeFirst();if(!visited.add(p.asLong())||Math.abs(p.getX()-b.x())>65||Math.abs(p.getZ()-b.z())>65)continue;
|
||||
if(!level.getBlockState(p).is(Blocks.WATER))continue;count++;
|
||||
for(var d:Direction.Plane.HORIZONTAL)queue.add(p.relative(d));
|
||||
}
|
||||
if(count<650)throw new IllegalStateException("Fragmented upper basin, water cells="+count+" "+b);
|
||||
var upper=new BlockPos(b.x()+b.dx()*23,b.y(),b.z()+b.dz()*23);
|
||||
var lower=new BlockPos(b.x()+b.dx()*37,b.y()-4,b.z()+b.dz()*37);
|
||||
if(!level.getBlockState(upper).is(Blocks.WATER)||!level.getBlockState(lower).is(Blocks.WATER))throw new IllegalStateException("Missing spillway pair");
|
||||
counts.add(count);
|
||||
}
|
||||
var plan=Dungeon180.plan(source);int spawners=0,caches=0,lanterns=0,frames=0;
|
||||
for(var r:plan.rooms()){
|
||||
var p=new BlockPos(r.x(),r.y()+1,r.z());level.getChunk(p.getX()>>4,p.getZ()>>4);
|
||||
if(r.kind()==1){if(!(level.getBlockEntity(p) instanceof SpawnerBlockEntity))throw new IllegalStateException("Missing spawner at "+p);spawners++;}
|
||||
if(r.kind()==2){if(!level.getBlockState(p).is(Blocks.RAIL))throw new IllegalStateException("Missing cache siding");caches++;}
|
||||
var walking=p.offset(2,0,0);
|
||||
if(!level.getBlockState(walking.below()).isSolidRender()||level.getBlockState(walking).isSolidRender()||level.getBlockState(walking.above()).isSolidRender())throw new IllegalStateException("Blocked room walkway "+walking);
|
||||
}
|
||||
// Sample all owned corridors, including chunk seams and the daylight exit.
|
||||
int missingFloor=0,blocked=0;
|
||||
var defects=new ArrayList<String>();
|
||||
for(var cell:plan.paths().values()){
|
||||
if(Dungeon180.roomAt(plan,cell.x(),cell.z())!=null)continue;
|
||||
var p=new BlockPos(cell.x(),cell.y(),cell.z());
|
||||
if(!level.getBlockState(p).isSolidRender()){missingFloor++;defects.add("floor:"+p);}
|
||||
if(level.getBlockState(p.above()).isSolidRender()||level.getBlockState(p.above(2)).isSolidRender()){blocked++;defects.add("blocked:"+p);}
|
||||
if(level.getBlockState(p.above(3)).is(Blocks.LANTERN))lanterns++;
|
||||
if(level.getBlockState(p.above(4)).is(Blocks.DARK_OAK_LOG))frames++;
|
||||
if(level.getBlockState(p.above()).is(Blocks.SEA_PICKLE))throw new IllegalStateException("Dry sea pickle");
|
||||
}
|
||||
if(missingFloor>0||blocked>0)throw new IllegalStateException("Dungeon passage defects: floor="+missingFloor+" blocked="+blocked+" "+defects);
|
||||
if(spawners<4||caches<4||lanterns>frames/2+4)throw new IllegalStateException("Dungeon population or lighting mismatch");
|
||||
var loot=level.getServer().reloadableRegistries().getLootTable(Dungeon180.LOOT);
|
||||
var items=loot.getRandomItems(new net.minecraft.world.level.storage.loot.LootParams.Builder(level)
|
||||
.withParameter(net.minecraft.world.level.storage.loot.parameters.LootContextParams.ORIGIN,new net.minecraft.world.phys.Vec3(0,150,0))
|
||||
.create(net.minecraft.world.level.storage.loot.parameters.LootContextParamSets.CHEST));
|
||||
var found=new HashSet<String>();for(var stack:items)found.add(net.minecraft.core.registries.BuiltInRegistries.ITEM.getKey(stack.getItem()).toString());
|
||||
if(!found.containsAll(Set.of("minecraft:diamond","minecraft:emerald","sanctuary:ruby","sanctuary:sapphire")))throw new IllegalStateException("Missing requested gems in native loot: "+found);
|
||||
var result=new LinkedHashMap<String,Object>();result.put("lootItems",found);result.put("cherryTrees",trees);result.put("upperBasinAreas",counts);result.put("basins",Basins180.sites(source));result.put("rooms",plan.rooms());result.put("spawners",spawners);result.put("lootCarts",caches);result.put("corridorLanterns",lanterns);result.put("supportFrames",frames);return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
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 AdventureStone180(java.util.Optional<net.minecraft.world.level.levelgen.densityfunction.DensityFunction> slope) implements MaterialRule {
|
||||
public static final MapCodec<AdventureStone180> CODEC=com.mojang.serialization.codecs.RecordCodecBuilder.mapCodec(i->i.group(
|
||||
net.minecraft.world.level.levelgen.densityfunction.DensityFunction.CODEC.optionalFieldOf("slope").forGetter(AdventureStone180::slope)
|
||||
).apply(i,AdventureStone180::new));
|
||||
private static final java.util.Set<String> CAVES=java.util.Set.of("adventure_caves","adventure_grove","adventure_lush","adventure_marsh","adventure_mycelium");
|
||||
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();
|
||||
var slopeGetter=slope.map(d->context.getDensitiesInChunk(d,false)).orElse(null);
|
||||
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 sulfur=biome.equals("natural_sulfur_187");
|
||||
boolean cave=sulfur||CAVES.contains(biome);
|
||||
if(sulfur){
|
||||
// Coherent mineral beds on the original rock, including ceilings. Never carve a shell.
|
||||
double deposit=volume(seed^0x1875A1L,x,y,z,17);
|
||||
if(deposit>-.08)return (deposit>.51?Blocks.CINNABAR:Blocks.SULFUR).defaultBlockState();
|
||||
if(deposit>-.25)return Blocks.CALCITE.defaultBlockState();
|
||||
}
|
||||
if(y>=48 && context.stoneDepthAbove()<=3){
|
||||
if(cave){
|
||||
double patch=volume(seed^0x178A055L,x,y,z,8);
|
||||
if(context.stoneDepthAbove()==1){
|
||||
if(biome.equals("adventure_mycelium"))return (patch>-.20?Blocks.MYCELIUM:Blocks.GRASS_BLOCK).defaultBlockState();
|
||||
if(biome.equals("adventure_marsh"))return (patch>.20?Blocks.MUD:Blocks.GRASS_BLOCK).defaultBlockState();
|
||||
if(biome.equals("adventure_lush") && patch>-.50)return Blocks.MOSS_BLOCK.defaultBlockState();
|
||||
if(biome.equals("adventure_grove"))return (patch>.10?Blocks.MOSS_BLOCK:patch<-.35?Blocks.PODZOL:Blocks.GRASS_BLOCK).defaultBlockState();
|
||||
if(patch>.72)return Blocks.MOSS_BLOCK.defaultBlockState();
|
||||
}
|
||||
}else if(!biome.equals("adventure_cliffs") && !biome.equals("adventure_void")
|
||||
&& !(slopeGetter!=null && y>=248 && y<320 && slopeGetter.get()>.95f))
|
||||
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<AdventureStone180> codec(){return CODEC;}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import java.util.*;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.*;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
/** Local offers for this new preset only. Native block states own progress; no expansion is activated. */
|
||||
final class AnchorOffers186 {
|
||||
static final BlockPos CORE=new BlockPos(0,640,0);
|
||||
static final List<PalaceAnchors.Demand> DEMANDS=List.of(
|
||||
new PalaceAnchors.Demand(Items.STONE,32),new PalaceAnchors.Demand(Items.OAK_LOG,16),
|
||||
new PalaceAnchors.Demand(Items.WHEAT,16),new PalaceAnchors.Demand(Items.COPPER_INGOT,16),
|
||||
new PalaceAnchors.Demand(Items.COAL,16),new PalaceAnchors.Demand(Items.AMETHYST_SHARD,8),
|
||||
new PalaceAnchors.Demand(Items.IRON_INGOT,8),new PalaceAnchors.Demand(Items.GLASS,16));
|
||||
private AnchorOffers186() {}
|
||||
static void register(){
|
||||
UseBlockCallback.EVENT.register((player,world,hand,hit)->{
|
||||
if(!(player instanceof ServerPlayer p)||!(world instanceof ServerLevel level)
|
||||
||hand!=InteractionHand.MAIN_HAND||p.isSpectator()||!WorldgenLab.anchors186(level.getChunkSource().getGenerator()))return InteractionResult.PASS;
|
||||
var pos=hit.getBlockPos();var block=level.getBlockState(pos);
|
||||
if(!block.is(ExpansionAnchorBlock.BLOCK))return InteractionResult.PASS;
|
||||
int sector=block.getValue(ExpansionAnchorBlock.SECTOR);
|
||||
var result=offer(level,pos,p.getItemInHand(hand));var demand=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;
|
||||
});
|
||||
}
|
||||
static PalaceAnchors.Result offer(ServerLevel level,BlockPos pos,ItemStack held){
|
||||
var g=level.getChunkSource().getGenerator();if(!WorldgenLab.anchors186(g))return PalaceAnchors.Result.INVALID;
|
||||
var site=Anchors186.plan(Anchors186.source(g),level.getSeed()).sites().stream().filter(s->s.anchor().equals(pos)).findFirst().orElse(null);
|
||||
if(site==null)return PalaceAnchors.Result.INVALID;
|
||||
var anchor=level.getBlockState(pos);
|
||||
if(!anchor.is(ExpansionAnchorBlock.BLOCK)||anchor.getValue(ExpansionAnchorBlock.SECTOR)!=site.sector())return PalaceAnchors.Result.INVALID;
|
||||
if(anchor.getValue(ExpansionAnchorBlock.LIT))return PalaceAnchors.Result.ALREADY_ACTIVE;
|
||||
var core=level.getBlockState(CORE);
|
||||
if(!core.is(OriginBlock.BLOCK))return PalaceAnchors.Result.INVALID;
|
||||
if(!core.getValue(OriginBlock.LIT))return PalaceAnchors.Result.CORE_OFF;
|
||||
var demand=DEMANDS.get(site.sector());
|
||||
if(!held.is(demand.item())||held.getCount()<demand.count())return PalaceAnchors.Result.REQUIRED;
|
||||
level.setBlock(CORE,OriginBlock.relay(core,site.sector(),true),Block.UPDATE_ALL);
|
||||
level.setBlock(pos,anchor.setValue(ExpansionAnchorBlock.LIT,true),Block.UPDATE_ALL);
|
||||
held.shrink(demand.count());return PalaceAnchors.Result.ACTIVATED;
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,List<Anchors186.Site> sites){
|
||||
var before=level.getBlockState(CORE);var anchors=new HashMap<BlockPos,net.minecraft.world.level.block.state.BlockState>();
|
||||
for(var s:sites)anchors.put(s.anchor(),level.getBlockState(s.anchor()));
|
||||
try {
|
||||
level.setBlock(CORE,before.setValue(OriginBlock.RELAYS,0).setValue(OriginBlock.LIT,true),Block.UPDATE_ALL);
|
||||
int mask=0;
|
||||
for(int i:new int[]{7,2,5,0,6,1,4,3}){
|
||||
var site=sites.get(i);var demand=DEMANDS.get(i);
|
||||
level.setBlock(site.anchor(),anchors.get(site.anchor()).setValue(ExpansionAnchorBlock.LIT,false),Block.UPDATE_ALL);
|
||||
var wrong=new ItemStack(Items.DIRT,64);var shortStack=new ItemStack(demand.item(),demand.count()-1);
|
||||
if(offer(level,site.anchor(),wrong)!=PalaceAnchors.Result.REQUIRED||wrong.getCount()!=64
|
||||
||offer(level,site.anchor(),shortStack)!=PalaceAnchors.Result.REQUIRED||shortStack.getCount()!=demand.count()-1)throw new IllegalStateException("Invalid offer consumed");
|
||||
var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ACTIVATED||held.getCount()!=1)throw new IllegalStateException("Valid offer failed");
|
||||
mask|=1<<i;
|
||||
if(level.getBlockState(CORE).getValue(OriginBlock.RELAYS)!=mask||!level.getBlockState(site.anchor()).getValue(ExpansionAnchorBlock.LIT))throw new IllegalStateException("Wrong relay bit");
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ALREADY_ACTIVE||held.getCount()!=1)throw new IllegalStateException("Repeated offer consumed");
|
||||
}
|
||||
return Map.of("distinctMaterials",8,"relayMask",mask,"invalidAndRepeatedOffersPreserved",true);
|
||||
} finally {
|
||||
level.setBlock(CORE,before,Block.UPDATE_ALL);anchors.forEach((p,b)->level.setBlock(p,b,Block.UPDATE_ALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import java.util.*;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.*;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
/** Local offers for this new preset only. Native block states own progress; no expansion is activated. */
|
||||
final class AnchorOffers187 {
|
||||
static final BlockPos CORE=new BlockPos(0,640,0);
|
||||
static final List<PalaceAnchors.Demand> DEMANDS=List.of(
|
||||
new PalaceAnchors.Demand(Items.STONE,32),new PalaceAnchors.Demand(Items.OAK_LOG,16),
|
||||
new PalaceAnchors.Demand(Items.WHEAT,16),new PalaceAnchors.Demand(Items.COPPER_INGOT,16),
|
||||
new PalaceAnchors.Demand(Items.COAL,16),new PalaceAnchors.Demand(Items.AMETHYST_SHARD,8),
|
||||
new PalaceAnchors.Demand(Items.IRON_INGOT,8),new PalaceAnchors.Demand(Items.GLASS,16));
|
||||
private AnchorOffers187() {}
|
||||
static void register(){
|
||||
UseBlockCallback.EVENT.register((player,world,hand,hit)->{
|
||||
if(!(player instanceof ServerPlayer p)||!(world instanceof ServerLevel level)
|
||||
||hand!=InteractionHand.MAIN_HAND||p.isSpectator()||!WorldgenLab.natural187(level.getChunkSource().getGenerator()))return InteractionResult.PASS;
|
||||
var pos=hit.getBlockPos();var block=level.getBlockState(pos);
|
||||
if(!block.is(ExpansionAnchorBlock.BLOCK))return InteractionResult.PASS;
|
||||
int sector=block.getValue(ExpansionAnchorBlock.SECTOR);
|
||||
var result=offer(level,pos,p.getItemInHand(hand));var demand=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;
|
||||
});
|
||||
}
|
||||
static PalaceAnchors.Result offer(ServerLevel level,BlockPos pos,ItemStack held){
|
||||
var g=level.getChunkSource().getGenerator();if(!WorldgenLab.natural187(g))return PalaceAnchors.Result.INVALID;
|
||||
var site=Anchors187.plan(Anchors187.source(g),level.getSeed()).sites().stream().filter(s->s.anchor().equals(pos)).findFirst().orElse(null);
|
||||
if(site==null)return PalaceAnchors.Result.INVALID;
|
||||
var anchor=level.getBlockState(pos);
|
||||
if(!anchor.is(ExpansionAnchorBlock.BLOCK)||anchor.getValue(ExpansionAnchorBlock.SECTOR)!=site.sector())return PalaceAnchors.Result.INVALID;
|
||||
if(anchor.getValue(ExpansionAnchorBlock.LIT))return PalaceAnchors.Result.ALREADY_ACTIVE;
|
||||
var core=level.getBlockState(CORE);
|
||||
if(!core.is(OriginBlock.BLOCK))return PalaceAnchors.Result.INVALID;
|
||||
if(!core.getValue(OriginBlock.LIT))return PalaceAnchors.Result.CORE_OFF;
|
||||
var demand=DEMANDS.get(site.sector());
|
||||
if(!held.is(demand.item())||held.getCount()<demand.count())return PalaceAnchors.Result.REQUIRED;
|
||||
level.setBlock(CORE,OriginBlock.relay(core,site.sector(),true),Block.UPDATE_ALL);
|
||||
level.setBlock(pos,anchor.setValue(ExpansionAnchorBlock.LIT,true),Block.UPDATE_ALL);
|
||||
held.shrink(demand.count());return PalaceAnchors.Result.ACTIVATED;
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,List<Anchors187.Site> sites){
|
||||
var before=level.getBlockState(CORE);var anchors=new HashMap<BlockPos,net.minecraft.world.level.block.state.BlockState>();
|
||||
for(var s:sites)anchors.put(s.anchor(),level.getBlockState(s.anchor()));
|
||||
try {
|
||||
level.setBlock(CORE,before.setValue(OriginBlock.RELAYS,0).setValue(OriginBlock.LIT,true),Block.UPDATE_ALL);
|
||||
int mask=0;
|
||||
for(int i:new int[]{7,2,5,0,6,1,4,3}){
|
||||
var site=sites.get(i);var demand=DEMANDS.get(i);
|
||||
level.setBlock(site.anchor(),anchors.get(site.anchor()).setValue(ExpansionAnchorBlock.LIT,false),Block.UPDATE_ALL);
|
||||
var wrong=new ItemStack(Items.DIRT,64);var shortStack=new ItemStack(demand.item(),demand.count()-1);
|
||||
if(offer(level,site.anchor(),wrong)!=PalaceAnchors.Result.REQUIRED||wrong.getCount()!=64
|
||||
||offer(level,site.anchor(),shortStack)!=PalaceAnchors.Result.REQUIRED||shortStack.getCount()!=demand.count()-1)throw new IllegalStateException("Invalid offer consumed");
|
||||
var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ACTIVATED||held.getCount()!=1)throw new IllegalStateException("Valid offer failed");
|
||||
mask|=1<<i;
|
||||
if(level.getBlockState(CORE).getValue(OriginBlock.RELAYS)!=mask||!level.getBlockState(site.anchor()).getValue(ExpansionAnchorBlock.LIT))throw new IllegalStateException("Wrong relay bit");
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ALREADY_ACTIVE||held.getCount()!=1)throw new IllegalStateException("Repeated offer consumed");
|
||||
}
|
||||
return Map.of("distinctMaterials",8,"relayMask",mask,"invalidAndRepeatedOffersPreserved",true);
|
||||
} finally {
|
||||
level.setBlock(CORE,before,Block.UPDATE_ALL);anchors.forEach((p,b)->level.setBlock(p,b,Block.UPDATE_ALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import java.util.*;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.*;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
/** Local offers for this new preset only. Native block states own progress; no expansion is activated. */
|
||||
final class AnchorOffers188 {
|
||||
static final BlockPos CORE=new BlockPos(0,640,0);
|
||||
static final List<PalaceAnchors.Demand> DEMANDS=List.of(
|
||||
new PalaceAnchors.Demand(Items.STONE,32),new PalaceAnchors.Demand(Items.OAK_LOG,16),
|
||||
new PalaceAnchors.Demand(Items.WHEAT,16),new PalaceAnchors.Demand(Items.COPPER_INGOT,16),
|
||||
new PalaceAnchors.Demand(Items.COAL,16),new PalaceAnchors.Demand(Items.AMETHYST_SHARD,8),
|
||||
new PalaceAnchors.Demand(Items.IRON_INGOT,8),new PalaceAnchors.Demand(Items.GLASS,16));
|
||||
private AnchorOffers188() {}
|
||||
static void register(){
|
||||
UseBlockCallback.EVENT.register((player,world,hand,hit)->{
|
||||
if(!(player instanceof ServerPlayer p)||!(world instanceof ServerLevel level)
|
||||
||hand!=InteractionHand.MAIN_HAND||p.isSpectator()||!WorldgenLab.details188(level.getChunkSource().getGenerator()))return InteractionResult.PASS;
|
||||
var pos=hit.getBlockPos();var block=level.getBlockState(pos);
|
||||
if(!block.is(ExpansionAnchorBlock.BLOCK))return InteractionResult.PASS;
|
||||
int sector=block.getValue(ExpansionAnchorBlock.SECTOR);
|
||||
var result=offer(level,pos,p.getItemInHand(hand));var demand=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;
|
||||
});
|
||||
}
|
||||
static PalaceAnchors.Result offer(ServerLevel level,BlockPos pos,ItemStack held){
|
||||
var g=level.getChunkSource().getGenerator();if(!WorldgenLab.details188(g))return PalaceAnchors.Result.INVALID;
|
||||
var site=Anchors188.plan(Anchors188.source(g),level.getSeed()).sites().stream().filter(s->s.anchor().equals(pos)).findFirst().orElse(null);
|
||||
if(site==null)return PalaceAnchors.Result.INVALID;
|
||||
var anchor=level.getBlockState(pos);
|
||||
if(!anchor.is(ExpansionAnchorBlock.BLOCK)||anchor.getValue(ExpansionAnchorBlock.SECTOR)!=site.sector())return PalaceAnchors.Result.INVALID;
|
||||
if(anchor.getValue(ExpansionAnchorBlock.LIT))return PalaceAnchors.Result.ALREADY_ACTIVE;
|
||||
var core=level.getBlockState(CORE);
|
||||
if(!core.is(OriginBlock.BLOCK))return PalaceAnchors.Result.INVALID;
|
||||
if(!core.getValue(OriginBlock.LIT))return PalaceAnchors.Result.CORE_OFF;
|
||||
var demand=DEMANDS.get(site.sector());
|
||||
if(!held.is(demand.item())||held.getCount()<demand.count())return PalaceAnchors.Result.REQUIRED;
|
||||
level.setBlock(CORE,OriginBlock.relay(core,site.sector(),true),Block.UPDATE_ALL);
|
||||
level.setBlock(pos,anchor.setValue(ExpansionAnchorBlock.LIT,true),Block.UPDATE_ALL);
|
||||
held.shrink(demand.count());return PalaceAnchors.Result.ACTIVATED;
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,List<Anchors188.Site> sites){
|
||||
var before=level.getBlockState(CORE);var anchors=new HashMap<BlockPos,net.minecraft.world.level.block.state.BlockState>();
|
||||
for(var s:sites)anchors.put(s.anchor(),level.getBlockState(s.anchor()));
|
||||
try {
|
||||
level.setBlock(CORE,before.setValue(OriginBlock.RELAYS,0).setValue(OriginBlock.LIT,true),Block.UPDATE_ALL);
|
||||
int mask=0;
|
||||
for(int i:new int[]{7,2,5,0,6,1,4,3}){
|
||||
var site=sites.get(i);var demand=DEMANDS.get(i);
|
||||
level.setBlock(site.anchor(),anchors.get(site.anchor()).setValue(ExpansionAnchorBlock.LIT,false),Block.UPDATE_ALL);
|
||||
var wrong=new ItemStack(Items.DIRT,64);var shortStack=new ItemStack(demand.item(),demand.count()-1);
|
||||
if(offer(level,site.anchor(),wrong)!=PalaceAnchors.Result.REQUIRED||wrong.getCount()!=64
|
||||
||offer(level,site.anchor(),shortStack)!=PalaceAnchors.Result.REQUIRED||shortStack.getCount()!=demand.count()-1)throw new IllegalStateException("Invalid offer consumed");
|
||||
var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ACTIVATED||held.getCount()!=1)throw new IllegalStateException("Valid offer failed");
|
||||
mask|=1<<i;
|
||||
if(level.getBlockState(CORE).getValue(OriginBlock.RELAYS)!=mask||!level.getBlockState(site.anchor()).getValue(ExpansionAnchorBlock.LIT))throw new IllegalStateException("Wrong relay bit");
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ALREADY_ACTIVE||held.getCount()!=1)throw new IllegalStateException("Repeated offer consumed");
|
||||
}
|
||||
return Map.of("distinctMaterials",8,"relayMask",mask,"invalidAndRepeatedOffersPreserved",true);
|
||||
} finally {
|
||||
level.setBlock(CORE,before,Block.UPDATE_ALL);anchors.forEach((p,b)->level.setBlock(p,b,Block.UPDATE_ALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import java.util.*;
|
||||
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.*;
|
||||
import net.minecraft.world.*;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
/** Local offers for this new preset only. Native block states own progress; no expansion is activated. */
|
||||
final class AnchorOffers189 {
|
||||
static final BlockPos CORE=new BlockPos(0,640,0);
|
||||
static final List<PalaceAnchors.Demand> DEMANDS=List.of(
|
||||
new PalaceAnchors.Demand(Items.STONE,32),new PalaceAnchors.Demand(Items.OAK_LOG,16),
|
||||
new PalaceAnchors.Demand(Items.WHEAT,16),new PalaceAnchors.Demand(Items.COPPER_INGOT,16),
|
||||
new PalaceAnchors.Demand(Items.COAL,16),new PalaceAnchors.Demand(Items.AMETHYST_SHARD,8),
|
||||
new PalaceAnchors.Demand(Items.IRON_INGOT,8),new PalaceAnchors.Demand(Items.GLASS,16));
|
||||
private AnchorOffers189() {}
|
||||
static void register(){
|
||||
UseBlockCallback.EVENT.register((player,world,hand,hit)->{
|
||||
if(!(player instanceof ServerPlayer p)||!(world instanceof ServerLevel level)
|
||||
||hand!=InteractionHand.MAIN_HAND||p.isSpectator()||!WorldgenLab.finishedTerrain(level.getChunkSource().getGenerator()))return InteractionResult.PASS;
|
||||
var pos=hit.getBlockPos();var block=level.getBlockState(pos);
|
||||
if(!block.is(ExpansionAnchorBlock.BLOCK)||SharedIsland196.revision(level.getChunkSource().getGenerator())>=199)return InteractionResult.PASS;
|
||||
int sector=block.getValue(ExpansionAnchorBlock.SECTOR);
|
||||
var result=offer(level,pos,p.getItemInHand(hand));var demand=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;
|
||||
});
|
||||
}
|
||||
static PalaceAnchors.Result offer(ServerLevel level,BlockPos pos,ItemStack held){
|
||||
var g=level.getChunkSource().getGenerator();if(!WorldgenLab.finishedTerrain(g))return PalaceAnchors.Result.INVALID;
|
||||
var site=Anchors189.plan(Anchors189.source(g),level.getSeed()).sites().stream().filter(s->s.anchor().equals(pos)).findFirst().orElse(null);
|
||||
if(site==null)return PalaceAnchors.Result.INVALID;
|
||||
var anchor=level.getBlockState(pos);
|
||||
if(!anchor.is(ExpansionAnchorBlock.BLOCK)||anchor.getValue(ExpansionAnchorBlock.SECTOR)!=site.sector())return PalaceAnchors.Result.INVALID;
|
||||
if(anchor.getValue(ExpansionAnchorBlock.LIT))return PalaceAnchors.Result.ALREADY_ACTIVE;
|
||||
var core=level.getBlockState(CORE);
|
||||
if(!core.is(OriginBlock.BLOCK))return PalaceAnchors.Result.INVALID;
|
||||
if(!core.getValue(OriginBlock.LIT))return PalaceAnchors.Result.CORE_OFF;
|
||||
var demand=DEMANDS.get(site.sector());
|
||||
if(!held.is(demand.item())||held.getCount()<demand.count())return PalaceAnchors.Result.REQUIRED;
|
||||
level.setBlock(CORE,OriginBlock.relay(core,site.sector(),true),Block.UPDATE_ALL);
|
||||
level.setBlock(pos,anchor.setValue(ExpansionAnchorBlock.LIT,true),Block.UPDATE_ALL);
|
||||
held.shrink(demand.count());return PalaceAnchors.Result.ACTIVATED;
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,List<Anchors189.Site> sites){
|
||||
var before=level.getBlockState(CORE);var anchors=new HashMap<BlockPos,net.minecraft.world.level.block.state.BlockState>();
|
||||
for(var s:sites)anchors.put(s.anchor(),level.getBlockState(s.anchor()));
|
||||
try {
|
||||
level.setBlock(CORE,before.setValue(OriginBlock.RELAYS,0).setValue(OriginBlock.LIT,true),Block.UPDATE_ALL);
|
||||
int mask=0;
|
||||
for(int i:new int[]{7,2,5,0,6,1,4,3}){
|
||||
var site=sites.get(i);var demand=DEMANDS.get(i);
|
||||
level.setBlock(site.anchor(),anchors.get(site.anchor()).setValue(ExpansionAnchorBlock.LIT,false),Block.UPDATE_ALL);
|
||||
var wrong=new ItemStack(Items.DIRT,64);var shortStack=new ItemStack(demand.item(),demand.count()-1);
|
||||
if(offer(level,site.anchor(),wrong)!=PalaceAnchors.Result.REQUIRED||wrong.getCount()!=64
|
||||
||offer(level,site.anchor(),shortStack)!=PalaceAnchors.Result.REQUIRED||shortStack.getCount()!=demand.count()-1)throw new IllegalStateException("Invalid offer consumed");
|
||||
var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ACTIVATED||held.getCount()!=1)throw new IllegalStateException("Valid offer failed");
|
||||
mask|=1<<i;
|
||||
if(level.getBlockState(CORE).getValue(OriginBlock.RELAYS)!=mask||!level.getBlockState(site.anchor()).getValue(ExpansionAnchorBlock.LIT))throw new IllegalStateException("Wrong relay bit");
|
||||
if(offer(level,site.anchor(),held)!=PalaceAnchors.Result.ALREADY_ACTIVE||held.getCount()!=1)throw new IllegalStateException("Repeated offer consumed");
|
||||
}
|
||||
return Map.of("distinctMaterials",8,"relayMask",mask,"invalidAndRepeatedOffersPreserved",true);
|
||||
} finally {
|
||||
level.setBlock(CORE,before,Block.UPDATE_ALL);anchors.forEach((p,b)->level.setBlock(p,b,Block.UPDATE_ALL));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.ExpansionAnchorBlock;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
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.densityfunction.SamplerContext;
|
||||
|
||||
/** Eight seed-owned refuges. Planning reads density only; placement never loads a neighbour chunk. */
|
||||
public final class Anchors186 {
|
||||
enum Kind { SURFACE, CAVE, SKY }
|
||||
record Site(int sector,BlockPos floor,Kind kind,int dx,int dz,int rx,int rz,boolean bright) {
|
||||
BlockPos anchor(){return floor.above(2);}
|
||||
BlockPos entrance(){return floor.offset(dx*11,1,dz*11);}
|
||||
}
|
||||
record Plan(List<Site> sites,Map<ChunkPos,Map<BlockPos,BlockState>> chunks,long millis) {}
|
||||
private record Candidate(BlockPos floor,Kind kind,int dx,int dz,double score) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Anchors186() {}
|
||||
static AdventureBiomes180 source(ChunkGenerator g){return (AdventureBiomes180)((NoiseBasedChunkGenerator)g).getBiomeSource();}
|
||||
static Plan plan(AdventureBiomes180 s,long seed){synchronized(PLANS){return PLANS.computeIfAbsent(s,key->create(s,seed));}}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
private static long mix(long n){n=(n^(n>>>30))*0xbf58476d1ce4e5b9L;n=(n^(n>>>27))*0x94d049bb133111ebL;return n^(n>>>31);}
|
||||
private static boolean reserved(AdventureBiomes180 s,int x,int z){
|
||||
if(Math.hypot(x,z)<88||Math.hypot(x+152,z-152)<52||Dungeon180.near(s,x,z,28))return true;
|
||||
for(var b:Basins180.sites(s))if(Math.hypot(x-b.x(),z-b.z())<78)return true;
|
||||
for(var p:NaturalWater184.plan(s).pools())if(Math.abs(x-p.seed().getX())<68&&Math.abs(z-p.seed().getZ())<68)return true;
|
||||
return false;
|
||||
}
|
||||
private static Candidate candidate(AdventureBiomes180 s,int x,int y,int z,Kind kind,long seed){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+3,z))return null;
|
||||
// Require existing material beneath the entire centre, including the small pedestal.
|
||||
for(int dx=-3;dx<=3;dx+=3)for(int dz=-3;dz<=3;dz+=3)
|
||||
if(!rock(s,x+dx,y-3,z+dz)||!rock(s,x+dx,y-5,z+dz))return null;
|
||||
int best=-1,ex=0,ez=0;
|
||||
for(var d:Direction.Plane.HORIZONTAL){
|
||||
int score=0;boolean end=false;
|
||||
for(int step=1;step<=11;step++){
|
||||
int px=x+d.getStepX()*step,pz=z+d.getStepZ()*step;
|
||||
if(!rock(s,px,y-3,pz))break;
|
||||
if(!rock(s,px,y+1,pz)&&!rock(s,px,y+2,pz))score++;
|
||||
if(step==11)end=!rock(s,px,y+1,pz)&&!rock(s,px,y+2,pz);
|
||||
}
|
||||
if(end)for(int step=2;step<=11;step++)for(int side=-1;side<=1;side++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int px=x+d.getStepX()*step-d.getStepZ()*(bend+side),pz=z+d.getStepZ()*step+d.getStepX()*(bend+side);
|
||||
if(!rock(s,px,y-3,pz))end=false;
|
||||
}
|
||||
if(end&&score>best){best=score;ex=d.getStepX();ez=d.getStepZ();}
|
||||
}
|
||||
if(best<7)return null;
|
||||
int air=0,roof=0;
|
||||
for(int dx=-6;dx<=6;dx+=3)for(int dz=-6;dz<=6;dz+=3){
|
||||
if(!rock(s,x+dx,y+3,z+dz))air++;
|
||||
if(rock(s,x+dx,y+12,z+dz)||rock(s,x+dx,y+24,z+dz))roof++;
|
||||
}
|
||||
if(kind==Kind.CAVE&&roof<8)return null;
|
||||
double jitter=(mix(seed^((long)x*73428767)^((long)y*912931)^z)>>>11)*0x1.0p-53;
|
||||
return new Candidate(new BlockPos(x,y,z),kind,ex,ez,air*.1+best*.15+jitter*9);
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s,long seed){
|
||||
long started=System.nanoTime();var candidates=new ArrayList<Candidate>();
|
||||
// Sparse fixed grid, finite vertical scan. No terrain regions, raycasts or runtime search.
|
||||
for(int x=-300;x<=300;x+=12)for(int z=-300;z<=300;z+=12){
|
||||
if(Math.hypot(x,z)>310||reserved(s,x,z))continue;
|
||||
int top=s.field().surface(x,z);if(top<140)continue;
|
||||
var surface=candidate(s,x,top,z,Kind.SURFACE,seed);if(surface!=null)candidates.add(surface);
|
||||
for(int y=Math.min(top-18,236);y>=92;y--){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+4,z))continue;
|
||||
var cave=candidate(s,x,y,z,Kind.CAVE,seed);if(cave!=null)candidates.add(cave);
|
||||
}
|
||||
}
|
||||
for(var reef:s.field().base().reefs()){
|
||||
if(reef.length()<30)continue;
|
||||
for(int dx=-12;dx<=12;dx+=6)for(int dz=-12;dz<=12;dz+=6){
|
||||
int x=reef.x()+dx,z=reef.z()+dz;
|
||||
for(int y=reef.y()+reef.rise()+2;y>=reef.y()-5;y--){
|
||||
if(!rock(s,x,y,z))continue;
|
||||
var sky=candidate(s,x,y,z,Kind.SKY,seed);if(sky!=null)candidates.add(sky);break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var chosen=new ArrayList<Site>();int skyCount=0;
|
||||
// Two eligible aerial sectors are selected by seed; other sectors alternate cave and surface preference.
|
||||
var aerial=new ArrayList<Integer>();
|
||||
for(int sector=0;sector<8;sector++){final int k=sector;if(candidates.stream().anyMatch(c->c.kind==Kind.SKY&&Rosette183.sector(c.floor.getX(),c.floor.getZ())==k))aerial.add(k);}
|
||||
aerial.sort(Comparator.comparingLong(k->mix(seed^k*0x186L)));
|
||||
var skySectors=new HashSet<>(aerial.subList(0,Math.min(2,aerial.size())));
|
||||
for(int sector=0;sector<8;sector++){
|
||||
final int k=sector;Kind preferred=skySectors.contains(k)?Kind.SKY:((sector+(int)(seed&1))%2==0?Kind.CAVE:Kind.SURFACE);
|
||||
var ranked=candidates.stream().filter(c->Rosette183.sector(c.floor.getX(),c.floor.getZ())==k)
|
||||
.sorted(Comparator.<Candidate>comparingDouble(c->c.score+(c.kind==preferred?100:0)).reversed()).toList();
|
||||
Candidate selected=null;
|
||||
for(var c:ranked){
|
||||
if(c.kind==Kind.SKY&&!skySectors.contains(k))continue;
|
||||
if(chosen.stream().anyMatch(v->v.floor.distSqr(c.floor)<44*44))continue;
|
||||
selected=c;break;
|
||||
}
|
||||
if(selected==null)throw new IllegalStateException("No naturally supported anchor in sector "+k+" for seed "+seed);
|
||||
var c=selected;long grain=mix(seed^k*7919L);
|
||||
chosen.add(new Site(k,c.floor,c.kind,c.dx,c.dz,6+(int)(grain&1),5+(int)((grain>>>1)&1),(grain&4)==0));
|
||||
if(c.kind==Kind.SKY)skyCount++;
|
||||
}
|
||||
if(skyCount==0)throw new IllegalStateException("No supported aerial anchor for seed "+seed);
|
||||
var chunks=new HashMap<ChunkPos,Map<BlockPos,BlockState>>();
|
||||
for(var site:chosen)for(var e:sculpt(s,site,seed).entrySet())
|
||||
chunks.computeIfAbsent(new ChunkPos(e.getKey().getX()>>4,e.getKey().getZ()>>4),key->new LinkedHashMap<>()).put(e.getKey(),e.getValue());
|
||||
chunks.replaceAll((k,v)->Map.copyOf(v));
|
||||
return new Plan(List.copyOf(chosen),Map.copyOf(chunks),(System.nanoTime()-started)/1_000_000);
|
||||
}
|
||||
private static void put(Map<BlockPos,BlockState> m,BlockPos p,Block b){m.put(p,b.defaultBlockState());}
|
||||
private static Map<BlockPos,BlockState> sculpt(AdventureBiomes180 s,Site site,long seed){
|
||||
var m=new LinkedHashMap<BlockPos,BlockState>();var o=site.floor;int y=o.getY();
|
||||
Block stone=site.kind==Kind.SKY?Blocks.CALCITE:site.kind==Kind.CAVE?Blocks.TUFF:Blocks.ANDESITE;
|
||||
double phase=(mix(seed^site.sector) & 1023)/163.0;
|
||||
for(int dx=-site.rx-2;dx<=site.rx+2;dx++)for(int dz=-site.rz-2;dz<=site.rz+2;dz++){
|
||||
int x=o.getX()+dx,z=o.getZ()+dz;
|
||||
double d=dx*dx/(double)(site.rx*site.rx)+dz*dz/(double)(site.rz*site.rz)+.12*Math.sin(dx*.7+phase)*Math.cos(dz*.6);
|
||||
if(d>1.15)continue;
|
||||
int floor=y+(Math.abs(dx)<=3&&Math.abs(dz)<=3?0:(int)Math.round(.8*Math.sin(dx*.5+phase)*Math.cos(dz*.5)));
|
||||
if(!rock(s,x,floor-3,z))continue; // Keep the edge of the host landform instead of hanging a platform over void.
|
||||
var at=new BlockPos(x,floor,z);
|
||||
if(d<=1){
|
||||
for(int h=-2;h<=0;h++)put(m,at.above(h),h==0&&Math.floorMod(x+z+site.sector,5)==0?Blocks.MOSS_BLOCK:stone);
|
||||
int height=3+(int)(4*Math.sqrt(Math.max(0,1-d)));
|
||||
for(int h=1;h<=height;h++)put(m,at.above(h),Blocks.AIR);
|
||||
if(d>.62&&Math.floorMod(x*7+z*13,11)==0){
|
||||
put(m,at,Blocks.MOSS_BLOCK);put(m,at.above(),Blocks.FLOWERING_AZALEA);
|
||||
}
|
||||
}else {
|
||||
// Short reliefs follow surviving rock faces; no closed constructed shell.
|
||||
for(int h=0;h<=5;h++)if(rock(s,x,floor+h,z))
|
||||
put(m,at.above(h),h==2&&Math.floorMod(x+z,3)==0?Blocks.CHISELED_TUFF:h==0?Blocks.MOSS_BLOCK:stone);
|
||||
}
|
||||
}
|
||||
// A slight bend opens to an actual, dry natural floor found during selection.
|
||||
for(int step=2;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int x=o.getX()+site.dx*step-site.dz*bend,z=o.getZ()+site.dz*step+site.dx*bend;
|
||||
for(int side=-1;side<=1;side++){
|
||||
var p=new BlockPos(x-site.dz*side,y,z+site.dx*side);
|
||||
for(int h=-2;h<=0;h++)put(m,p.above(h),h==0?Blocks.MOSS_BLOCK:stone);
|
||||
for(int h=1;h<=4;h++)put(m,p.above(h),Blocks.AIR);
|
||||
}
|
||||
}
|
||||
// Low eroded crests shelter surface sites; all are rooted in the existing mass.
|
||||
for(int side:new int[]{-1,1}){
|
||||
int dx=-site.dx*4-site.dz*side*3,dz=-site.dz*4+site.dx*side*3;
|
||||
var base=o.offset(dx,0,dz);if(!rock(s,base.getX(),y-3,base.getZ()))continue;
|
||||
for(int h=-2;h<=3+(site.sector+side&1);h++)put(m,base.above(h),h==2?Blocks.CHISELED_TUFF:stone);
|
||||
put(m,base.above(2).offset(site.dx,0,site.dz),Rosette183.GLASS[site.sector]);
|
||||
if(site.bright)put(m,base.above(2),Blocks.SEA_LANTERN);
|
||||
else put(m,base.above(3),Blocks.AMETHYST_BLOCK);
|
||||
}
|
||||
for(int dx=-1;dx<=1;dx++)for(int dz=-1;dz<=1;dz++){
|
||||
put(m,o.offset(dx,0,dz),Blocks.CHISELED_TUFF);
|
||||
for(int h=1;h<=4;h++)put(m,o.offset(dx,h,dz),Blocks.AIR);
|
||||
}
|
||||
put(m,o.above(),Rosette183.GLASS[site.sector]);
|
||||
m.put(site.anchor(),ExpansionAnchorBlock.BLOCK.defaultBlockState().setValue(ExpansionAnchorBlock.SECTOR,site.sector));
|
||||
return m;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator g,ChunkAccess chunk){
|
||||
if(!WorldgenLab.anchors186(g))return;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(chunk.getPos());
|
||||
if(entries!=null)entries.forEach(chunk::setBlockState);
|
||||
}
|
||||
public static boolean protects(WorldGenLevel level,ChunkGenerator g,BlockPos pos){
|
||||
if(!WorldgenLab.anchors186(g)||pos.getY()<88||pos.getY()>512||Math.abs(pos.getX())>325||Math.abs(pos.getZ())>325)return false;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(new ChunkPos(pos.getX()>>4,pos.getZ()>>4));
|
||||
return entries!=null&&entries.containsKey(pos);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 source){
|
||||
var p=plan(source,level.getSeed());var again=create(source,level.getSeed());
|
||||
if(!p.sites.equals(again.sites)||!p.chunks.equals(again.chunks))throw new IllegalStateException("Unstable anchor plan");
|
||||
int count=0;var details=new ArrayList<Map<String,Object>>();
|
||||
for(var cp:p.chunks.keySet())level.getChunk(cp.x(),cp.z());
|
||||
for(var e:p.chunks.entrySet())for(var b:e.getValue().entrySet()){
|
||||
if(!e.getKey().contains(b.getKey()))throw new IllegalStateException("Cross-chunk anchor write");
|
||||
if(!level.getBlockState(b.getKey()).equals(b.getValue()))throw new IllegalStateException("Anchor refuge mismatch "+b.getKey()+" expected "+b.getValue()+" got "+level.getBlockState(b.getKey()));
|
||||
if(b.getValue().is(ExpansionAnchorBlock.BLOCK))count++;
|
||||
}
|
||||
for(var site:p.sites){
|
||||
if(Rosette183.sector(site.floor.getX(),site.floor.getZ())!=site.sector)throw new IllegalStateException("Wrong colour sector");
|
||||
// Walk the full three-wide bent approach and verify its actual native support.
|
||||
for(int step=2;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;var at=site.floor.offset(site.dx*step-site.dz*bend,1,site.dz*step+site.dx*bend);
|
||||
if(!level.getBlockState(at).isAir()||!level.getBlockState(at.above()).isAir()||level.getBlockState(at.below()).getCollisionShape(level,at.below()).isEmpty())throw new IllegalStateException("Anchor approach blocked "+at);
|
||||
}
|
||||
if(!rock(source,site.floor.getX(),site.floor.getY()-5,site.floor.getZ()))throw new IllegalStateException("Anchor above void");
|
||||
details.add(Map.of("sector",site.sector,"kind",site.kind.name(),"anchor",site.anchor().toShortString(),"entrance",site.entrance().toShortString(),"bright",site.bright));
|
||||
}
|
||||
if(count!=8||p.sites.stream().map(Site::sector).distinct().count()!=8)throw new IllegalStateException("Expected exactly eight anchors");
|
||||
var offers=AnchorOffers186.check(level,p.sites);
|
||||
return Map.of("sites",details,"planningMs",p.millis,"chunks",p.chunks.size(),"blocks",p.chunks.values().stream().mapToInt(Map::size).sum(),"deterministic",true,"offers",offers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.ExpansionAnchorBlock;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
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.densityfunction.SamplerContext;
|
||||
|
||||
/** Eight seed-owned refuges. Planning reads density only; placement never loads a neighbour chunk. */
|
||||
public final class Anchors187 {
|
||||
enum Kind { SURFACE, CAVE, SKY }
|
||||
record Site(int sector,BlockPos floor,Kind kind,int dx,int dz,int rx,int rz,boolean bright) {
|
||||
BlockPos anchor(){return floor.above(2);}
|
||||
BlockPos entrance(){return floor.offset(dx*11,1,dz*11);}
|
||||
}
|
||||
record Plan(List<Site> sites,Map<ChunkPos,Map<BlockPos,BlockState>> chunks,long millis) {}
|
||||
private record Candidate(BlockPos floor,Kind kind,int dx,int dz,double score) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Anchors187() {}
|
||||
static AdventureBiomes180 source(ChunkGenerator g){return (AdventureBiomes180)((NoiseBasedChunkGenerator)g).getBiomeSource();}
|
||||
static Plan plan(AdventureBiomes180 s,long seed){synchronized(PLANS){return PLANS.computeIfAbsent(s,key->create(s,seed));}}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
private static long mix(long n){n=(n^(n>>>30))*0xbf58476d1ce4e5b9L;n=(n^(n>>>27))*0x94d049bb133111ebL;return n^(n>>>31);}
|
||||
private static boolean reserved(AdventureBiomes180 s,int x,int z){
|
||||
if(Math.hypot(x,z)<88||Dungeon180.near(s,x,z,28))return true;
|
||||
for(var b:Basins180.sites(s))if(Math.hypot(x-b.x(),z-b.z())<78)return true;
|
||||
for(var p:NaturalWater184.plan(s).pools())if(Math.abs(x-p.seed().getX())<68&&Math.abs(z-p.seed().getZ())<68)return true;
|
||||
for(var p:SurfaceWater187.plan(s).pools())if(Math.abs(x-p.seed().getX())<80&&Math.abs(z-p.seed().getZ())<80)return true;
|
||||
return false;
|
||||
}
|
||||
private static Candidate candidate(AdventureBiomes180 s,int x,int y,int z,Kind kind,long seed){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+3,z))return null;
|
||||
// Require existing material beneath the entire centre, including the small pedestal.
|
||||
for(int dx=-3;dx<=3;dx+=3)for(int dz=-3;dz<=3;dz+=3)
|
||||
if(!rock(s,x+dx,y-3,z+dz)||!rock(s,x+dx,y-5,z+dz))return null;
|
||||
int[][] inward={{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
|
||||
int sector=Rosette183.sector(x,z),ex=inward[sector][0],ez=inward[sector][1],best=0;
|
||||
// The moss approach is inward. The opposite gate looks out along the sector's exact compass axis.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
for(int side=-1;side<=1;side++){
|
||||
int px=x+ex*step-ez*(bend+side),pz=z+ez*step+ex*(bend+side);
|
||||
if(!rock(s,px,y-3,pz))return null;
|
||||
}
|
||||
if(step>0&&!rock(s,x+ex*step,y+1,z+ez*step)&&!rock(s,x+ex*step,y+2,z+ez*step))best++;
|
||||
}
|
||||
if(best<7||rock(s,x+ex*11,y+1,z+ez*11)||rock(s,x+ex*11,y+2,z+ez*11))return null;
|
||||
for(int side:new int[]{-1,1})if(!rock(s,x-ex*4-ez*side*3,y-3,z-ez*4+ex*side*3))return null;
|
||||
int air=0,roof=0;
|
||||
for(int dx=-6;dx<=6;dx+=3)for(int dz=-6;dz<=6;dz+=3){
|
||||
if(!rock(s,x+dx,y+3,z+dz))air++;
|
||||
if(rock(s,x+dx,y+12,z+dz)||rock(s,x+dx,y+24,z+dz))roof++;
|
||||
}
|
||||
if(kind==Kind.CAVE&&roof<8)return null;
|
||||
double jitter=(mix(seed^((long)x*73428767)^((long)y*912931)^z)>>>11)*0x1.0p-53;
|
||||
return new Candidate(new BlockPos(x,y,z),kind,ex,ez,air*.1+best*.15+jitter*9);
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s,long seed){
|
||||
long started=System.nanoTime();var candidates=new ArrayList<Candidate>();
|
||||
// Sparse fixed grid, finite vertical scan. No terrain regions, raycasts or runtime search.
|
||||
for(int x=-300;x<=300;x+=12)for(int z=-300;z<=300;z+=12){
|
||||
if(Math.hypot(x,z)>310||reserved(s,x,z))continue;
|
||||
int top=s.field().surface(x,z);if(top<140)continue;
|
||||
var surface=candidate(s,x,top,z,Kind.SURFACE,seed);if(surface!=null)candidates.add(surface);
|
||||
for(int y=Math.min(top-18,236);y>=92;y--){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+4,z))continue;
|
||||
var cave=candidate(s,x,y,z,Kind.CAVE,seed);if(cave!=null)candidates.add(cave);
|
||||
}
|
||||
}
|
||||
for(var reef:s.field().base().reefs()){
|
||||
if(reef.length()<30)continue;
|
||||
for(int dx=-12;dx<=12;dx+=6)for(int dz=-12;dz<=12;dz+=6){
|
||||
int x=reef.x()+dx,z=reef.z()+dz;
|
||||
for(int y=reef.y()+reef.rise()+2;y>=reef.y()-5;y--){
|
||||
if(!rock(s,x,y,z))continue;
|
||||
var sky=candidate(s,x,y,z,Kind.SKY,seed);if(sky!=null)candidates.add(sky);break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var chosen=new ArrayList<Site>();int skyCount=0;
|
||||
// Two eligible aerial sectors are selected by seed; other sectors alternate cave and surface preference.
|
||||
var aerial=new ArrayList<Integer>();
|
||||
for(int sector=0;sector<8;sector++){final int k=sector;if(candidates.stream().anyMatch(c->c.kind==Kind.SKY&&Rosette183.sector(c.floor.getX(),c.floor.getZ())==k))aerial.add(k);}
|
||||
aerial.sort(Comparator.comparingLong(k->mix(seed^k*0x186L)));
|
||||
var skySectors=new HashSet<>(aerial.subList(0,Math.min(2,aerial.size())));
|
||||
for(int sector=0;sector<8;sector++){
|
||||
final int k=sector;Kind preferred=skySectors.contains(k)?Kind.SKY:((sector+(int)(seed&1))%2==0?Kind.CAVE:Kind.SURFACE);
|
||||
var ranked=candidates.stream().filter(c->Rosette183.sector(c.floor.getX(),c.floor.getZ())==k)
|
||||
.sorted(Comparator.<Candidate>comparingDouble(c->c.score+(c.kind==preferred?100:0)).reversed()).toList();
|
||||
Candidate selected=null;
|
||||
for(var c:ranked){
|
||||
if(c.kind==Kind.SKY&&!skySectors.contains(k))continue;
|
||||
if(chosen.stream().anyMatch(v->v.floor.distSqr(c.floor)<44*44))continue;
|
||||
selected=c;break;
|
||||
}
|
||||
if(selected==null)throw new IllegalStateException("No naturally supported anchor in sector "+k+" for seed "+seed);
|
||||
var c=selected;long grain=mix(seed^k*7919L);
|
||||
chosen.add(new Site(k,c.floor,c.kind,c.dx,c.dz,6+(int)(grain&1),5+(int)((grain>>>1)&1),(grain&4)==0));
|
||||
if(c.kind==Kind.SKY)skyCount++;
|
||||
}
|
||||
if(skyCount==0)throw new IllegalStateException("No supported aerial anchor for seed "+seed);
|
||||
var chunks=new HashMap<ChunkPos,Map<BlockPos,BlockState>>();
|
||||
for(var site:chosen)for(var e:sculpt(s,site,seed).entrySet())
|
||||
chunks.computeIfAbsent(new ChunkPos(e.getKey().getX()>>4,e.getKey().getZ()>>4),key->new LinkedHashMap<>()).put(e.getKey(),e.getValue());
|
||||
chunks.replaceAll((k,v)->Map.copyOf(v));
|
||||
return new Plan(List.copyOf(chosen),Map.copyOf(chunks),(System.nanoTime()-started)/1_000_000);
|
||||
}
|
||||
private static void put(Map<BlockPos,BlockState> m,BlockPos p,Block b){m.put(p,b.defaultBlockState());}
|
||||
private static Map<BlockPos,BlockState> sculpt(AdventureBiomes180 s,Site site,long seed){
|
||||
var m=new LinkedHashMap<BlockPos,BlockState>();var o=site.floor;int y=o.getY();
|
||||
Block stone=site.kind==Kind.SKY?Blocks.CALCITE:site.kind==Kind.CAVE?Blocks.TUFF:Blocks.ANDESITE;
|
||||
double phase=(mix(seed^site.sector) & 1023)/163.0;
|
||||
for(int dx=-site.rx-2;dx<=site.rx+2;dx++)for(int dz=-site.rz-2;dz<=site.rz+2;dz++){
|
||||
int x=o.getX()+dx,z=o.getZ()+dz;
|
||||
double d=dx*dx/(double)(site.rx*site.rx)+dz*dz/(double)(site.rz*site.rz)+.12*Math.sin(dx*.7+phase)*Math.cos(dz*.6);
|
||||
if(d>1.15)continue;
|
||||
int floor=y+(Math.abs(dx)<=3&&Math.abs(dz)<=3?0:(int)Math.round(.8*Math.sin(dx*.5+phase)*Math.cos(dz*.5)));
|
||||
if(!rock(s,x,floor-3,z))continue; // Keep the edge of the host landform instead of hanging a platform over void.
|
||||
var at=new BlockPos(x,floor,z);
|
||||
if(d<=1){
|
||||
for(int h=-2;h<=0;h++)put(m,at.above(h),h==0&&Math.floorMod(x+z+site.sector,5)==0?Blocks.MOSS_BLOCK:stone);
|
||||
int height=3+(int)(4*Math.sqrt(Math.max(0,1-d)));
|
||||
for(int h=1;h<=height;h++)put(m,at.above(h),Blocks.AIR);
|
||||
if(d>.62&&Math.floorMod(x*7+z*13,11)==0){
|
||||
put(m,at,Blocks.MOSS_BLOCK);put(m,at.above(),Blocks.FLOWERING_AZALEA);
|
||||
}
|
||||
}else {
|
||||
// Short reliefs follow surviving rock faces; no closed constructed shell.
|
||||
for(int h=0;h<=5;h++)if(rock(s,x,floor+h,z))
|
||||
put(m,at.above(h),h==2&&Math.floorMod(x+z,3)==0?Blocks.CHISELED_TUFF:h==0?Blocks.MOSS_BLOCK:stone);
|
||||
}
|
||||
}
|
||||
// A slight bend opens to an actual, dry natural floor found during selection.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int x=o.getX()+site.dx*step-site.dz*bend,z=o.getZ()+site.dz*step+site.dx*bend;
|
||||
for(int side=-1;side<=1;side++){
|
||||
var p=new BlockPos(x-site.dz*side,y,z+site.dx*side);
|
||||
for(int h=-2;h<=0;h++)put(m,p.above(h),h==0&&step>=2?Blocks.MOSS_BLOCK:stone);
|
||||
for(int h=1;h<=4;h++)put(m,p.above(h),Blocks.AIR);
|
||||
}
|
||||
}
|
||||
// Low eroded crests shelter surface sites; all are rooted in the existing mass.
|
||||
for(int side:new int[]{-1,1}){
|
||||
int dx=-site.dx*4-site.dz*side*3,dz=-site.dz*4+site.dx*side*3;
|
||||
var base=o.offset(dx,0,dz);if(!rock(s,base.getX(),y-3,base.getZ()))continue;
|
||||
for(int h=-2;h<=2;h++)put(m,base.above(h),h==1?Blocks.CHISELED_TUFF:stone);
|
||||
if(site.bright)put(m,base,Blocks.SEA_LANTERN);
|
||||
put(m,base.above(3),Blocks.AMETHYST_BLOCK);
|
||||
put(m,base.above(4),Rosette183.GLASS[site.sector]);
|
||||
}
|
||||
for(int dx=-1;dx<=1;dx++)for(int dz=-1;dz<=1;dz++){
|
||||
put(m,o.offset(dx,0,dz),Blocks.CHISELED_TUFF);
|
||||
for(int h=1;h<=4;h++)put(m,o.offset(dx,h,dz),Blocks.AIR);
|
||||
}
|
||||
put(m,o.above(),Rosette183.GLASS[site.sector]);
|
||||
m.put(site.anchor(),ExpansionAnchorBlock.BLOCK.defaultBlockState().setValue(ExpansionAnchorBlock.SECTOR,site.sector));
|
||||
return m;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator g,ChunkAccess chunk){
|
||||
if(!WorldgenLab.natural187(g))return;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(chunk.getPos());
|
||||
if(entries!=null)entries.forEach(chunk::setBlockState);
|
||||
}
|
||||
public static boolean protects(WorldGenLevel level,ChunkGenerator g,BlockPos pos){
|
||||
if(!WorldgenLab.natural187(g)||pos.getY()<88||pos.getY()>512||Math.abs(pos.getX())>325||Math.abs(pos.getZ())>325)return false;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(new ChunkPos(pos.getX()>>4,pos.getZ()>>4));
|
||||
return entries!=null&&entries.containsKey(pos);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 source){
|
||||
var p=plan(source,level.getSeed());var again=create(source,level.getSeed());
|
||||
if(!p.sites.equals(again.sites)||!p.chunks.equals(again.chunks))throw new IllegalStateException("Unstable anchor plan");
|
||||
int count=0;var details=new ArrayList<Map<String,Object>>();
|
||||
for(var cp:p.chunks.keySet())level.getChunk(cp.x(),cp.z());
|
||||
for(var e:p.chunks.entrySet())for(var b:e.getValue().entrySet()){
|
||||
if(!e.getKey().contains(b.getKey()))throw new IllegalStateException("Cross-chunk anchor write");
|
||||
if(!level.getBlockState(b.getKey()).equals(b.getValue()))throw new IllegalStateException("Anchor refuge mismatch "+b.getKey()+" expected "+b.getValue()+" got "+level.getBlockState(b.getKey()));
|
||||
if(b.getValue().is(ExpansionAnchorBlock.BLOCK))count++;
|
||||
}
|
||||
for(var site:p.sites){
|
||||
if(Rosette183.sector(site.floor.getX(),site.floor.getZ())!=site.sector)throw new IllegalStateException("Wrong colour sector");
|
||||
// Walk the full three-wide bent approach and verify its actual native support.
|
||||
for(int step=-6;step<=11;step++){
|
||||
if(step>=-1&&step<=1)continue;
|
||||
int bend=step>3&&step<9?1:0;var at=site.floor.offset(site.dx*step-site.dz*bend,1,site.dz*step+site.dx*bend);
|
||||
if(!level.getBlockState(at).isAir()||!level.getBlockState(at.above()).isAir()||level.getBlockState(at.below()).getCollisionShape(level,at.below()).isEmpty())throw new IllegalStateException("Anchor approach blocked "+at);
|
||||
}
|
||||
if(!rock(source,site.floor.getX(),site.floor.getY()-5,site.floor.getZ()))throw new IllegalStateException("Anchor above void");
|
||||
if(site.dx*site.floor.getX()+site.dz*site.floor.getZ()>=0)throw new IllegalStateException("Approach points outward");
|
||||
for(int side:new int[]{-1,1}){
|
||||
var pylon=site.floor.offset(-site.dx*4-site.dz*side*3,0,-site.dz*4+site.dx*side*3);
|
||||
if(!level.getBlockState(pylon.above(3)).is(Blocks.AMETHYST_BLOCK)||!level.getBlockState(pylon.above(4)).is(Rosette183.GLASS[site.sector]))throw new IllegalStateException("Pylon glass must sit on amethyst");
|
||||
}
|
||||
details.add(Map.of("sector",site.sector,"kind",site.kind.name(),"anchor",site.anchor().toShortString(),"entrance",site.entrance().toShortString(),"bright",site.bright));
|
||||
}
|
||||
if(count!=8||p.sites.stream().map(Site::sector).distinct().count()!=8)throw new IllegalStateException("Expected exactly eight anchors");
|
||||
var offers=AnchorOffers187.check(level,p.sites);
|
||||
return Map.of("sites",details,"planningMs",p.millis,"chunks",p.chunks.size(),"blocks",p.chunks.values().stream().mapToInt(Map::size).sum(),"deterministic",true,"offers",offers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.ExpansionAnchorBlock;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
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.densityfunction.SamplerContext;
|
||||
|
||||
/** Eight seed-owned refuges. Planning reads density only; placement never loads a neighbour chunk. */
|
||||
public final class Anchors188 {
|
||||
enum Kind { SURFACE, CAVE, SKY }
|
||||
record Site(int sector,BlockPos floor,Kind kind,int dx,int dz,int rx,int rz,boolean bright) {
|
||||
BlockPos anchor(){return floor.above(2);}
|
||||
BlockPos entrance(){return floor.offset(dx*11,1,dz*11);}
|
||||
}
|
||||
record Plan(List<Site> sites,Map<ChunkPos,Map<BlockPos,BlockState>> chunks,long millis) {}
|
||||
private record Candidate(BlockPos floor,Kind kind,int dx,int dz,double score) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Anchors188() {}
|
||||
static AdventureBiomes180 source(ChunkGenerator g){return (AdventureBiomes180)((NoiseBasedChunkGenerator)g).getBiomeSource();}
|
||||
static Plan plan(AdventureBiomes180 s,long seed){synchronized(PLANS){return PLANS.computeIfAbsent(s,key->create(s,seed));}}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
private static long mix(long n){n=(n^(n>>>30))*0xbf58476d1ce4e5b9L;n=(n^(n>>>27))*0x94d049bb133111ebL;return n^(n>>>31);}
|
||||
private static boolean reserved(AdventureBiomes180 s,int x,int z){
|
||||
if(Math.hypot(x,z)<88||Dungeon180.near(s,x,z,28))return true;
|
||||
for(var b:Basins180.sites(s))if(Math.hypot(x-b.x(),z-b.z())<78)return true;
|
||||
for(var p:NaturalWater184.plan(s).pools())if(Math.abs(x-p.seed().getX())<68&&Math.abs(z-p.seed().getZ())<68)return true;
|
||||
for(var p:SurfaceWater187.plan(s).pools())if(Math.abs(x-p.seed().getX())<80&&Math.abs(z-p.seed().getZ())<80)return true;
|
||||
return false;
|
||||
}
|
||||
private static Candidate candidate(AdventureBiomes180 s,int x,int y,int z,Kind kind,long seed){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+3,z))return null;
|
||||
// Require existing material beneath the entire centre, including the small pedestal.
|
||||
for(int dx=-3;dx<=3;dx+=3)for(int dz=-3;dz<=3;dz+=3)
|
||||
if(!rock(s,x+dx,y-3,z+dz)||!rock(s,x+dx,y-5,z+dz))return null;
|
||||
int[][] inward={{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
|
||||
int sector=Rosette183.sector(x,z),ex=inward[sector][0],ez=inward[sector][1],best=0;
|
||||
// The moss approach is inward. The opposite gate looks out along the sector's exact compass axis.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
for(int side=-1;side<=1;side++){
|
||||
int px=x+ex*step-ez*(bend+side),pz=z+ez*step+ex*(bend+side);
|
||||
if(!rock(s,px,y-3,pz))return null;
|
||||
}
|
||||
if(step>0&&!rock(s,x+ex*step,y+1,z+ez*step)&&!rock(s,x+ex*step,y+2,z+ez*step))best++;
|
||||
}
|
||||
if(best<7||rock(s,x+ex*11,y+1,z+ez*11)||rock(s,x+ex*11,y+2,z+ez*11))return null;
|
||||
for(int side:new int[]{-1,1})if(!rock(s,x-ex*4-ez*side*3,y-3,z-ez*4+ex*side*3))return null;
|
||||
int air=0,roof=0;
|
||||
for(int dx=-6;dx<=6;dx+=3)for(int dz=-6;dz<=6;dz+=3){
|
||||
if(!rock(s,x+dx,y+3,z+dz))air++;
|
||||
if(rock(s,x+dx,y+12,z+dz)||rock(s,x+dx,y+24,z+dz))roof++;
|
||||
}
|
||||
if(kind==Kind.CAVE&&roof<8)return null;
|
||||
double jitter=(mix(seed^((long)x*73428767)^((long)y*912931)^z)>>>11)*0x1.0p-53;
|
||||
return new Candidate(new BlockPos(x,y,z),kind,ex,ez,air*.1+best*.15+jitter*9);
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s,long seed){
|
||||
long started=System.nanoTime();var candidates=new ArrayList<Candidate>();
|
||||
// Sparse fixed grid, finite vertical scan. No terrain regions, raycasts or runtime search.
|
||||
for(int x=-300;x<=300;x+=12)for(int z=-300;z<=300;z+=12){
|
||||
if(Math.hypot(x,z)>310||reserved(s,x,z))continue;
|
||||
int top=s.field().surface(x,z);if(top<140)continue;
|
||||
var surface=candidate(s,x,top,z,Kind.SURFACE,seed);if(surface!=null)candidates.add(surface);
|
||||
for(int y=Math.min(top-18,236);y>=92;y--){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+4,z))continue;
|
||||
var cave=candidate(s,x,y,z,Kind.CAVE,seed);if(cave!=null)candidates.add(cave);
|
||||
}
|
||||
}
|
||||
for(var reef:s.field().base().reefs()){
|
||||
if(reef.length()<30)continue;
|
||||
for(int dx=-12;dx<=12;dx+=6)for(int dz=-12;dz<=12;dz+=6){
|
||||
int x=reef.x()+dx,z=reef.z()+dz;
|
||||
for(int y=reef.y()+reef.rise()+2;y>=reef.y()-5;y--){
|
||||
if(!rock(s,x,y,z))continue;
|
||||
var sky=candidate(s,x,y,z,Kind.SKY,seed);if(sky!=null)candidates.add(sky);break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var chosen=new ArrayList<Site>();int skyCount=0;
|
||||
// Two eligible aerial sectors are selected by seed; other sectors alternate cave and surface preference.
|
||||
var aerial=new ArrayList<Integer>();
|
||||
for(int sector=0;sector<8;sector++){final int k=sector;if(candidates.stream().anyMatch(c->c.kind==Kind.SKY&&Rosette183.sector(c.floor.getX(),c.floor.getZ())==k))aerial.add(k);}
|
||||
aerial.sort(Comparator.comparingLong(k->mix(seed^k*0x186L)));
|
||||
var skySectors=new HashSet<>(aerial.subList(0,Math.min(2,aerial.size())));
|
||||
for(int sector=0;sector<8;sector++){
|
||||
final int k=sector;Kind preferred=skySectors.contains(k)?Kind.SKY:((sector+(int)(seed&1))%2==0?Kind.CAVE:Kind.SURFACE);
|
||||
var ranked=candidates.stream().filter(c->Rosette183.sector(c.floor.getX(),c.floor.getZ())==k)
|
||||
.sorted(Comparator.<Candidate>comparingDouble(c->c.score+(c.kind==preferred?100:0)).reversed()).toList();
|
||||
Candidate selected=null;
|
||||
for(var c:ranked){
|
||||
if(c.kind==Kind.SKY&&!skySectors.contains(k))continue;
|
||||
if(chosen.stream().anyMatch(v->v.floor.distSqr(c.floor)<44*44))continue;
|
||||
selected=c;break;
|
||||
}
|
||||
if(selected==null)throw new IllegalStateException("No naturally supported anchor in sector "+k+" for seed "+seed);
|
||||
var c=selected;long grain=mix(seed^k*7919L);
|
||||
chosen.add(new Site(k,c.floor,c.kind,c.dx,c.dz,6+(int)(grain&1),5+(int)((grain>>>1)&1),(grain&4)==0));
|
||||
if(c.kind==Kind.SKY)skyCount++;
|
||||
}
|
||||
if(skyCount==0)throw new IllegalStateException("No supported aerial anchor for seed "+seed);
|
||||
var chunks=new HashMap<ChunkPos,Map<BlockPos,BlockState>>();
|
||||
for(var site:chosen)for(var e:sculpt(s,site,seed).entrySet())
|
||||
chunks.computeIfAbsent(new ChunkPos(e.getKey().getX()>>4,e.getKey().getZ()>>4),key->new LinkedHashMap<>()).put(e.getKey(),e.getValue());
|
||||
chunks.replaceAll((k,v)->Map.copyOf(v));
|
||||
return new Plan(List.copyOf(chosen),Map.copyOf(chunks),(System.nanoTime()-started)/1_000_000);
|
||||
}
|
||||
private static void put(Map<BlockPos,BlockState> m,BlockPos p,Block b){m.put(p,b.defaultBlockState());}
|
||||
private static Map<BlockPos,BlockState> sculpt(AdventureBiomes180 s,Site site,long seed){
|
||||
var m=new LinkedHashMap<BlockPos,BlockState>();var o=site.floor;int y=o.getY();
|
||||
Block stone=site.kind==Kind.SKY?Blocks.CALCITE:site.kind==Kind.CAVE?Blocks.TUFF:Blocks.ANDESITE;
|
||||
Block accent=switch(site.sector){case 0,7->Blocks.CALCITE;case 1,2->Blocks.MOSS_BLOCK;case 3->Blocks.TERRACOTTA;case 4->Blocks.DYED_TERRACOTTA.red();case 5->Blocks.AMETHYST_BLOCK;default->Blocks.DYED_TERRACOTTA.yellow();};
|
||||
double phase=(mix(seed^site.sector) & 1023)/163.0;
|
||||
for(int dx=-site.rx-2;dx<=site.rx+2;dx++)for(int dz=-site.rz-2;dz<=site.rz+2;dz++){
|
||||
int x=o.getX()+dx,z=o.getZ()+dz;
|
||||
double d=dx*dx/(double)(site.rx*site.rx)+dz*dz/(double)(site.rz*site.rz)+.12*Math.sin(dx*.7+phase)*Math.cos(dz*.6);
|
||||
if(d>1.15)continue;
|
||||
int floor=y+(Math.abs(dx)<=3&&Math.abs(dz)<=3?0:(int)Math.round(.8*Math.sin(dx*.5+phase)*Math.cos(dz*.5)));
|
||||
if(!rock(s,x,floor-3,z))continue; // Keep the edge of the host landform instead of hanging a platform over void.
|
||||
var at=new BlockPos(x,floor,z);
|
||||
if(d<=1){
|
||||
for(int h=-2;h<=0;h++)put(m,at.above(h),h==0&&Math.floorMod(x+z+site.sector,5)==0?Blocks.MOSS_BLOCK:stone);
|
||||
int height=3+(int)(4*Math.sqrt(Math.max(0,1-d)));
|
||||
for(int h=1;h<=height;h++)put(m,at.above(h),Blocks.AIR);
|
||||
if(d>.62&&Math.floorMod(x*7+z*13,11)==0){
|
||||
put(m,at,Blocks.MOSS_BLOCK);put(m,at.above(),Blocks.FLOWERING_AZALEA);
|
||||
}
|
||||
}else {
|
||||
// Short reliefs follow surviving rock faces; no closed constructed shell.
|
||||
for(int h=0;h<=5;h++)if(rock(s,x,floor+h,z))
|
||||
put(m,at.above(h),h==2&&Math.floorMod(x+z,3)==0?Blocks.CHISELED_TUFF:h==0?Blocks.MOSS_BLOCK:stone);
|
||||
}
|
||||
}
|
||||
// A slight bend opens to an actual, dry natural floor found during selection.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int x=o.getX()+site.dx*step-site.dz*bend,z=o.getZ()+site.dz*step+site.dx*bend;
|
||||
for(int side=-1;side<=1;side++){
|
||||
var p=new BlockPos(x-site.dz*side,y,z+site.dx*side);
|
||||
for(int h=-2;h<=0;h++)put(m,p.above(h),h==0&&step>=2?Blocks.MOSS_BLOCK:stone);
|
||||
for(int h=1;h<=4;h++)put(m,p.above(h),Blocks.AIR);
|
||||
}
|
||||
}
|
||||
// Low eroded crests shelter surface sites; all are rooted in the existing mass.
|
||||
for(int side:new int[]{-1,1}){
|
||||
int dx=-site.dx*4-site.dz*side*3,dz=-site.dz*4+site.dx*side*3;
|
||||
var base=o.offset(dx,0,dz);if(!rock(s,base.getX(),y-3,base.getZ()))continue;
|
||||
for(int h=-2;h<=2;h++)put(m,base.above(h),h==1?Blocks.CHISELED_TUFF:stone);
|
||||
if(site.bright)put(m,base,Blocks.SEA_LANTERN);
|
||||
put(m,base.above(3),Blocks.AMETHYST_BLOCK);
|
||||
put(m,base.above(4),Rosette183.GLASS[site.sector]);
|
||||
}
|
||||
for(int dx=-1;dx<=1;dx++)for(int dz=-1;dz<=1;dz++){
|
||||
put(m,o.offset(dx,0,dz),Blocks.CHISELED_TUFF);
|
||||
for(int h=1;h<=4;h++)put(m,o.offset(dx,h,dz),Blocks.AIR);
|
||||
}
|
||||
put(m,o.above(),Rosette183.GLASS[site.sector]);
|
||||
m.put(site.anchor(),ExpansionAnchorBlock.BLOCK.defaultBlockState().setValue(ExpansionAnchorBlock.SECTOR,site.sector));
|
||||
// Keep the accepted geometry; the host material and sector colour shape each refuge's palette.
|
||||
m.replaceAll((p,b)->{
|
||||
int h=p.getY()-y;
|
||||
double patch=EcologyBiomes176.noise(seed^0x188AL,p.getX()/5.0,p.getZ()/5.0);
|
||||
if(b.is(stone)){
|
||||
if(site.kind==Kind.SURFACE&&h<=0)return (h<0?Blocks.DIRT:patch>.3?Blocks.COARSE_DIRT:patch<-.3?Blocks.MOSS_BLOCK:Blocks.GRASS_BLOCK).defaultBlockState();
|
||||
if(site.kind==Kind.CAVE)return (patch>.30?Blocks.DEEPSLATE:patch<-.35?Blocks.CALCITE:Blocks.TUFF).defaultBlockState();
|
||||
if(site.kind==Kind.SKY&&patch>.25)return Blocks.DIORITE.defaultBlockState();
|
||||
}
|
||||
if(b.is(Blocks.CHISELED_TUFF)&&patch>-.2)return accent.defaultBlockState();
|
||||
return b;
|
||||
});
|
||||
return m;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator g,ChunkAccess chunk){
|
||||
if(!WorldgenLab.details188(g))return;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(chunk.getPos());
|
||||
if(entries!=null)entries.forEach(chunk::setBlockState);
|
||||
}
|
||||
public static boolean protects(WorldGenLevel level,ChunkGenerator g,BlockPos pos){
|
||||
if(!WorldgenLab.details188(g)||pos.getY()<88||pos.getY()>512||Math.abs(pos.getX())>325||Math.abs(pos.getZ())>325)return false;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(new ChunkPos(pos.getX()>>4,pos.getZ()>>4));
|
||||
return entries!=null&&entries.containsKey(pos);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 source){
|
||||
var p=plan(source,level.getSeed());var again=create(source,level.getSeed());
|
||||
if(!p.sites.equals(again.sites)||!p.chunks.equals(again.chunks))throw new IllegalStateException("Unstable anchor plan");
|
||||
int count=0;var details=new ArrayList<Map<String,Object>>();
|
||||
for(var cp:p.chunks.keySet())level.getChunk(cp.x(),cp.z());
|
||||
for(var e:p.chunks.entrySet())for(var b:e.getValue().entrySet()){
|
||||
if(!e.getKey().contains(b.getKey()))throw new IllegalStateException("Cross-chunk anchor write");
|
||||
if(!level.getBlockState(b.getKey()).equals(b.getValue()))throw new IllegalStateException("Anchor refuge mismatch "+b.getKey()+" expected "+b.getValue()+" got "+level.getBlockState(b.getKey()));
|
||||
if(b.getValue().is(ExpansionAnchorBlock.BLOCK))count++;
|
||||
}
|
||||
for(var site:p.sites){
|
||||
if(Rosette183.sector(site.floor.getX(),site.floor.getZ())!=site.sector)throw new IllegalStateException("Wrong colour sector");
|
||||
// Walk the full three-wide bent approach and verify its actual native support.
|
||||
for(int step=-6;step<=11;step++){
|
||||
if(step>=-1&&step<=1)continue;
|
||||
int bend=step>3&&step<9?1:0;var at=site.floor.offset(site.dx*step-site.dz*bend,1,site.dz*step+site.dx*bend);
|
||||
if(!level.getBlockState(at).isAir()||!level.getBlockState(at.above()).isAir()||level.getBlockState(at.below()).getCollisionShape(level,at.below()).isEmpty())throw new IllegalStateException("Anchor approach blocked "+at);
|
||||
}
|
||||
if(!rock(source,site.floor.getX(),site.floor.getY()-5,site.floor.getZ()))throw new IllegalStateException("Anchor above void");
|
||||
if(site.dx*site.floor.getX()+site.dz*site.floor.getZ()>=0)throw new IllegalStateException("Approach points outward");
|
||||
for(int side:new int[]{-1,1}){
|
||||
var pylon=site.floor.offset(-site.dx*4-site.dz*side*3,0,-site.dz*4+site.dx*side*3);
|
||||
if(!level.getBlockState(pylon.above(3)).is(Blocks.AMETHYST_BLOCK)||!level.getBlockState(pylon.above(4)).is(Rosette183.GLASS[site.sector]))throw new IllegalStateException("Pylon glass must sit on amethyst");
|
||||
}
|
||||
details.add(Map.of("sector",site.sector,"kind",site.kind.name(),"anchor",site.anchor().toShortString(),"entrance",site.entrance().toShortString(),"bright",site.bright));
|
||||
}
|
||||
if(count!=8||p.sites.stream().map(Site::sector).distinct().count()!=8)throw new IllegalStateException("Expected exactly eight anchors");
|
||||
var offers=AnchorOffers188.check(level,p.sites);
|
||||
return Map.of("sites",details,"planningMs",p.millis,"chunks",p.chunks.size(),"blocks",p.chunks.values().stream().mapToInt(Map::size).sum(),"deterministic",true,"offers",offers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.palace.ExpansionAnchorBlock;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.*;
|
||||
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.densityfunction.SamplerContext;
|
||||
|
||||
/** Eight seed-owned refuges. Planning reads density only; placement never loads a neighbour chunk. */
|
||||
public final class Anchors189 {
|
||||
enum Kind { SURFACE, CAVE, SKY }
|
||||
record Site(int sector,BlockPos floor,Kind kind,int dx,int dz,int rx,int rz,boolean bright) {
|
||||
BlockPos anchor(){return floor.above(2);}
|
||||
BlockPos entrance(){return floor.offset(dx*11,1,dz*11);}
|
||||
}
|
||||
record Plan(List<Site> sites,Map<ChunkPos,Map<BlockPos,BlockState>> chunks,long millis) {}
|
||||
private record Candidate(BlockPos floor,Kind kind,int dx,int dz,double score) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Anchors189() {}
|
||||
static AdventureBiomes180 source(ChunkGenerator g){return (AdventureBiomes180)((NoiseBasedChunkGenerator)g).getBiomeSource();}
|
||||
static Plan plan(AdventureBiomes180 s,long seed){synchronized(PLANS){return PLANS.computeIfAbsent(s,key->create(s,seed));}}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
private static long mix(long n){n=(n^(n>>>30))*0xbf58476d1ce4e5b9L;n=(n^(n>>>27))*0x94d049bb133111ebL;return n^(n>>>31);}
|
||||
private static boolean reserved(AdventureBiomes180 s,int x,int z){
|
||||
if(Math.hypot(x,z)<88||Dungeon180.near(s,x,z,28))return true;
|
||||
for(var b:Basins180.sites(s))if(Math.hypot(x-b.x(),z-b.z())<78)return true;
|
||||
for(var p:NaturalWater184.plan(s).pools())if(Math.abs(x-p.seed().getX())<68&&Math.abs(z-p.seed().getZ())<68)return true;
|
||||
for(var p:SurfaceWater187.plan(s).pools())if(Math.abs(x-p.seed().getX())<80&&Math.abs(z-p.seed().getZ())<80)return true;
|
||||
return false;
|
||||
}
|
||||
private static Candidate candidate(AdventureBiomes180 s,int x,int y,int z,Kind kind,long seed){return candidate(s,x,y,z,kind,seed,false);}
|
||||
private static Candidate candidate(AdventureBiomes180 s,int x,int y,int z,Kind kind,long seed,boolean precise){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+3,z))return null;
|
||||
if(s.field().base().capacity()!=fr.koka.sanctuary.worldgen.IslandCapacity.TEN&&(precise?reserved199(s,x,y,z):reservedAt(s,x,y,z)))return null;
|
||||
// Require existing material beneath the entire centre, including the small pedestal.
|
||||
for(int dx=-3;dx<=3;dx+=3)for(int dz=-3;dz<=3;dz+=3)
|
||||
if(!rock(s,x+dx,y-3,z+dz)||!rock(s,x+dx,y-5,z+dz))return null;
|
||||
int[][] inward={{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1}};
|
||||
int sector=Rosette183.sector(x,z),ex=inward[sector][0],ez=inward[sector][1],best=0;
|
||||
// The moss approach is inward. The opposite gate looks out along the sector's exact compass axis.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
for(int side=-1;side<=1;side++){
|
||||
int px=x+ex*step-ez*(bend+side),pz=z+ez*step+ex*(bend+side);
|
||||
if(!rock(s,px,y-3,pz))return null;
|
||||
}
|
||||
if(step>0&&!rock(s,x+ex*step,y+1,z+ez*step)&&!rock(s,x+ex*step,y+2,z+ez*step))best++;
|
||||
}
|
||||
if(s.field().coherent()&&kind!=Kind.CAVE){
|
||||
int previous=y;
|
||||
for(int step=0;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0,px=x+ex*step-ez*bend,pz=z+ez*step+ex*bend;
|
||||
int gy=step<=2?y:ground(s,px,pz,y);
|
||||
if(!rock(s,px,gy,pz)||Math.abs(gy-previous)>1)return null;
|
||||
for(int side=-1;side<=1;side++)if(Math.abs(ground(s,px-ez*side,pz+ex*side,y)-gy)>1)return null;
|
||||
previous=gy;
|
||||
}
|
||||
}
|
||||
if(best<7||rock(s,x+ex*11,y+1,z+ez*11)||rock(s,x+ex*11,y+2,z+ez*11))return null;
|
||||
for(int side:new int[]{-1,1})if(!rock(s,x-ex*4-ez*side*3,y-3,z-ez*4+ex*side*3))return null;
|
||||
int air=0,roof=0;
|
||||
for(int dx=-6;dx<=6;dx+=3)for(int dz=-6;dz<=6;dz+=3){
|
||||
if(!rock(s,x+dx,y+3,z+dz))air++;
|
||||
if(rock(s,x+dx,y+12,z+dz)||rock(s,x+dx,y+24,z+dz))roof++;
|
||||
}
|
||||
if(kind==Kind.CAVE&&roof<8)return null;
|
||||
double jitter=(mix(seed^((long)x*73428767)^((long)y*912931)^z)>>>11)*0x1.0p-53;
|
||||
return new Candidate(new BlockPos(x,y,z),kind,ex,ez,air*.1+best*.15+jitter*9);
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s,long seed){
|
||||
long started=System.nanoTime();var candidates=new ArrayList<Candidate>();
|
||||
// Sparse fixed grid, finite vertical scan. No terrain regions, raycasts or runtime search.
|
||||
double spread=s.field().base().spread();int step=(int)Math.round(12*spread),limit=25*step;
|
||||
for(int x=-limit;x<=limit;x+=step)for(int z=-limit;z<=limit;z+=step){
|
||||
if(Math.hypot(x,z)>310*spread||Math.hypot(x,z)<88
|
||||
||s.field().base().capacity()==fr.koka.sanctuary.worldgen.IslandCapacity.TEN&&reserved(s,x,z))continue;
|
||||
int top=s.field().surface(x,z);if(top<140)continue;
|
||||
var surface=candidate(s,x,top,z,Kind.SURFACE,seed);if(surface!=null)candidates.add(surface);
|
||||
for(int y=Math.min(top-18,236);y>=92;y--){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+4,z))continue;
|
||||
var cave=candidate(s,x,y,z,Kind.CAVE,seed);if(cave!=null)candidates.add(cave);
|
||||
}
|
||||
}
|
||||
for(var reef:s.field().base().reefs()){
|
||||
if(reef.length()<30)continue;
|
||||
int extent=s.field().coherent()?20:12,spacing=s.field().coherent()?4:6;
|
||||
for(int dx=-extent;dx<=extent;dx+=spacing)for(int dz=-extent;dz<=extent;dz+=spacing){
|
||||
int x=reef.x()+dx,z=reef.z()+dz;
|
||||
for(int y=reef.y()+reef.rise()+2;y>=reef.y()-5;y--){
|
||||
if(!rock(s,x,y,z))continue;
|
||||
var sky=candidate(s,x,y,z,Kind.SKY,seed);if(sky!=null)candidates.add(sky);break;
|
||||
}
|
||||
}
|
||||
}
|
||||
var chosen=new ArrayList<Site>();int skyCount=0;
|
||||
// Two eligible aerial sectors are selected by seed; other sectors alternate cave and surface preference.
|
||||
var aerial=new ArrayList<Integer>();
|
||||
for(int sector=0;sector<8;sector++){final int k=sector;if(candidates.stream().anyMatch(c->c.kind==Kind.SKY&&Rosette183.sector(c.floor.getX(),c.floor.getZ())==k))aerial.add(k);}
|
||||
aerial.sort(Comparator.comparingLong(k->mix(seed^k*0x186L)));
|
||||
var skySectors=new HashSet<>(aerial.subList(0,Math.min(2,aerial.size())));
|
||||
for(int sector=0;sector<8;sector++){
|
||||
final int k=sector;Kind preferred=skySectors.contains(k)?Kind.SKY:((sector+(int)(seed&1))%2==0?Kind.CAVE:Kind.SURFACE);
|
||||
var ranked=candidates.stream().filter(c->Rosette183.sector(c.floor.getX(),c.floor.getZ())==k)
|
||||
.sorted(Comparator.<Candidate>comparingDouble(c->c.score+(c.kind==preferred?100:0)).reversed()).toList();
|
||||
Candidate selected=null;
|
||||
for(var c:ranked){
|
||||
if(c.kind==Kind.SKY&&!skySectors.contains(k))continue;
|
||||
if(chosen.stream().anyMatch(v->v.floor.distSqr(c.floor)<44*44))continue;
|
||||
selected=c;break;
|
||||
}
|
||||
if(selected==null&&s.expansions!=null)selected=denseCandidate199(s,seed,k,chosen);
|
||||
if(selected==null)throw new IllegalStateException("No naturally supported anchor in sector "+k+" for seed "+seed);
|
||||
var c=selected;long grain=mix(seed^k*7919L);
|
||||
chosen.add(new Site(k,c.floor,c.kind,c.dx,c.dz,6+(int)(grain&1),5+(int)((grain>>>1)&1),(grain&4)==0));
|
||||
if(c.kind==Kind.SKY)skyCount++;
|
||||
}
|
||||
if(skyCount==0)throw new IllegalStateException("No supported aerial anchor for seed "+seed);
|
||||
var chunks=new HashMap<ChunkPos,Map<BlockPos,BlockState>>();
|
||||
for(var site:chosen)for(var e:sculpt(s,site,seed).entrySet())
|
||||
chunks.computeIfAbsent(new ChunkPos(e.getKey().getX()>>4,e.getKey().getZ()>>4),key->new LinkedHashMap<>()).put(e.getKey(),e.getValue());
|
||||
chunks.replaceAll((k,v)->Map.copyOf(v));
|
||||
return new Plan(List.copyOf(chosen),Map.copyOf(chunks),(System.nanoTime()-started)/1_000_000);
|
||||
}
|
||||
/** The coarse historical grid can miss narrow valid floors on Small. New worlds retry only the missing sector. */
|
||||
private static Candidate denseCandidate199(AdventureBiomes180 s,long seed,int sector,List<Site> chosen){
|
||||
int limit=(int)(310*s.field().base().spread());
|
||||
for(int x=-limit;x<=limit;x+=3)for(int z=-limit;z<=limit;z+=3){
|
||||
if(Rosette183.sector(x,z)!=sector||Math.hypot(x,z)<88||Math.hypot(x,z)>limit)continue;
|
||||
int top=s.field().surface(x,z);if(top<140)continue;
|
||||
for(int y=top;y>=64;y--){
|
||||
if(y>top-18&&y!=top)continue;
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+4,z))continue;
|
||||
var at=new BlockPos(x,y,z);if(chosen.stream().anyMatch(v->v.floor.distSqr(at)<44*44))continue;
|
||||
var candidate=candidate(s,x,y,z,y==top?Kind.SURFACE:Kind.CAVE,seed,true);
|
||||
if(candidate!=null)return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static final Map<AdventureBiomes180,List<net.minecraft.world.level.levelgen.structure.BoundingBox>> WATER199=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private static boolean reserved199(AdventureBiomes180 s,int x,int y,int z){
|
||||
var dungeon=Dungeon180.plan(s);
|
||||
for(var room:dungeon.rooms())if(Math.abs(y-room.y())<20&&Math.abs(x-room.x())<room.rx()+18&&Math.abs(z-room.z())<room.rz()+18)return true;
|
||||
for(var cell:dungeon.paths().values())if(Math.abs(y-cell.y())<16&&Math.abs(x-cell.x())<18&&Math.abs(z-cell.z())<18)return true;
|
||||
if(dungeon.hut()!=null&&Math.abs(y-dungeon.hut().getY())<20&&Math.hypot(x-dungeon.hut().getX(),z-dungeon.hut().getZ())<28)return true;
|
||||
for(var b:Basins180.sites(s)){
|
||||
int u=(x-b.x())*b.dx()+(z-b.z())*b.dz(),v=-(x-b.x())*b.dz()+(z-b.z())*b.dx();
|
||||
if(y>b.y()-28&&y<b.y()+14&&u>=-46&&u<=74&&Math.abs(v)<=39)return true;
|
||||
}
|
||||
var boxes=WATER199.computeIfAbsent(s,key->{
|
||||
var out=new ArrayList<net.minecraft.world.level.levelgen.structure.BoundingBox>();
|
||||
var bodies=new ArrayList<Set<BlockPos>>();
|
||||
NaturalWater184.plan(s).pools().forEach(p->bodies.add(p.cells()));SurfaceWater187.plan(s).pools().forEach(p->bodies.add(p.cells()));
|
||||
for(var cells:bodies)if(!cells.isEmpty()){
|
||||
var b=net.minecraft.world.level.levelgen.structure.BoundingBox.encapsulatingPositions(cells).orElseThrow();
|
||||
out.add(new net.minecraft.world.level.levelgen.structure.BoundingBox(b.minX()-18,b.minY()-12,b.minZ()-18,b.maxX()+18,b.maxY()+12,b.maxZ()+18));
|
||||
}
|
||||
return List.copyOf(out);
|
||||
});
|
||||
return boxes.stream().anyMatch(b->b.isInside(x,y,z));
|
||||
}
|
||||
/** Small/Large may stack dry refuges above/below another site, but their volumes must stay separate. */
|
||||
private static boolean reservedAt(AdventureBiomes180 s,int x,int y,int z){
|
||||
var dungeon=Dungeon180.plan(s);
|
||||
for(var room:dungeon.rooms())if(Math.abs(y-room.y())<20&&Math.abs(x-room.x())<room.rx()+18&&Math.abs(z-room.z())<room.rz()+18)return true;
|
||||
for(var cell:dungeon.paths().values())if(Math.abs(y-cell.y())<16&&Math.abs(x-cell.x())<18&&Math.abs(z-cell.z())<18)return true;
|
||||
if(dungeon.hut()!=null&&Math.abs(y-dungeon.hut().getY())<20&&Math.hypot(x-dungeon.hut().getX(),z-dungeon.hut().getZ())<28)return true;
|
||||
for(var b:Basins180.sites(s))if(y>b.y()-28&&y<b.y()+14&&Math.hypot(x-b.x(),z-b.z())<90)return true;
|
||||
for(var p:NaturalWater184.plan(s).pools())if(y>p.surface()-40&&y<p.surface()+14&&Math.abs(x-p.seed().getX())<70&&Math.abs(z-p.seed().getZ())<70)return true;
|
||||
for(var p:SurfaceWater187.plan(s).pools())if(y>p.surface()-48&&y<p.surface()+14&&Math.abs(x-p.seed().getX())<90&&Math.abs(z-p.seed().getZ())<90)return true;
|
||||
return false;
|
||||
}
|
||||
private static void put(Map<BlockPos,BlockState> m,BlockPos p,Block b){m.put(p,b.defaultBlockState());}
|
||||
private static Map<BlockPos,BlockState> sculpt(AdventureBiomes180 s,Site site,long seed){
|
||||
if(site.kind==Kind.SURFACE||s.field().coherent()&&site.kind==Kind.SKY)return surfaceSculpt(s,site,seed);
|
||||
var m=new LinkedHashMap<BlockPos,BlockState>();var o=site.floor;int y=o.getY();
|
||||
Block stone=site.kind==Kind.SKY?Blocks.CALCITE:site.kind==Kind.CAVE?Blocks.TUFF:Blocks.ANDESITE;
|
||||
Block accent=switch(site.sector){case 0,7->Blocks.CALCITE;case 1,2->Blocks.MOSS_BLOCK;case 3->Blocks.TERRACOTTA;case 4->Blocks.DYED_TERRACOTTA.red();case 5->Blocks.AMETHYST_BLOCK;default->Blocks.DYED_TERRACOTTA.yellow();};
|
||||
double phase=(mix(seed^site.sector) & 1023)/163.0;
|
||||
for(int dx=-site.rx-2;dx<=site.rx+2;dx++)for(int dz=-site.rz-2;dz<=site.rz+2;dz++){
|
||||
int x=o.getX()+dx,z=o.getZ()+dz;
|
||||
double d=dx*dx/(double)(site.rx*site.rx)+dz*dz/(double)(site.rz*site.rz)+.12*Math.sin(dx*.7+phase)*Math.cos(dz*.6);
|
||||
if(d>1.15)continue;
|
||||
int floor=y+(Math.abs(dx)<=3&&Math.abs(dz)<=3?0:(int)Math.round(.8*Math.sin(dx*.5+phase)*Math.cos(dz*.5)));
|
||||
if(!rock(s,x,floor-3,z))continue; // Keep the edge of the host landform instead of hanging a platform over void.
|
||||
var at=new BlockPos(x,floor,z);
|
||||
if(d<=1){
|
||||
for(int h=-2;h<=0;h++)put(m,at.above(h),h==0&&EcologyBiomes176.noise(seed^0x189A0L,x/14.0,z/14.0)>.3?Blocks.MOSS_BLOCK:stone);
|
||||
int height=3+(int)(4*Math.sqrt(Math.max(0,1-d)));
|
||||
for(int h=1;h<=height;h++)put(m,at.above(h),Blocks.AIR);
|
||||
if(d>.62&&Math.floorMod(x*7+z*13,11)==0){
|
||||
put(m,at,Blocks.MOSS_BLOCK);put(m,at.above(),Blocks.FLOWERING_AZALEA);
|
||||
}
|
||||
}else {
|
||||
// Short reliefs follow surviving rock faces; no closed constructed shell.
|
||||
for(int h=0;h<=5;h++)if(rock(s,x,floor+h,z))
|
||||
put(m,at.above(h),h==2&&Math.floorMod(x+z,3)==0?Blocks.CHISELED_TUFF:h==0?Blocks.MOSS_BLOCK:stone);
|
||||
}
|
||||
}
|
||||
// A slight bend opens to an actual, dry natural floor found during selection.
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int x=o.getX()+site.dx*step-site.dz*bend,z=o.getZ()+site.dz*step+site.dx*bend;
|
||||
for(int side=-1;side<=1;side++){
|
||||
var p=new BlockPos(x-site.dz*side,y,z+site.dx*side);
|
||||
for(int h=-2;h<=0;h++)put(m,p.above(h),h==0&&step>=2?Blocks.MOSS_BLOCK:stone);
|
||||
for(int h=1;h<=4;h++)put(m,p.above(h),Blocks.AIR);
|
||||
}
|
||||
}
|
||||
// Low eroded crests shelter surface sites; all are rooted in the existing mass.
|
||||
for(int side:new int[]{-1,1}){
|
||||
int dx=-site.dx*4-site.dz*side*3,dz=-site.dz*4+site.dx*side*3;
|
||||
var base=o.offset(dx,0,dz);if(!rock(s,base.getX(),y-3,base.getZ()))continue;
|
||||
for(int h=-2;h<=2;h++)put(m,base.above(h),h==1?Blocks.CHISELED_TUFF:stone);
|
||||
if(site.bright)put(m,base,Blocks.SEA_LANTERN);
|
||||
put(m,base.above(3),Blocks.AMETHYST_BLOCK);
|
||||
put(m,base.above(4),Rosette183.GLASS[site.sector]);
|
||||
}
|
||||
for(int dx=-1;dx<=1;dx++)for(int dz=-1;dz<=1;dz++){
|
||||
put(m,o.offset(dx,0,dz),Blocks.CHISELED_TUFF);
|
||||
for(int h=1;h<=4;h++)put(m,o.offset(dx,h,dz),Blocks.AIR);
|
||||
}
|
||||
put(m,o.above(),Rosette183.GLASS[site.sector]);
|
||||
m.put(site.anchor(),ExpansionAnchorBlock.BLOCK.defaultBlockState().setValue(ExpansionAnchorBlock.SECTOR,site.sector));
|
||||
// Keep the accepted geometry; the host material and sector colour shape each refuge's palette.
|
||||
m.replaceAll((p,b)->{
|
||||
int h=p.getY()-y;
|
||||
double patch=EcologyBiomes176.noise(seed^0x188AL,p.getX()/5.0,p.getZ()/5.0);
|
||||
if(b.is(stone)){
|
||||
if(site.kind==Kind.SURFACE&&h<=0)return (h<0?Blocks.DIRT:patch<-.3?Blocks.MOSS_BLOCK:Blocks.GRASS_BLOCK).defaultBlockState();
|
||||
if(site.kind==Kind.CAVE)return (patch>.30?Blocks.DEEPSLATE:patch<-.35?Blocks.CALCITE:Blocks.TUFF).defaultBlockState();
|
||||
if(site.kind==Kind.SKY&&patch>.25)return Blocks.DIORITE.defaultBlockState();
|
||||
}
|
||||
if(b.is(Blocks.CHISELED_TUFF)&&patch>-.2)return accent.defaultBlockState();
|
||||
return b;
|
||||
});
|
||||
return m;
|
||||
}
|
||||
static int ground(AdventureBiomes180 s,int x,int z,int near){
|
||||
// Density only: neither leaves, trunks nor structures can become a soil support.
|
||||
for(int y=near+12;y>=near-8;y--)if(rock(s,x,y,z)&&!rock(s,x,y+1,z))return y;
|
||||
return near;
|
||||
}
|
||||
private static int pathY(AdventureBiomes180 s,Site site,int x,int z,int step){
|
||||
return (site.kind==Kind.SURFACE||s.field().coherent()&&site.kind==Kind.SKY)&&Math.abs(step)>2?ground(s,x,z,site.floor.getY()):site.floor.getY();
|
||||
}
|
||||
private static BlockPos pylon(AdventureBiomes180 s,Site site,int side){
|
||||
var p=site.floor.offset(-site.dx*4-site.dz*side*3,0,-site.dz*4+site.dx*side*3);
|
||||
return (site.kind==Kind.SURFACE||s.field().coherent()&&site.kind==Kind.SKY)?new BlockPos(p.getX(),ground(s,p.getX(),p.getZ(),p.getY()),p.getZ()):p;
|
||||
}
|
||||
private static Map<BlockPos,BlockState> surfaceSculpt(AdventureBiomes180 s,Site site,long seed){
|
||||
var m=new LinkedHashMap<BlockPos,BlockState>();var o=site.floor;
|
||||
for(int dx=-site.rx-2;dx<=site.rx+2;dx++)for(int dz=-site.rz-2;dz<=site.rz+2;dz++){
|
||||
double d=dx*dx/(double)(site.rx*site.rx)+dz*dz/(double)(site.rz*site.rz);
|
||||
if(d>1)continue;int x=o.getX()+dx,z=o.getZ()+dz,y=ground(s,x,z,o.getY());
|
||||
if(!rock(s,x,y-1,z))continue;
|
||||
double patch=EcologyBiomes176.noise(seed^0x189A0L,x/14.0,z/14.0);
|
||||
// One surface block, on the original rock. Broad moss only near the inward approach.
|
||||
put(m,new BlockPos(x,y,z),!s.field().coherent()&&patch>.25?Blocks.MOSS_BLOCK:Blocks.GRASS_BLOCK);
|
||||
}
|
||||
for(int step=-6;step<=11;step++){
|
||||
int bend=step>3&&step<9?1:0;
|
||||
int x=o.getX()+site.dx*step-site.dz*bend,z=o.getZ()+site.dz*step+site.dx*bend;
|
||||
for(int side=-1;side<=1;side++){
|
||||
int px=x-site.dz*side,pz=z+site.dx*side;
|
||||
int y=pathY(s,site,px,pz,step);var p=new BlockPos(px,y,pz);
|
||||
for(int h=-2;h<=0;h++)if(h==0||!rock(s,px,y+h,pz))put(m,p.above(h),h==0?(step>=2?(s.field().coherent()?Blocks.COARSE_DIRT:Blocks.MOSS_BLOCK):Blocks.ANDESITE):Blocks.DIRT);
|
||||
for(int h=1;h<=3;h++)put(m,p.above(h),Blocks.AIR);
|
||||
}
|
||||
}
|
||||
for(int side:new int[]{-1,1}){
|
||||
var p=pylon(s,site,side);
|
||||
put(m,p,Blocks.ANDESITE);put(m,p.above(),Blocks.CHISELED_TUFF);put(m,p.above(2),Blocks.TUFF);
|
||||
if(site.bright)put(m,p.above(2),Blocks.GLOWSTONE);
|
||||
put(m,p.above(3),Blocks.AMETHYST_BLOCK);put(m,p.above(4),Rosette183.GLASS[site.sector]);
|
||||
}
|
||||
for(int dx=-1;dx<=1;dx++)for(int dz=-1;dz<=1;dz++){
|
||||
for(int h=-2;h<=0;h++)put(m,o.offset(dx,h,dz),h==0?Blocks.CHISELED_TUFF:Blocks.TUFF);
|
||||
for(int h=1;h<=4;h++)put(m,o.offset(dx,h,dz),Blocks.AIR);
|
||||
}
|
||||
put(m,o.above(),Rosette183.GLASS[site.sector]);
|
||||
m.put(site.anchor(),ExpansionAnchorBlock.BLOCK.defaultBlockState().setValue(ExpansionAnchorBlock.SECTOR,site.sector));
|
||||
return m;
|
||||
}
|
||||
public static boolean treeBlocked(AdventureBiomes180 s,BlockPos p){
|
||||
for(var a:plan(s,s.seed()).sites())if(Math.abs(p.getY()-a.floor.getY())<24&&Math.hypot(p.getX()-a.floor.getX(),p.getZ()-a.floor.getZ())<15)return true;
|
||||
return false;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator g,ChunkAccess chunk){
|
||||
if(!WorldgenLab.finishedTerrain(g))return;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(chunk.getPos());
|
||||
if(entries!=null)entries.forEach(chunk::setBlockState);
|
||||
}
|
||||
public static boolean protects(WorldGenLevel level,ChunkGenerator g,BlockPos pos){
|
||||
if(!WorldgenLab.finishedTerrain(g)||pos.getY()<88||pos.getY()>512)return false;
|
||||
var entries=plan(source(g),level.getSeed()).chunks.get(new ChunkPos(pos.getX()>>4,pos.getZ()>>4));
|
||||
return entries!=null&&entries.containsKey(pos);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 source){
|
||||
var p=plan(source,level.getSeed());var again=create(source,level.getSeed());
|
||||
if(!p.sites.equals(again.sites)||!p.chunks.equals(again.chunks))throw new IllegalStateException("Unstable anchor plan");
|
||||
int count=0;var details=new ArrayList<Map<String,Object>>();
|
||||
for(var cp:p.chunks.keySet())level.getChunk(cp.x(),cp.z());
|
||||
for(var e:p.chunks.entrySet())for(var b:e.getValue().entrySet()){
|
||||
if(!e.getKey().contains(b.getKey()))throw new IllegalStateException("Cross-chunk anchor write");
|
||||
if(!level.getBlockState(b.getKey()).equals(b.getValue()))throw new IllegalStateException("Anchor refuge mismatch "+b.getKey()+" expected "+b.getValue()+" got "+level.getBlockState(b.getKey()));
|
||||
if(b.getValue().is(ExpansionAnchorBlock.BLOCK))count++;
|
||||
}
|
||||
for(var site:p.sites){
|
||||
if(Rosette183.sector(site.floor.getX(),site.floor.getZ())!=site.sector)throw new IllegalStateException("Wrong colour sector");
|
||||
// Walk the full three-wide bent approach and verify its actual native support.
|
||||
for(int step=-6;step<=11;step++){
|
||||
if(step>=-1&&step<=1)continue;
|
||||
int bend=step>3&&step<9?1:0;var at=site.floor.offset(site.dx*step-site.dz*bend,1,site.dz*step+site.dx*bend);
|
||||
at=new BlockPos(at.getX(),pathY(source,site,at.getX(),at.getZ(),step)+1,at.getZ());
|
||||
if(!level.getBlockState(at).isAir()||!level.getBlockState(at.above()).isAir()||level.getBlockState(at.below()).getCollisionShape(level,at.below()).isEmpty())throw new IllegalStateException("Anchor approach blocked "+at);
|
||||
}
|
||||
if(!rock(source,site.floor.getX(),site.floor.getY()-5,site.floor.getZ()))throw new IllegalStateException("Anchor above void");
|
||||
if(site.dx*site.floor.getX()+site.dz*site.floor.getZ()>=0)throw new IllegalStateException("Approach points outward");
|
||||
for(int side:new int[]{-1,1}){
|
||||
var pylon=pylon(source,site,side);
|
||||
if(!level.getBlockState(pylon.above(3)).is(Blocks.AMETHYST_BLOCK)||!level.getBlockState(pylon.above(4)).is(Rosette183.GLASS[site.sector]))throw new IllegalStateException("Pylon glass must sit on amethyst");
|
||||
}
|
||||
details.add(Map.of("sector",site.sector,"kind",site.kind.name(),"anchor",site.anchor().toShortString(),"entrance",site.entrance().toShortString(),"bright",site.bright));
|
||||
}
|
||||
if(count!=8||p.sites.stream().map(Site::sector).distinct().count()!=8)throw new IllegalStateException("Expected exactly eight anchors");
|
||||
var offers=AnchorOffers189.check(level,p.sites);
|
||||
return Map.of("sites",details,"planningMs",p.millis,"chunks",p.chunks.size(),"blocks",p.chunks.values().stream().mapToInt(Map::size).sum(),"deterministic",true,"offers",offers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent184 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Palace184.Plan> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent184() {}
|
||||
private static Palace184.Plan plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace184.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.ascent184(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);Sulfur183.place(source,chunk);NaturalWater184.place(source,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).blocks().forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("palace",Palace184.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("sulfur",Sulfur183.check(level,source));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent185 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Map<BlockPos,BlockState>> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent185() {}
|
||||
private static Map<BlockPos,BlockState> plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace185.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.stem185(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);Sulfur183.place(source,chunk);NaturalWater184.place(source,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("palace",Palace185.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("sulfur",Sulfur183.check(level,source));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent186 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Map<BlockPos,BlockState>> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent186() {}
|
||||
private static Map<BlockPos,BlockState> plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace186.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.anchors186(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);Sulfur183.place(source,chunk);NaturalWater184.place(source,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("anchors",Anchors186.check(level,source));
|
||||
result.put("palace",Palace186.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("sulfur",Sulfur183.check(level,source));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent187 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Map<BlockPos,BlockState>> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent187() {}
|
||||
private static Map<BlockPos,BlockState> plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace186.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.natural187(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);NaturalWater184.place(source,chunk);SurfaceWater187.place(source,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("anchors",Anchors187.check(level,source));
|
||||
result.put("palace",Palace186.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("surfaceWater",SurfaceWater187.check(level,source));
|
||||
result.put("sulfur",Sulfur187.check(level,source));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent188 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Map<BlockPos,BlockState>> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent188() {}
|
||||
private static Map<BlockPos,BlockState> plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace186.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.details188(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);NaturalWater184.place(source,chunk);SurfaceWater187.place(source,chunk);Lakes188.decorate(source,chunk);SulfurDetails188.place(level,generator,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("anchors",Anchors188.check(level,source));
|
||||
result.put("palace",Palace186.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("surfaceWater",SurfaceWater187.check(level,source));
|
||||
result.put("sulfur",Sulfur187.check(level,source));result.put("sulfurFeatures",SulfurDetails188.check(level,source));result.put("lakeBeds",Lakes188.check(level,source));result.put("ironLoot",IronLoot188.check(level));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** New world only: retained island, native cavity water and natural vertical palace. */
|
||||
public final class Ascent189 {
|
||||
private record Key(long seed,Station183.Layout layout) {}
|
||||
private static final Map<Key,Map<BlockPos,BlockState>> PLANS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private Ascent189() {}
|
||||
private static Map<BlockPos,BlockState> plan(long seed,ChunkGenerator g){return PLANS.computeIfAbsent(new Key(seed,Station183.layout(g)),k->Palace186.plan(k.seed(),k.layout()));}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.finishedTerrain(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);NaturalWater184.place(source,chunk);Water189.finish(source,chunk);Gallery189.place(source,chunk);Ruins189.place(source,chunk);SulfurDetails188.place(level,generator,chunk);
|
||||
var pos=chunk.getPos();
|
||||
if(pos.x()>=-3&&pos.x()<=2&&pos.z()>=-3&&pos.z()<=2)
|
||||
plan(level.getSeed(),generator).forEach((p,b)->{if(pos.contains(p))chunk.setBlockState(p,b);});
|
||||
PalaceAtlas184.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var generator=level.getChunkSource().getGenerator();var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();result.put(source.field().eroded()?"erodedMassif":source.originalRelief()?"originalRelief":source.field().refined()?"naturalRelief":"terraces",Terrain189.check(level,source));
|
||||
if(source.field().coherent())result.put("continuity",Continuity193.check(level,source));
|
||||
result.put("anchors",Anchors189.check(level,source));
|
||||
result.put("palace",Palace186.check(level,plan(level.getSeed(),generator)));
|
||||
result.put("naturalWater",NaturalWater184.check(level,source));
|
||||
result.put("atlas",PalaceAtlas184.check(level));result.put("gallery",Cavern183.check(level));
|
||||
result.put("surfaceWater",Water189.check(level,source));
|
||||
result.put("sulfur",Sulfur187.check(level,source));result.put("sulfurFeatures",SulfurDetails188.check(level,source));result.put("galleryDetails",Gallery189.check(level,source));result.put("ruins",Ruins189.check(level,source));result.put("ironLoot",IronLoot188.check(level));result.put("gemDistricts",Gems183.check(level));return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import fr.koka.sanctuary.SanctuaryMod;
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.culling.Frustum;
|
||||
import net.minecraft.resources.Identifier;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
/** Verify the actual client frustum hooks once per new lab level, including both terrain APIs. */
|
||||
final class AscentView184 {
|
||||
private static final Set<ClientLevel> CHECKED=Collections.newSetFromMap(new WeakHashMap<>());
|
||||
static void register(){ClientTickEvents.END_CLIENT_TICK.register(client->{
|
||||
var level=client.level;
|
||||
if(level==null||!level.dimensionTypeRegistration().is(Identifier.fromNamespaceAndPath("sanctuary_test","ascent_1600"))||!CHECKED.add(level))return;
|
||||
var frustum=new Frustum(new Matrix4f().scaling(1/4096f),new Matrix4f());
|
||||
var crown=new AABB(-32,1152,-32,32,1328,32);var root=new AABB(-16,624,-16,16,672,16);
|
||||
var cube=new BoundingBox(-32,1152,-32,32,1328,32);
|
||||
frustum.prepare(0,250,0);
|
||||
if(frustum.isVisible(crown)||frustum.cubeInFrustum(cube)<0||!frustum.isVisible(root))throw new IllegalStateException("Ascent ground reveal regression");
|
||||
frustum.prepare(0,640,0);
|
||||
if(!frustum.isVisible(crown)||frustum.cubeInFrustum(cube)>=0)throw new IllegalStateException("Ascent crown remains hidden at rosette");
|
||||
SanctuaryMod.LOGGER.info("ASCENT_VIEW_VERIFIED groundHidden=true rosetteVisible=true crownRevealedAt640=true");
|
||||
});}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Two bounded, terrain-selected lake systems. Each broad upper pool spills into a lower pool. */
|
||||
public final class Basins180 {
|
||||
public record Basin(int x,int y,int z,int dx,int dz,int score){}
|
||||
private static final Map<AdventureBiomes180,List<Basin>> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Basins180(){}
|
||||
private static boolean solid(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
public static List<Basin> sites(AdventureBiomes180 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,Basins180::plan);}}
|
||||
private static List<Basin> plan(AdventureBiomes180 s){
|
||||
var candidates=new ArrayList<Basin>();
|
||||
for(int x=-192;x<=192;x+=32)for(int z=-192;z<=192;z+=32){
|
||||
if(Math.hypot(x,z)>240||Dungeon180.near(s,x,z,48))continue;
|
||||
for(int y=Math.min(212,s.terrain(x,z).top()-22);y>=104;y-=4){
|
||||
if(!solid(s,x,y,z)||solid(s,x,y+3,z)||solid(s,x,y+10,z))continue;
|
||||
for(int axis=0;axis<4;axis++){
|
||||
int dx=axis==0?1:axis==1?-1:0,dz=axis==2?1:axis==3?-1:0,score=0;
|
||||
if(Dungeon180.near(s,x+dx*30,z+dz*30,30))continue;
|
||||
for(int u=-24;u<=48;u+=8)for(int v=-16;v<=16;v+=8){
|
||||
int bx=x+dx*u-dz*v,bz=z+dz*u+dx*v;
|
||||
if(solid(s,bx,y-8,bz)&&!solid(s,bx,y+6,bz)&&s.terrain(bx,bz).top()>y+14)score++;
|
||||
}
|
||||
if(s.field().coherent()&&galleryOverlap(s,x,y+2,z,dx,dz))continue;
|
||||
candidates.add(new Basin(x,y+2,z,dx,dz,score));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
candidates.sort(Comparator.comparingInt(Basin::score).reversed());var chosen=new ArrayList<Basin>();
|
||||
for(var b:candidates)if(b.score()>=24&&chosen.stream().allMatch(a->Math.hypot(a.x()-b.x(),a.z()-b.z())>145)){
|
||||
chosen.add(b);if(chosen.size()==2)break;
|
||||
}
|
||||
if(chosen.isEmpty()||(!s.field().coherent()&&chosen.size()!=2))throw new IllegalStateException("No two retained cave lake sites: "+chosen+" candidates="+candidates.size());
|
||||
return List.copyOf(chosen);
|
||||
}
|
||||
private static boolean galleryOverlap(AdventureBiomes180 s,int x,int y,int z,int dx,int dz){
|
||||
if(Math.hypot(x,z)>135)return false;
|
||||
for(var c:Cavern183.plan(s).cells().values()){
|
||||
if(c.y()>y+4||c.ceiling()<y-16)continue;
|
||||
int u=(c.x()-x)*dx+(c.z()-z)*dz,v=-(c.x()-x)*dz+(c.z()-z)*dx;
|
||||
if(u>=-34&&u<=60&&Math.abs(v)<=26)return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
for(var b:sites(source)){
|
||||
if(Math.abs(c.getPos().getMiddleBlockX()-b.x())>76||Math.abs(c.getPos().getMiddleBlockZ()-b.z())>76)continue;
|
||||
for(int x=c.getPos().getMinBlockX();x<=c.getPos().getMaxBlockX();x++)for(int z=c.getPos().getMinBlockZ();z<=c.getPos().getMaxBlockZ();z++){
|
||||
double u=(x-b.x())*b.dx()+(z-b.z())*b.dz(),v=-(x-b.x())*b.dz()+(z-b.z())*b.dx();
|
||||
double wobble=.10*EcologyBiomes176.noise(level.getSeed()^0x180BA5L,x/12.0,z/12.0);
|
||||
double upper=u*u/(29*29.0)+v*v/(21*21.0)+wobble;
|
||||
double lower=(u-37)*(u-37)/(18*18.0)+v*v/(14*14.0)+wobble;
|
||||
if(upper>1.08&&lower>1.08)continue;
|
||||
boolean low=upper>1.0||(u>26&&Math.abs(v)<=2&&lower<=1);
|
||||
double shape=low?lower:upper;if(shape>1.08)continue;
|
||||
int waterY=b.y()-(low?4:0);
|
||||
boolean spill=Math.abs(v)<=2&&u>22&&u<38;
|
||||
boolean shore=shape>.88&&!spill;
|
||||
if(Dungeon180.protectedAt(source,x,waterY,z))continue;
|
||||
int nativeFloor=waterY-8;
|
||||
for(int probe=waterY+3;probe>=waterY-10;probe--)if(AdventureCaves180.natural(c.getBlockState(new BlockPos(x,probe,z)))){nativeFloor=probe;break;}
|
||||
// Keep protruding rock and existing high banks. The lake bends around those reliefs.
|
||||
if(!spill && nativeFloor>waterY+1)continue;
|
||||
if(shore && nativeFloor>=waterY)continue;
|
||||
int depth=shore?1:Math.clamp(waterY-nativeFloor+1,2,8);
|
||||
// Native clay/rock form a continuous bed and bank; no water can escape underneath.
|
||||
for(int y=waterY-depth-2;y<=waterY;y++){
|
||||
var material=y<waterY-depth?Blocks.TUFF:y==waterY-depth?Blocks.CLAY:shore?(y==waterY?Blocks.MOSS_BLOCK:Blocks.CLAY):Blocks.WATER;
|
||||
var p=new BlockPos(x,y,z);c.setBlockState(p,material.defaultBlockState());
|
||||
if(material==Blocks.WATER)c.markPosForPostProcessing(p);
|
||||
}
|
||||
for(int y=waterY+1;y<=b.y()+3;y++)c.setBlockState(new BlockPos(x,y,z),Blocks.AIR.defaultBlockState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.EntityTypes;
|
||||
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.block.state.properties.RailShape;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Small seed-owned complexes, clipped per chunk; no global hydrology or existing-world writes. */
|
||||
public final class CaveComplex179 {
|
||||
public record Site(int x,int y,int z,int dx,int dz,int length,boolean hut){}
|
||||
private static final Map<LivingBiomes179,List<Site>> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private CaveComplex179(){}
|
||||
public static List<Site> sites(LivingBiomes179 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,CaveComplex179::plan);}}
|
||||
private static boolean rock(LivingBiomes179 source,int x,int y,int z){return source.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
private static List<Site> plan(LivingBiomes179 source){
|
||||
var sites=new ArrayList<Site>();
|
||||
for(int side=0;side<3;side++){
|
||||
int dx=side==0?1:side==1?-1:0,dz=side==2?1:0;Site best=null;int score=-1;
|
||||
for(int r=112;r<=208;r+=24)for(int cross=-72;cross<=72;cross+=24){
|
||||
int x=dx*r-dz*cross,z=dz*r+dx*cross,top=source.terrain(x,z).top();
|
||||
for(int y=Math.min(208,top-20);y>=112;y-=2){
|
||||
if(!rock(source,x,y,z)||rock(source,x,y+3,z)||rock(source,x,y+9,z))continue;
|
||||
int n=0;for(int a=-6;a<=6;a+=3)for(int b=-6;b<=6;b+=3)if(!rock(source,x+a,y+4,z+b))n++;
|
||||
if(n>score){score=n;best=new Site(x,y,z,dx,dz,0,false);}break;
|
||||
}
|
||||
}
|
||||
if(best!=null){
|
||||
int length=36;
|
||||
// Stop just outside the island skin: a real daylight opening, not an endless bridge.
|
||||
while(length<180 && source.terrain(best.x()+dx*length,best.z()+dz*length).top()>best.y()+4)length+=4;
|
||||
sites.add(new Site(best.x(),best.y(),best.z(),dx,dz,length+3,false));
|
||||
}
|
||||
}
|
||||
Site hut=null;int best=-1;
|
||||
for(int x=-224;x<=224;x+=16)for(int z=-224;z<=224;z+=16){
|
||||
int top=source.terrain(x,z).top();
|
||||
for(int y=Math.min(216,top-20);y>=96;y-=2){
|
||||
if(!LivingCaves179.biome(source,x,y,z).equals("living_marsh")||!rock(source,x,y,z)||rock(source,x,y+1,z)||rock(source,x,y+8,z))continue;
|
||||
int n=0;for(int a=-4;a<=4;a+=2)for(int b=-4;b<=4;b+=2)if(!rock(source,x+a,y+5,z+b)&&rock(source,x+a,y-3,z+b))n++;
|
||||
if(n>best){best=n;hut=new Site(x,y,z,1,0,0,true);}break;
|
||||
}
|
||||
}
|
||||
if(hut!=null)sites.add(hut);
|
||||
return List.copyOf(sites);
|
||||
}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology179(generator))return;
|
||||
var source=(LivingBiomes179)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
for(var site:sites(source)){
|
||||
int radius=site.hut()?7:site.length()+30;
|
||||
if(c.getPos().getMaxBlockX()<site.x()-radius||c.getPos().getMinBlockX()>site.x()+radius||c.getPos().getMaxBlockZ()<site.z()-radius||c.getPos().getMinBlockZ()>site.z()+radius)continue;
|
||||
for(int x=c.getPos().getMinBlockX();x<=c.getPos().getMaxBlockX();x++)for(int z=c.getPos().getMinBlockZ();z<=c.getPos().getMaxBlockZ();z++){
|
||||
int u=(x-site.x())*site.dx()+(z-site.z())*site.dz(),v=-(x-site.x())*site.dz()+(z-site.z())*site.dx();
|
||||
if(site.hut())hut(c,site,x,z,u,v);else mine(c,site,x,z,u,v);
|
||||
}
|
||||
if(site.hut() && c.getPos().contains(new BlockPos(site.x(),site.y(),site.z()))){
|
||||
var random=RandomSource.create(level.getSeed()^0x179A11L);
|
||||
LivingCaves179.spawn(level,new BlockPos(site.x(),site.y()+2,site.z()),EntityTypes.WITCH,random);
|
||||
LivingCaves179.spawn(level,new BlockPos(site.x()+1,site.y()+2,site.z()),EntityTypes.CAT,random);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void set(ChunkAccess c,int x,int y,int z,Block b){c.setBlockState(new BlockPos(x,y,z),b.defaultBlockState());}
|
||||
private static void mine(ChunkAccess c,Site s,int x,int z,int u,int v){
|
||||
boolean hall=Math.abs(u)<=7&&Math.abs(v)<=6;
|
||||
boolean trunk=u>=-30&&u<=s.length()&&Math.abs(v)<=2;
|
||||
boolean branch=(Math.abs(u+22)<=2||Math.abs(u-22)<=2)&&Math.abs(v)<=26;
|
||||
if(!hall&&!trunk&&!branch)return;
|
||||
int height=hall?7:4,y=s.y();
|
||||
for(int dy=1;dy<=height;dy++)set(c,x,y+dy,z,Blocks.AIR);
|
||||
set(c,x,y,z,Blocks.DARK_OAK_PLANKS);
|
||||
boolean pillar=hall?Math.abs(u)==7&&Math.abs(v)==6:trunk?Math.abs(v)==2&&Math.floorMod(u,6)==0:Math.abs(v)%6==0&&(Math.abs(u+22)==2||Math.abs(u-22)==2);
|
||||
if(pillar){
|
||||
for(int dy=1;dy<=height;dy++)set(c,x,y+dy,z,Blocks.DARK_OAK_LOG);
|
||||
// Suspended sections attach to a natural ceiling where one exists.
|
||||
int anchor=0;
|
||||
for(int dy=height+1;dy<=40;dy++)if(LivingCaves179.natural(c.getBlockState(new BlockPos(x,y+dy,z)))){anchor=dy;break;}
|
||||
if(anchor>0)for(int dy=height+1;dy<anchor;dy++)set(c,x,y+dy,z,Blocks.IRON_CHAIN);
|
||||
}
|
||||
boolean beam=hall?(Math.abs(u)%7==0):(trunk?Math.floorMod(u,6)==0:Math.floorMod(v,6)==0);
|
||||
if(beam)set(c,x,y+height,z,Blocks.DARK_OAK_LOG);
|
||||
if(beam && !pillar && (hall?u==0&&Math.abs(v)==5:trunk?v==0:true))
|
||||
c.setBlockState(new BlockPos(x,y+height-1,z),Blocks.LANTERN.defaultBlockState().setValue(LanternBlock.HANGING,true));
|
||||
if(trunk&&v==0&&!hall)c.setBlockState(new BlockPos(x,y+1,z),Blocks.RAIL.defaultBlockState().setValue(RailBlock.SHAPE,s.dx()!=0?RailShape.EAST_WEST:RailShape.NORTH_SOUTH));
|
||||
if(hall&&Math.abs(u)==5&&Math.abs(v)==4)set(c,x,y+1,z,Blocks.BARREL);
|
||||
}
|
||||
private static void hut(ChunkAccess c,Site s,int x,int z,int u,int v){
|
||||
if(Math.abs(u)>4||Math.abs(v)>5)return;
|
||||
int y=s.y()+1;
|
||||
boolean corner=Math.abs(u)==3&&Math.abs(v)==4;
|
||||
if(corner){for(int dy=-4;dy<=4;dy++)set(c,x,y+dy,z,Blocks.SPRUCE_LOG);}
|
||||
if(Math.abs(u)<=3&&Math.abs(v)<=4){
|
||||
set(c,x,y,z,Blocks.SPRUCE_PLANKS);
|
||||
for(int dy=1;dy<=4;dy++)set(c,x,y+dy,z,Blocks.AIR);
|
||||
boolean wall=Math.abs(u)==3||Math.abs(v)==4;
|
||||
if(wall&&!(v==-4&&u==0))for(int dy=1;dy<=3;dy++)set(c,x,y+dy,z,corner?Blocks.SPRUCE_LOG:dy==2&&((Math.abs(u)==3&&v==0)||(Math.abs(v)==4&&Math.abs(u)==2))?Blocks.GLASS_PANE:Blocks.SPRUCE_PLANKS);
|
||||
}
|
||||
// Stepped overhanging roof, with a clear entrance and a porch.
|
||||
int roof=y+4+(4-Math.abs(u))/2;set(c,x,roof,z,Blocks.DARK_OAK_SLAB);
|
||||
if(v==-5&&Math.abs(u)<=2)set(c,x,y,z,Blocks.SPRUCE_PLANKS);
|
||||
if(u==2&&v==2)set(c,x,y+1,z,Blocks.CAULDRON);
|
||||
if(u==-2&&v==2)set(c,x,y+1,z,Blocks.CRAFTING_TABLE);
|
||||
if(u==0&&v==3)set(c,x,y+1,z,Blocks.LANTERN);
|
||||
if(u==-2&&v==3)set(c,x,y+1,z,Blocks.POTTED_RED_MUSHROOM);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Local, seed-bound rock gallery. All carving belongs to new gallery_v1 chunks. */
|
||||
public final class Cavern182 {
|
||||
record Room(int x,int y,int z,int rx,int rz) {}
|
||||
record Cell(int x,int y,int z,int ceiling,boolean path) {}
|
||||
record Plan(List<Room> rooms,Map<Long,Cell> cells,List<BlockPos> route) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Cavern182() {}
|
||||
private static long key(int x,int z){return ((long)x<<32)^(z&0xffffffffL);}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
static Plan plan(AdventureBiomes180 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,Cavern182::create);}}
|
||||
|
||||
private static int floor(AdventureBiomes180 s,int x,int z,int low,int high,int fallback) {
|
||||
int best=-1,bestScore=Integer.MIN_VALUE;
|
||||
for(int y=high;y>=low;y--) {
|
||||
if(!rock(s,x,y,z)||!rock(s,x,y-3,z)||rock(s,x,y+1,z))continue;
|
||||
boolean roof=false;for(int h=12;h<=40;h+=4)if(rock(s,x,y+h,z)){roof=true;break;}
|
||||
if(!roof)continue;
|
||||
int score=0;
|
||||
for(int dx=-8;dx<=8;dx+=4)for(int dz=-8;dz<=8;dz+=4)
|
||||
if(!rock(s,x+dx,y+4,z+dz)&&rock(s,x+dx,y-4,z+dz))score+=3;
|
||||
score-=Math.abs(y-fallback)/3;
|
||||
if(score>bestScore){best=y;bestScore=score;}
|
||||
}
|
||||
if(best>=0)return best;
|
||||
if(fallback>0&&rock(s,x,fallback-3,z))return fallback;
|
||||
throw new IllegalStateException("No supported central gallery floor at "+x+","+z);
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s) {
|
||||
int y=floor(s,0,0,148,212,-1);
|
||||
var rooms=List.of(new Room(0,y,0,14,11),
|
||||
new Room(-34,floor(s,-34,-26,y-18,y+8,y-6),-26,10,8),
|
||||
new Room(-38,floor(s,-38,24,y-30,y+8,y-16),24,12,10));
|
||||
var cells=new LinkedHashMap<Long,Cell>();
|
||||
for(var r:rooms)for(int x=r.x-r.rx-1;x<=r.x+r.rx+1;x++)for(int z=r.z-r.rz-1;z<=r.z+r.rz+1;z++) {
|
||||
double u=(x-r.x)/(double)r.rx,v=(z-r.z)/(double)r.rz;
|
||||
double d=u*u+v*v+.07*Math.sin(x*.6)*Math.cos(z*.4);
|
||||
if(d>1)continue;
|
||||
double amplitude=Math.clamp((Math.hypot(x-r.x,z-r.z)-5)/4,0,1);
|
||||
int ground=r.y+(int)Math.round(amplitude*(Math.sin(x*.19)*1.1+Math.sin(z*.23)));
|
||||
int ceiling=r.y+5+(int)(9*Math.sqrt(Math.max(0,1-d)));
|
||||
cells.put(key(x,z),new Cell(x,ground,z,ceiling,false));
|
||||
}
|
||||
var route=new ArrayList<BlockPos>();
|
||||
link(cells,route,rooms.get(0),rooms.get(1),new int[][]{{0,0},{-8,-12},{-23,-17},{-34,-26}});
|
||||
link(cells,route,rooms.get(1),rooms.get(2),new int[][]{{-34,-26},{-49,-16},{-48,5},{-38,24}});
|
||||
link(cells,route,rooms.get(2),rooms.get(0),new int[][]{{-38,24},{-25,20},{-12,8},{0,0}});
|
||||
return new Plan(rooms,Map.copyOf(cells),List.copyOf(route));
|
||||
}
|
||||
private static void link(Map<Long,Cell> cells,List<BlockPos> route,Room a,Room b,int[][] turns) {
|
||||
var line=new ArrayList<int[]>();
|
||||
for(int i=1;i<turns.length;i++) {
|
||||
int x=turns[i-1][0],z=turns[i-1][1],tx=turns[i][0],tz=turns[i][1];
|
||||
int dx=Math.abs(tx-x),dz=Math.abs(tz-z),sx=Integer.signum(tx-x),sz=Integer.signum(tz-z),err=dx-dz;
|
||||
while(x!=tx||z!=tz){line.add(new int[]{x,z});int twice=2*err;if(twice> -dz){err-=dz;x+=sx;}if(twice<dx){err+=dx;z+=sz;}}
|
||||
}
|
||||
line.add(new int[]{b.x,b.z});
|
||||
for(int i=0;i<line.size();i++) {
|
||||
var p=line.get(i);int y=(int)Math.round(a.y+(b.y-a.y)*i/(double)(line.size()-1));
|
||||
route.add(new BlockPos(p[0],y+1,p[1]));
|
||||
for(int dx=-2;dx<=2;dx++)for(int dz=-2;dz<=2;dz++) {
|
||||
if(dx*dx+dz*dz>5)continue;
|
||||
int x=p[0]+dx,z=p[1]+dz;var old=cells.get(key(x,z));
|
||||
// Keep the first corridor floor at self-overlaps, guaranteeing a stable shared seam.
|
||||
if(old!=null&&old.path)continue;
|
||||
int roof=Math.max(old==null?y+6:old.ceiling,y+5+(dx==0||dz==0?2:0));
|
||||
cells.put(key(x,z),new Cell(x,y,z,roof,true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void set(ChunkAccess c,BlockPos p,Block b){c.setBlockState(p,b.defaultBlockState());}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk) {
|
||||
if(!WorldgenLab.gallery182(generator))return;
|
||||
if(chunk.getPos().getMaxBlockX()< -54||chunk.getPos().getMinBlockX()>15
|
||||
||chunk.getPos().getMaxBlockZ()< -36||chunk.getPos().getMinBlockZ()>35)return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();var p=plan(source);
|
||||
for(var c:p.cells.values()) {
|
||||
var at=new BlockPos(c.x,c.y,c.z);if(!chunk.getPos().contains(at))continue;
|
||||
for(int y=c.y-2;y<=c.ceiling;y++) {
|
||||
var pos=new BlockPos(c.x,y,c.z);
|
||||
if(y>c.y){set(chunk,pos,Blocks.AIR);continue;}
|
||||
// Sculpt only this floor's local foundation, retaining minerals elsewhere.
|
||||
Block stone=y<c.y?Blocks.TUFF:Math.floorMod(c.x*11+c.z*7,19)<3?Blocks.MOSS_BLOCK:Blocks.ANDESITE;
|
||||
if(Math.abs(c.x)<=2&&Math.abs(c.z)<=2)stone=Blocks.POLISHED_TUFF;
|
||||
set(chunk,pos,stone);
|
||||
}
|
||||
// Vertical seams colour existing stone; there is no box-shaped lining.
|
||||
for(int[] d:new int[][]{{1,0},{-1,0},{0,1},{0,-1}}) {
|
||||
int wx=c.x+d[0],wz=c.z+d[1];if(p.cells.containsKey(key(wx,wz)))continue;
|
||||
for(int y=c.y+1;y<c.ceiling;y++) {
|
||||
var wall=new BlockPos(wx,y,wz);if(!chunk.getPos().contains(wall))continue;
|
||||
if(!AdventureCaves180.natural(chunk.getBlockState(wall)))continue;
|
||||
if(Math.floorMod(wx*3+wz*5+y/4,13)<2)set(chunk,wall,Blocks.CALCITE);
|
||||
else if(y==c.y+1&&Math.floorMod(wx+wz,4)==0)set(chunk,wall,Blocks.MOSS_BLOCK);
|
||||
else if(y==c.y+3&&Math.floorMod(wx+wz,7)==0)set(chunk,wall,Blocks.CHISELED_TUFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
int y=p.rooms.getFirst().y;
|
||||
// The dark core is a visual placeholder; the broken stone arch is not a Nether frame.
|
||||
putIfOwned(chunk,0,y+1,0,Blocks.CHISELED_POLISHED_BLACKSTONE);
|
||||
putIfOwned(chunk,0,y+2,0,Blocks.OBSIDIAN);
|
||||
for(int side:new int[]{-1,1})for(int h=0;h<7;h++) {
|
||||
int x=side*(4-(h>=5?1:0));
|
||||
putIfOwned(chunk,x,y+1+h,-5,h%3==0?Blocks.OBSIDIAN:Blocks.POLISHED_BASALT);
|
||||
}
|
||||
for(int[] b:new int[][]{{-6,-4},{6,-4},{-5,5},{5,5}}) {
|
||||
var cell=p.cells.get(key(b[0],b[1]));
|
||||
putIfOwned(chunk,b[0],cell.y-1,b[1],Blocks.SEA_LANTERN);
|
||||
putIfOwned(chunk,b[0],cell.y,b[1],Blocks.COPPER_GRATE.waxed().oxidized());
|
||||
}
|
||||
}
|
||||
private static void putIfOwned(ChunkAccess chunk,int x,int y,int z,Block b) {
|
||||
var p=new BlockPos(x,y,z);if(chunk.getPos().contains(p))set(chunk,p,b);
|
||||
}
|
||||
|
||||
static Map<String,Object> check(ServerLevel level) {
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).getBiomeSource();var p=plan(source);
|
||||
for(int x=-4;x<=0;x++)for(int z=-3;z<=2;z++)level.getChunk(x,z);
|
||||
int cy=p.rooms.getFirst().y;
|
||||
if(!level.getBlockState(new BlockPos(0,cy+2,0)).is(Blocks.OBSIDIAN))throw new IllegalStateException("Missing lower core");
|
||||
// Follow the actual saved surfaces across chunk boundaries, not just the planned centreline.
|
||||
var floors=new HashMap<Long,Integer>();
|
||||
for(var c:p.cells.values()) {
|
||||
if(Math.abs(c.x)<=1&&Math.abs(c.z)<=1)continue;
|
||||
int actual=Integer.MIN_VALUE;
|
||||
for(int y=c.y+2;y>=c.y-2;y--) {
|
||||
var at=new BlockPos(c.x,y,c.z);
|
||||
if(!level.getBlockState(at).getCollisionShape(level,at).isEmpty()&&level.getBlockState(at.above()).isAir()&&level.getBlockState(at.above(2)).isAir()){actual=y;break;}
|
||||
}
|
||||
if(actual!=Integer.MIN_VALUE)floors.put(key(c.x,c.z),actual);
|
||||
}
|
||||
long start=key(2,0);if(!floors.containsKey(start))throw new IllegalStateException("No gallery approach");
|
||||
var visited=new HashSet<Long>();var queue=new ArrayDeque<Long>();queue.add(start);
|
||||
while(!queue.isEmpty()) {
|
||||
long k=queue.removeFirst();if(!visited.add(k))continue;int x=(int)(k>>32),z=(int)k;
|
||||
for(int[] d:new int[][]{{1,0},{-1,0},{0,1},{0,-1}}) {
|
||||
long next=key(x+d[0],z+d[1]);var y=floors.get(next);
|
||||
if(y!=null&&Math.abs(y-floors.get(k))<=1&&!visited.contains(next))queue.add(next);
|
||||
}
|
||||
}
|
||||
for(var r:p.rooms)if(!visited.contains(key(r.x+2,r.z)))throw new IllegalStateException("Disconnected gallery room: "+r);
|
||||
if(visited.size()<floors.size()*.9)throw new IllegalStateException("Too many isolated gallery surfaces: "+visited.size()+"/"+floors.size());
|
||||
return Map.of("lowerCore",new BlockPos(0,cy+2,0).toShortString(),"rooms",p.rooms,"walkableCells",visited.size(),"plannedColumns",p.cells.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Local, seed-bound rock gallery. All carving belongs to new discovery_v1 chunks. */
|
||||
public final class Cavern183 {
|
||||
record Room(int x,int y,int z,int rx,int rz) {}
|
||||
record Cell(int x,int y,int z,int ceiling,boolean path) {}
|
||||
record Plan(List<Room> rooms,Map<Long,Cell> cells,List<BlockPos> route) {}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Cavern183() {}
|
||||
private static long key(int x,int z){return ((long)x<<32)^(z&0xffffffffL);}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
static Plan plan(AdventureBiomes180 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,Cavern183::create);}}
|
||||
|
||||
private static int floor(AdventureBiomes180 s,int x,int z,int low,int high,int fallback) {
|
||||
int best=-1,bestScore=Integer.MIN_VALUE;
|
||||
for(int y=high;y>=low;y--) {
|
||||
if(!rock(s,x,y,z)||!rock(s,x,y-3,z)||rock(s,x,y+1,z))continue;
|
||||
boolean roof=false;for(int h=12;h<=40;h+=4)if(rock(s,x,y+h,z)){roof=true;break;}
|
||||
if(!roof)continue;
|
||||
int score=0;
|
||||
for(int dx=-8;dx<=8;dx+=4)for(int dz=-8;dz<=8;dz+=4)
|
||||
if(!rock(s,x+dx,y+4,z+dz)&&rock(s,x+dx,y-4,z+dz))score+=3;
|
||||
score-=Math.abs(y-fallback)/3;
|
||||
if(score>bestScore){best=y;bestScore=score;}
|
||||
}
|
||||
if(best>=0)return best;
|
||||
if(fallback>0&&rock(s,x,fallback-3,z))return fallback;
|
||||
return -1;
|
||||
}
|
||||
private static Room satellite(AdventureBiomes180 s,int x,int z,int centre,int preferred,int rx,int rz) {
|
||||
Room best=null;int score=Integer.MAX_VALUE;
|
||||
// Search only nearby existing cave floors, rather than forcing a floating room at a fixed coordinate.
|
||||
for(int dx=-28;dx<=28;dx+=4)for(int dz=-28;dz<=28;dz+=4){
|
||||
if(Math.hypot(x+dx,z+dz)>58||(z<0?z+dz> -12:z+dz<12))continue;
|
||||
double separation=Math.pow((x+dx)/(double)(19+rx+3),2)+Math.pow((z+dz)/(double)(16+rz+3),2);
|
||||
if(separation<1)continue; // Separate floor levels must meet through a ramp, never overlapping rooms.
|
||||
int y=floor(s,x+dx,z+dz,centre-18,centre+8,-1);if(y<0)continue;
|
||||
int cost=dx*dx+dz*dz+2*Math.abs(y-preferred);
|
||||
if(cost<score){score=cost;best=new Room(x+dx,y,z+dz,rx,rz);}
|
||||
}
|
||||
return best; // Some seeds have no separate pocket here; retain the central chamber instead of forcing a room.
|
||||
}
|
||||
private static Plan create(AdventureBiomes180 s) {
|
||||
int y=floor(s,0,0,148,212,-1);
|
||||
// Some seeds have their central cave below the historical search band.
|
||||
// Preserve every previously admitted floor; widen only a failed search.
|
||||
if(y<0&&s.field().coherent())y=floor(s,0,0,48,240,-1);
|
||||
// A seed can have solid rock at the origin without an existing cave floor.
|
||||
// Keep admitted layouts identical; only sculpt a local chamber when needed.
|
||||
if(y<0&&s.polish197())y=supportedChamber(s);
|
||||
if(y<0&&s.stableGallery198())y=GalleryFloor198.choose((x,h,z)->rock(s,x,h,z));
|
||||
if(y<0)throw new IllegalStateException("No supported central gallery floor");
|
||||
var rooms=new ArrayList<Room>();rooms.add(new Room(0,y,0,19,16));
|
||||
if(s.field().coherent()){
|
||||
var north=satellite(s,-34,-26,y,y-6,10,8);if(north!=null)rooms.add(north);
|
||||
var south=satellite(s,-38,24,y,y-16,12,10);if(south!=null)rooms.add(south);
|
||||
}else{
|
||||
rooms.add(new Room(-34,floor(s,-34,-26,y-18,y+8,y-6),-26,10,8));
|
||||
rooms.add(new Room(-38,floor(s,-38,24,y-30,y+8,y-16),24,12,10));
|
||||
}
|
||||
for(var room:rooms)if(room.y<0)throw new IllegalStateException("No supported central gallery floor at "+room.x+","+room.z);
|
||||
var cells=new LinkedHashMap<Long,Cell>();
|
||||
for(var r:rooms)for(int x=r.x-r.rx-1;x<=r.x+r.rx+1;x++)for(int z=r.z-r.rz-1;z<=r.z+r.rz+1;z++) {
|
||||
double u=(x-r.x)/(double)r.rx,v=(z-r.z)/(double)r.rz;
|
||||
double d=u*u+v*v+.07*Math.sin(x*.6)*Math.cos(z*.4);
|
||||
if(d>1)continue;
|
||||
double amplitude=Math.clamp((Math.hypot(x-r.x,z-r.z)-5)/4,0,1);
|
||||
int ground=r.y+(int)Math.round(amplitude*(Math.sin(x*.19)*1.1+Math.sin(z*.23)));
|
||||
int ceiling=r.y+5+(int)(13*Math.sqrt(Math.max(0,1-d)));
|
||||
cells.put(key(x,z),new Cell(x,ground,z,ceiling,false));
|
||||
}
|
||||
var route=new ArrayList<BlockPos>();
|
||||
if(rooms.size()==3){
|
||||
var a=rooms.get(1);var b=rooms.get(2);
|
||||
link(cells,route,rooms.get(0),a,new int[][]{{0,0},{-8,-12},{-23,-17},{a.x,a.z}});
|
||||
link(cells,route,a,b,new int[][]{{a.x,a.z},{-49,-16},{-48,5},{b.x,b.z}});
|
||||
link(cells,route,b,rooms.get(0),new int[][]{{b.x,b.z},{-25,20},{-12,8},{0,0}});
|
||||
}else if(rooms.size()==2){
|
||||
var a=rooms.get(1);link(cells,route,rooms.getFirst(),a,new int[][]{{0,0},{a.x/2,a.z/2},{a.x,a.z}});
|
||||
}
|
||||
return new Plan(List.copyOf(rooms),Map.copyOf(cells),List.copyOf(route));
|
||||
}
|
||||
private static int supportedChamber(AdventureBiomes180 s) {
|
||||
int best=-1,bestScore=Integer.MIN_VALUE,top=s.field().surface(0,0);
|
||||
for(int y=32;y<=Math.min(240,top-22);y++) {
|
||||
if(!rock(s,0,y,0)||!rock(s,0,y-3,0))continue;
|
||||
int score=-Math.abs(y-164)/3;
|
||||
for(int x=-16;x<=16;x+=8)for(int z=-12;z<=12;z+=6) {
|
||||
if(rock(s,x,y-3,z))score+=4;
|
||||
if(rock(s,x,y+20,z))score+=2;
|
||||
if(!rock(s,x,y+5,z))score++;
|
||||
}
|
||||
if(score>bestScore){bestScore=score;best=y;}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
private static void link(Map<Long,Cell> cells,List<BlockPos> route,Room a,Room b,int[][] turns) {
|
||||
var line=new ArrayList<int[]>();
|
||||
for(int i=1;i<turns.length;i++) {
|
||||
int x=turns[i-1][0],z=turns[i-1][1],tx=turns[i][0],tz=turns[i][1];
|
||||
int dx=Math.abs(tx-x),dz=Math.abs(tz-z),sx=Integer.signum(tx-x),sz=Integer.signum(tz-z),err=dx-dz;
|
||||
while(x!=tx||z!=tz){line.add(new int[]{x,z});int twice=2*err;if(twice> -dz){err-=dz;x+=sx;}if(twice<dx){err+=dx;z+=sz;}}
|
||||
}
|
||||
line.add(new int[]{b.x,b.z});
|
||||
for(int i=0;i<line.size();i++) {
|
||||
var p=line.get(i);int y=(int)Math.round(a.y+(b.y-a.y)*i/(double)(line.size()-1));
|
||||
route.add(new BlockPos(p[0],y+1,p[1]));
|
||||
for(int dx=-2;dx<=2;dx++)for(int dz=-2;dz<=2;dz++) {
|
||||
if(dx*dx+dz*dz>5)continue;
|
||||
int x=p[0]+dx,z=p[1]+dz;var old=cells.get(key(x,z));
|
||||
// Keep the first corridor floor at self-overlaps, guaranteeing a stable shared seam.
|
||||
if(old!=null&&old.path)continue;
|
||||
int roof=Math.max(old==null?y+6:old.ceiling,y+5+(dx==0||dz==0?2:0));
|
||||
cells.put(key(x,z),new Cell(x,y,z,roof,true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void set(ChunkAccess c,BlockPos p,Block b){c.setBlockState(p,b.defaultBlockState());}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk) {
|
||||
if(!WorldgenLab.discovery183(generator)&&!WorldgenLab.ascent184(generator)&&!WorldgenLab.stem185(generator)&&!WorldgenLab.anchors186(generator)&&!WorldgenLab.natural187(generator)&&!WorldgenLab.details188(generator)&&!WorldgenLab.finishedTerrain(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
int margin=source.field().coherent()?32:0;
|
||||
if(chunk.getPos().getMaxBlockX()< -54-margin||chunk.getPos().getMinBlockX()>20
|
||||
||chunk.getPos().getMaxBlockZ()< -36-margin||chunk.getPos().getMinBlockZ()>35+margin)return;
|
||||
var p=plan(source);
|
||||
for(var c:p.cells.values()) {
|
||||
var at=new BlockPos(c.x,c.y,c.z);if(!chunk.getPos().contains(at))continue;
|
||||
for(int y=c.y-2;y<=c.ceiling;y++) {
|
||||
var pos=new BlockPos(c.x,y,c.z);
|
||||
if(y>c.y){set(chunk,pos,Blocks.AIR);continue;}
|
||||
// Sculpt only this floor's local foundation, retaining minerals elsewhere.
|
||||
Block stone=y<c.y?Blocks.TUFF:Math.floorMod(c.x*11+c.z*7,19)<3?Blocks.MOSS_BLOCK:Blocks.ANDESITE;
|
||||
if(Math.abs(c.x)<=2&&Math.abs(c.z)<=2)stone=Blocks.POLISHED_TUFF;
|
||||
set(chunk,pos,stone);
|
||||
}
|
||||
// Vertical seams colour existing stone; there is no box-shaped lining.
|
||||
for(int[] d:new int[][]{{1,0},{-1,0},{0,1},{0,-1}}) {
|
||||
int wx=c.x+d[0],wz=c.z+d[1];if(p.cells.containsKey(key(wx,wz)))continue;
|
||||
for(int y=c.y+1;y<c.ceiling;y++) {
|
||||
var wall=new BlockPos(wx,y,wz);if(!chunk.getPos().contains(wall))continue;
|
||||
if(!AdventureCaves180.natural(chunk.getBlockState(wall)))continue;
|
||||
if(Math.floorMod(wx*3+wz*5+y/4,13)<2)set(chunk,wall,Blocks.CALCITE);
|
||||
else if(y==c.y+1&&Math.floorMod(wx+wz,4)==0)set(chunk,wall,Blocks.MOSS_BLOCK);
|
||||
else if(y==c.y+3&&Math.floorMod(wx+wz,7)==0)set(chunk,wall,Blocks.CHISELED_TUFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
int y=p.rooms.getFirst().y;
|
||||
// Horizontal 5 x 5 rim, dry 3 x 3 recess exactly one block below the walking plane.
|
||||
for(int x=-2;x<=2;x++)for(int z=-2;z<=2;z++) {
|
||||
boolean rim=Math.abs(x)==2||Math.abs(z)==2;
|
||||
putIfOwned(chunk,x,y,z,rim?Blocks.OBSIDIAN:Blocks.AIR);
|
||||
putIfOwned(chunk,x,y-1,z,Blocks.POLISHED_DEEPSLATE);
|
||||
putIfOwned(chunk,x,y+1,z,Blocks.AIR);
|
||||
putIfOwned(chunk,x,y+2,z,Blocks.AIR);
|
||||
}
|
||||
for(int[] b:new int[][]{{-6,-4},{6,-4},{-5,5},{5,5}}) {
|
||||
var cell=p.cells.get(key(b[0],b[1]));
|
||||
putIfOwned(chunk,b[0],cell.y-1,b[1],Blocks.SEA_LANTERN);
|
||||
putIfOwned(chunk,b[0],cell.y,b[1],Blocks.COPPER_GRATE.waxed().oxidized());
|
||||
}
|
||||
}
|
||||
private static void putIfOwned(ChunkAccess chunk,int x,int y,int z,Block b) {
|
||||
var p=new BlockPos(x,y,z);if(chunk.getPos().contains(p))set(chunk,p,b);
|
||||
}
|
||||
|
||||
static Map<String,Object> check(ServerLevel level) {
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).getBiomeSource();var p=plan(source);
|
||||
var chunks=new HashSet<net.minecraft.world.level.ChunkPos>();
|
||||
for(var c:p.cells.values())chunks.add(new net.minecraft.world.level.ChunkPos(c.x>>4,c.z>>4));
|
||||
for(var cp:chunks)level.getChunk(cp.x(),cp.z());
|
||||
int cy=p.rooms.getFirst().y;
|
||||
for(int x=-2;x<=2;x++)for(int z=-2;z<=2;z++) {
|
||||
boolean rim=Math.abs(x)==2||Math.abs(z)==2;var at=new BlockPos(x,cy,z);
|
||||
if(!level.getBlockState(at).is(rim?Blocks.OBSIDIAN:Blocks.AIR)
|
||||
||!level.getBlockState(at.below()).is(Blocks.POLISHED_DEEPSLATE)
|
||||
||!level.getBlockState(at.above()).isAir()||!level.getBlockState(at.above(2)).isAir())
|
||||
throw new IllegalStateException("Incorrect horizontal portal at "+at);
|
||||
}
|
||||
// Follow the actual saved surfaces across chunk boundaries, not just the planned centreline.
|
||||
var floors=new HashMap<Long,Integer>();
|
||||
for(var c:p.cells.values()) {
|
||||
if(Math.abs(c.x)<=1&&Math.abs(c.z)<=1)continue;
|
||||
int actual=Integer.MIN_VALUE;
|
||||
for(int y=c.y+2;y>=c.y-2;y--) {
|
||||
var at=new BlockPos(c.x,y,c.z);
|
||||
if(!level.getBlockState(at).getCollisionShape(level,at).isEmpty()&&level.getBlockState(at.above()).isAir()&&level.getBlockState(at.above(2)).isAir()){actual=y;break;}
|
||||
}
|
||||
if(actual!=Integer.MIN_VALUE)floors.put(key(c.x,c.z),actual);
|
||||
}
|
||||
long start=key(2,0);if(!floors.containsKey(start))throw new IllegalStateException("No gallery approach");
|
||||
var visited=new HashSet<Long>();var queue=new ArrayDeque<Long>();queue.add(start);
|
||||
while(!queue.isEmpty()) {
|
||||
long k=queue.removeFirst();if(!visited.add(k))continue;int x=(int)(k>>32),z=(int)k;
|
||||
for(int[] d:new int[][]{{1,0},{-1,0},{0,1},{0,-1}}) {
|
||||
long next=key(x+d[0],z+d[1]);var y=floors.get(next);
|
||||
if(y!=null&&Math.abs(y-floors.get(k))<=1&&!visited.contains(next))queue.add(next);
|
||||
}
|
||||
}
|
||||
for(var r:p.rooms)if(!visited.contains(key(r.x+2,r.z)))throw new IllegalStateException("Disconnected gallery room: "+r);
|
||||
if(visited.size()<floors.size()*.9)throw new IllegalStateException("Too many isolated gallery surfaces: "+visited.size()+"/"+floors.size());
|
||||
return Map.of("horizontalPortal",new BlockPos(0,cy,0).toShortString(),"rooms",p.rooms,"walkableCells",visited.size(),"plannedColumns",p.cells.size());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.Identifier;
|
||||
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;
|
||||
|
||||
/** Three seed-selected summit chunks, each owning exactly one successful native cherry tree. */
|
||||
public final class Cherry180 {
|
||||
private static final Map<AdventureBiomes180,List<BlockPos>> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private static final Map<AdventureBiomes180,Map<Long,BlockPos>> PLACED=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private Cherry180(){}
|
||||
public static List<BlockPos> sites(AdventureBiomes180 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,s->{
|
||||
if(s.field().coherent())return coherentSites(s);
|
||||
if(s.field().eroded())return s.field().erosion().crowns().stream()
|
||||
.map(c->new BlockPos(c.x(),s.field().erosion().surface(c.x(),c.z()),c.z())).toList();
|
||||
var candidates=new ArrayList<BlockPos>();int cx=s.field().peakX()>>4,cz=s.field().peakZ()>>4;
|
||||
for(int dx=-4;dx<=4;dx++)for(int dz=-4;dz<=4;dz++){
|
||||
int x=(cx+dx)*16+8,z=(cz+dz)*16+8,y=s.terrain(x,z).top();if(y>=270)candidates.add(new BlockPos(x,y,z));
|
||||
}
|
||||
candidates.sort(Comparator.<BlockPos>comparingInt(BlockPos::getY).reversed());var chosen=new ArrayList<BlockPos>();
|
||||
for(var p:candidates)if(chosen.stream().allMatch(q->q.distSqr(p)>24*24)){chosen.add(p);if(chosen.size()==3)break;}
|
||||
if(chosen.size()!=3)throw new IllegalStateException("No three spacious summit sites");return List.copyOf(chosen);
|
||||
});}}
|
||||
private static List<BlockPos> coherentSites(AdventureBiomes180 s){
|
||||
var chosen=new ArrayList<BlockPos>();var water=Water189.plan(s).columns();
|
||||
for(var crown:s.field().erosion().crowns()){
|
||||
var candidates=new ArrayList<BlockPos>();
|
||||
for(int dx=-40;dx<=40;dx+=2)for(int dz=-40;dz<=40;dz+=2){
|
||||
int x=crown.x()+dx,z=crown.z()+dz,y=s.field().erosion().surface(x,z);
|
||||
if(y<250||s.field().erosion().slope(x,z)>.75||Math.abs(y-crown.height())>14)continue;
|
||||
var p=new BlockPos(x,y,z);
|
||||
if(chosen.stream().anyMatch(q->(q.getX()>>4)==(x>>4)&&(q.getZ()>>4)==(z>>4)||q.distSqr(p)<24*24))continue;
|
||||
if(Anchors189.treeBlocked(s,p.above()))continue;
|
||||
boolean clear=true;
|
||||
for(int ox=-5;ox<=5&&clear;ox++)for(int oz=-5;oz<=5&&clear;oz++){
|
||||
int top=water.getOrDefault(((long)(x+ox)<<32)^((z+oz)&0xffffffffL),-1);
|
||||
if(top>=0&&y>=top-30&&y<=top+28)clear=false;
|
||||
}
|
||||
for(int ox=-4;ox<=4&&clear;ox+=4)for(int oz=-4;oz<=4&&clear;oz+=4)
|
||||
for(int h=3;h<=11;h+=2)if(s.field().sampleValue(net.minecraft.world.level.levelgen.densityfunction.SamplerContext.EMPTY_UNCACHED,x+ox,y+h,z+oz)>0)clear=false;
|
||||
if(clear)candidates.add(p);
|
||||
}
|
||||
candidates.sort(Comparator.comparingDouble(p->p.distSqr(new BlockPos(crown.x(),(int)crown.height(),crown.z()))));
|
||||
if(candidates.isEmpty())throw new IllegalStateException("No dry, open summit tree site near "+crown);
|
||||
chosen.add(candidates.getFirst());
|
||||
}
|
||||
return List.copyOf(chosen);
|
||||
}
|
||||
public static List<BlockPos> trees(AdventureBiomes180 source){synchronized(PLACED){return List.copyOf(PLACED.getOrDefault(source,Map.of()).values());}}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
if(sites(source).stream().noneMatch(c.getPos()::contains))return;
|
||||
var random=RandomSource.create(level.getSeed()^c.getPos().pack()^0x180C4EL);
|
||||
var feature=level.registryAccess().lookupOrThrow(Registries.FEATURE).getValue(Identifier.withDefaultNamespace("cherry_bees_005"));
|
||||
var positions=new ArrayList<BlockPos>();
|
||||
if(source.field().eroded()){
|
||||
var center=sites(source).stream().filter(c.getPos()::contains).findFirst().orElseThrow();
|
||||
for(int dx=0;dx<16;dx++)for(int dz=0;dz<16;dz++)
|
||||
positions.add(new BlockPos(c.getPos().getMinBlockX()+dx,center.getY(),c.getPos().getMinBlockZ()+dz));
|
||||
positions.sort(Comparator.comparingDouble(p->p.distSqr(center)));
|
||||
}else for(int attempt=0;attempt<49;attempt++)
|
||||
positions.add(new BlockPos(c.getPos().getMinBlockX()+5+attempt%7,0,c.getPos().getMinBlockZ()+5+attempt/7));
|
||||
for(var candidate:positions){
|
||||
int x=candidate.getX(),z=candidate.getZ();
|
||||
if(source.field().eroded() && source.field().erosion().slope(x,z)>.75)continue;
|
||||
for(int y=315;y>=(source.field().coherent()?250:270);y--){
|
||||
var p=new BlockPos(x,y,z);if(!AdventureCaves180.natural(c.getBlockState(p))||!c.getBlockState(p.above()).isAir())continue;
|
||||
c.setBlockState(p,Blocks.GRASS_BLOCK.defaultBlockState());
|
||||
if(feature.place(level,generator,random,p.above())){synchronized(PLACED){PLACED.computeIfAbsent(source,s->new HashMap<>()).put(c.getPos().pack(),p.above());}return;}break;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unable to grow the reserved summit cherry in "+c.getPos());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Shared spatial checks for independent decorators; diagnostics run only in the laboratory. */
|
||||
final class Continuity193 {
|
||||
static boolean touches(Set<BlockPos> a,Set<BlockPos> b){
|
||||
if(a.size()>b.size())return touches(b,a);
|
||||
for(var p:a){if(b.contains(p))return true;for(var d:Direction.values())if(b.contains(p.relative(d)))return true;}
|
||||
return false;
|
||||
}
|
||||
static boolean covered(ReliefEcology177.Field f,int x,int y,int z){
|
||||
int roof=0;
|
||||
for(int[] d:new int[][]{{0,0},{4,0},{-4,0},{0,4},{0,-4}}){
|
||||
for(int dy=4;dy<=40;dy+=4)if(f.sampleValue(SamplerContext.EMPTY_UNCACHED,x+d[0],y+dy,z+d[1])>0){roof++;break;}
|
||||
}
|
||||
return roof>=3;
|
||||
}
|
||||
record Water(String kind,BlockPos seed,int surface,Set<BlockPos> cells){}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 s){
|
||||
var pools=new ArrayList<Water>();
|
||||
for(var p:NaturalWater184.plan(s).pools())pools.add(new Water("cavity",p.seed(),p.surface(),p.cells()));
|
||||
for(var p:SurfaceWater187.plan(s).pools())pools.add(new Water("surface",p.seed(),p.surface(),p.cells()));
|
||||
int pairs=0,cells=0;var details=new ArrayList<Map<String,Object>>();
|
||||
for(int i=0;i<pools.size();i++){
|
||||
var p=pools.get(i);
|
||||
for(int j=i+1;j<pools.size();j++){pairs++;if(touches(p.cells,pools.get(j).cells))throw new IllegalStateException("Independent water bodies touch: "+p.seed+" / "+pools.get(j).seed);}
|
||||
var chunks=new HashSet<net.minecraft.world.level.ChunkPos>();
|
||||
for(var q:p.cells)chunks.add(new net.minecraft.world.level.ChunkPos(q.getX()>>4,q.getZ()>>4));
|
||||
for(var cp:chunks)level.getChunk(cp.x(),cp.z());
|
||||
int empty=0;var missing=new TreeMap<String,Integer>();
|
||||
for(var q:p.cells){cells++;if(level.getFluidState(q).isEmpty()){empty++;missing.merge(level.getBlockState(q).toString(),1,Integer::sum);}}
|
||||
if(empty!=0)throw new IllegalStateException("Planned lake lost "+empty+" water cells at "+p.seed+" "+missing);
|
||||
details.add(Map.of("kind",p.kind,"seed",p.seed.toShortString(),"surface",p.surface,"cells",p.cells.size()));
|
||||
}
|
||||
int naturalFloors=0,aerial=0;var anchors=Anchors189.plan(s,s.seed());
|
||||
for(var site:anchors.sites())if(site.kind()!=Anchors189.Kind.CAVE){
|
||||
if(site.kind()==Anchors189.Kind.SKY)aerial++;
|
||||
var centre=site.floor();
|
||||
for(var chunk:anchors.chunks().values())for(var e:chunk.entrySet()){
|
||||
var p=e.getKey();if(Math.abs(p.getX()-centre.getX())>10||Math.abs(p.getZ()-centre.getZ())>10||Math.abs(p.getY()-centre.getY())>16)continue;
|
||||
if(e.getValue().is(Blocks.MOSS_BLOCK))throw new IllegalStateException("Moss checkerboard in exposed anchor "+p);
|
||||
if(e.getValue().is(Blocks.GRASS_BLOCK)){
|
||||
var f=s.field();if(f.sampleValue(SamplerContext.EMPTY_UNCACHED,p.getX(),p.getY(),p.getZ())<=0||f.sampleValue(SamplerContext.EMPTY_UNCACHED,p.getX(),p.getY()+1,p.getZ())>0)throw new IllegalStateException("Anchor floor detached from density "+p);
|
||||
naturalFloors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(aerial==0||naturalFloors==0)throw new IllegalStateException("Missing supported aerial refuge");
|
||||
return Map.of("waterBodies",details,"disjointPairsChecked",pairs,"waterCellsChecked",cells,"overlappingWaterBodies",0,"exposedAnchorMoss",0,"anchorFloorsOnNaturalSurface",naturalFloors,"aerialAnchors",aerial);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
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.densityfunction.SamplerContext;
|
||||
|
||||
/** Local repairs for new beta.197 worlds. No mutation of historical profiles. */
|
||||
public final class Details197 {
|
||||
private Details197() {}
|
||||
private static boolean rock(AdventureBiomes180 s,BlockPos p) {
|
||||
return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,p.getX(),p.getY(),p.getZ())>0;
|
||||
}
|
||||
static boolean openCliff(AdventureBiomes180 s,BlockPos lip,Direction outward) {
|
||||
// A void below a bank can be a cave, with soil above it. Require open sky
|
||||
// and a clear outward face for the first eight blocks of the curtain.
|
||||
for(int y=lip.getY()+3;y<=319;y++)
|
||||
if(rock(s,new BlockPos(lip.getX(),y,lip.getZ())))return false;
|
||||
for(int down=1;down<=8;down++)for(int forward=0;forward<=2;forward++)
|
||||
if(rock(s,lip.below(down).relative(outward,forward)))return false;
|
||||
return true;
|
||||
}
|
||||
public static BlockPos islandSpawn(ServerLevel level,int x,int z) {
|
||||
for(int y=316;y>=198;y--) {
|
||||
var floor=new BlockPos(x,y,z);var state=level.getBlockState(floor);
|
||||
if(state.is(BlockTags.LEAVES)||state.is(BlockTags.LOGS)||!state.isSolidRender())continue;
|
||||
if(level.getBlockState(floor.above()).isAir()&&level.getBlockState(floor.above(2)).isAir())return floor.above();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static int roofHeight(int x) {return 8-Math.abs(x);}
|
||||
static boolean hutFits(AdventureBiomes180 s,BlockPos p) {
|
||||
for(int x=-4;x<=4;x++)for(int z=-5;z<=5;z++) {
|
||||
int roof=roofHeight(x);
|
||||
for(int y=2;y<=roof+2;y++)if(rock(s,p.offset(x,y,z)))return false;
|
||||
}
|
||||
for(int x:new int[]{-3,3})for(int z:new int[]{-4,4}) {
|
||||
boolean support=false;
|
||||
for(int depth=0;depth<=8;depth++)if(rock(s,p.offset(x,-depth,z))){support=true;break;}
|
||||
if(!support)return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private static BlockState roof(int x) {
|
||||
if(x==0)return Blocks.DARK_OAK_SLAB.defaultBlockState();
|
||||
return Blocks.DARK_OAK_STAIRS.defaultBlockState().setValue(StairBlock.FACING,x<0?Direction.EAST:Direction.WEST);
|
||||
}
|
||||
private static void put(ChunkAccess c,BlockPos p,BlockState state){c.setBlockState(p,state);}
|
||||
static void hut(ChunkAccess c,BlockPos origin) {
|
||||
var p=origin.above();
|
||||
for(int x=-4;x<=4;x++)for(int z=-5;z<=5;z++) {
|
||||
var floor=p.offset(x,0,z);if(!c.getPos().contains(floor))continue;
|
||||
int h=roofHeight(x);boolean corner=Math.abs(x)==3&&Math.abs(z)==4;
|
||||
for(int y=1;y<h;y++)put(c,floor.above(y),Blocks.AIR.defaultBlockState());
|
||||
if(Math.abs(x)<=3&&Math.abs(z)<=4) {
|
||||
put(c,floor,Blocks.SPRUCE_PLANKS.defaultBlockState());
|
||||
if(Math.abs(x)==3||Math.abs(z)==4)for(int y=1;y<h;y++) {
|
||||
if(z==-4&&x==0&&y<=2)continue;
|
||||
put(c,floor.above(y),(corner?Blocks.SPRUCE_LOG:y==2&&z==0?Blocks.GLASS:Blocks.SPRUCE_PLANKS).defaultBlockState());
|
||||
}
|
||||
}
|
||||
if(corner)for(int depth=1;depth<=9;depth++) {
|
||||
var at=floor.below(depth);if(AdventureCaves180.natural(c.getBlockState(at)))break;
|
||||
put(c,at,Blocks.SPRUCE_LOG.defaultBlockState());
|
||||
}
|
||||
put(c,floor.above(h),roof(x));
|
||||
if(z==-5&&Math.abs(x)<=2)put(c,floor,Blocks.SPRUCE_PLANKS.defaultBlockState());
|
||||
if(x==2&&z==2)put(c,floor.above(),Blocks.CAULDRON.defaultBlockState());
|
||||
if(x==-2&&z==2)put(c,floor.above(),Blocks.CRAFTING_TABLE.defaultBlockState());
|
||||
if(x==0&&z==3)put(c,floor.above(),Blocks.LANTERN.defaultBlockState());
|
||||
}
|
||||
}
|
||||
public static boolean protectsHut(ChunkGenerator g,BlockPos p) {
|
||||
if(!SharedIsland196.current(g)||p.getY()<80||p.getY()>240||Math.abs(p.getX())>230||Math.abs(p.getZ())>230)return false;
|
||||
var s=(AdventureBiomes180)((NoiseBasedChunkGenerator)g).getBiomeSource();var hut=Dungeon180.plan(s).hut();
|
||||
return hut!=null&&Math.abs(p.getX()-hut.getX())<=4&&Math.abs(p.getZ()-hut.getZ())<=5
|
||||
&&p.getY()>=hut.getY()-8&&p.getY()<=hut.getY()+10;
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level) {
|
||||
var s=(AdventureBiomes180)level.getChunkSource().getGenerator().getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();var hut=Dungeon180.plan(s).hut();
|
||||
if(hut!=null) {
|
||||
int roofBlocks=0;
|
||||
for(int x=-4;x<=4;x++)for(int z=-5;z<=5;z++) {
|
||||
var p=hut.offset(x,1+roofHeight(x),z);level.getChunk(p.getX()>>4,p.getZ()>>4);
|
||||
if(!level.getBlockState(p).equals(roof(x)))throw new IllegalStateException("Broken hut roof at "+p+": "+level.getBlockState(p));
|
||||
roofBlocks++;
|
||||
}
|
||||
for(int y=2;y<=3;y++)if(!level.getBlockState(hut.offset(0,y,-4)).isAir())throw new IllegalStateException("Blocked hut entrance");
|
||||
result.put("hut",hut.toShortString());result.put("intactRoofBlocks",roofBlocks);
|
||||
}else result.put("hut","no supported clear marsh site");
|
||||
int curtains=0;
|
||||
for(var outlet:Water189.plan(s).outlets()) {
|
||||
var d=Direction.getApproximateNearest(outlet.lip().getX()-outlet.start().getX(),0,outlet.lip().getZ()-outlet.start().getZ());
|
||||
if(!openCliff(s,outlet.lip(),d))throw new IllegalStateException("Buried waterfall mouth "+outlet);
|
||||
for(int h=1;h<=8;h++) {
|
||||
var p=outlet.lip().below(h);
|
||||
if(level.getFluidState(p).isEmpty())throw new IllegalStateException("Obstructed waterfall "+p);
|
||||
if(level.getBlockState(p.relative(d)).isSolidRender())throw new IllegalStateException("Waterfall inside cliff "+p);
|
||||
}
|
||||
curtains++;
|
||||
}
|
||||
result.put("exteriorWaterfallLanes",curtains);return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
|
||||
/** Entirely opt-in, chunk-owned discovery revision; no save migration or runtime rebuild. */
|
||||
public final class Discovery183 {
|
||||
private Discovery183() {}
|
||||
private static final Map<Station183.Layout,Map<BlockPos,BlockState>> STATIONS=new java.util.concurrent.ConcurrentHashMap<>();
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess chunk){
|
||||
if(!WorldgenLab.discovery183(generator))return;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Cavern183.place(level,generator,chunk);
|
||||
Sulfur183.place(source,chunk);
|
||||
Water183.place(source,chunk);
|
||||
var p=chunk.getPos();
|
||||
if(p.x()>=-1&&p.x()<=0&&p.z()>=-1&&p.z()<=0)
|
||||
Rosette183.plan(level.getSeed()).forEach((at,b)->{if(p.contains(at))chunk.setBlockState(at,b);});
|
||||
if(p.x()>=-3&&p.x()<=2&&p.z()>=-2&&p.z()<=1)
|
||||
STATIONS.computeIfAbsent(Station183.layout(generator),Station183::plan).forEach((at,b)->{if(p.contains(at))chunk.setBlockState(at,b);});
|
||||
StationAtlas183.populate(level,generator,chunk);
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level){
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)level.getChunkSource().getGenerator()).getBiomeSource();
|
||||
var result=new LinkedHashMap<String,Object>();
|
||||
result.put("gallery",Cavern183.check(level));result.put("rosette",Rosette183.check(level));
|
||||
result.put("sulfur",Sulfur183.check(level,source));result.put("upperWater",Water183.check(level,source));
|
||||
result.put("gemDistricts",Gems183.check(level));result.put("ISS",Station183.check(level));result.put("atlas",StationAtlas183.check(level));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
import java.util.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import net.minecraft.core.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.*;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.*;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
import net.minecraft.world.level.block.*;
|
||||
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
|
||||
import net.minecraft.world.level.block.state.properties.RailShape;
|
||||
import net.minecraft.world.level.chunk.*;
|
||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
import net.minecraft.world.level.storage.loot.LootTable;
|
||||
|
||||
/** Connected, seed-owned mine dungeon; native saved spawners and deferred minecart loot. */
|
||||
public final class Dungeon180 {
|
||||
public record Room(int id,int x,int y,int z,int rx,int rz,int kind){} // 0 hall, 1 combat, 2 cache
|
||||
public record Cell(int x,int y,int z,int dx,int dz,int step){}
|
||||
public record Plan(List<Room> rooms,Map<Long,Cell> paths,BlockPos hut,int exitX,int exitZ){}
|
||||
private static final Map<AdventureBiomes180,Plan> PLANS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
public static final ResourceKey<LootTable> LOOT=ResourceKey.create(Registries.LOOT_TABLE,Identifier.fromNamespaceAndPath("sanctuary_test","chests/mine_cache_180"));
|
||||
private Dungeon180(){}
|
||||
private static final Map<AdventureBiomes180,Map<Long,Integer>> FLOORS=Collections.synchronizedMap(new WeakHashMap<>());
|
||||
/** Native dripstone pools can replace a floor after this chunk's corridors were placed. */
|
||||
public static boolean protectsFloor(ChunkGenerator generator,BlockPos pos){
|
||||
if(SharedIsland196.size(generator)==null||pos.getY()<80||pos.getY()>230||Math.abs(pos.getZ())>180)return false;
|
||||
var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();
|
||||
Map<Long,Integer> floors;
|
||||
synchronized(FLOORS){floors=FLOORS.computeIfAbsent(source,s->{
|
||||
var p=plan(s);var m=new HashMap<Long,Integer>();
|
||||
for(var cell:p.paths().values())for(int offset=-1;offset<=1;offset++){
|
||||
int x=cell.x()+(cell.dz()!=0?offset:0),z=cell.z()+(cell.dx()!=0?offset:0);
|
||||
m.putIfAbsent(key(x,z),cell.y());
|
||||
}
|
||||
// Centre lines and rooms own their floor even where the widened corridors overlap.
|
||||
for(var cell:p.paths().values())m.put(key(cell.x(),cell.z()),cell.y());
|
||||
for(var room:p.rooms())for(int x=room.x()-room.rx()-1;x<=room.x()+room.rx()+1;x++)
|
||||
for(int z=room.z()-room.rz()-1;z<=room.z()+room.rz()+1;z++){
|
||||
var owner=roomAt(p,x,z);if(owner!=null)m.put(key(x,z),owner.y());
|
||||
}
|
||||
return Map.copyOf(m);
|
||||
});}
|
||||
var y=floors.get(key(pos.getX(),pos.getZ()));return y!=null&&y==pos.getY();
|
||||
}
|
||||
static long key(int x,int z){return ((long)x<<32)^(z&0xffffffffL);}
|
||||
private static boolean rock(AdventureBiomes180 s,int x,int y,int z){return s.field().sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0;}
|
||||
public static Plan plan(AdventureBiomes180 source){synchronized(PLANS){return PLANS.computeIfAbsent(source,Dungeon180::create);}}
|
||||
private static Plan create(AdventureBiomes180 s){
|
||||
BlockPos origin=null;int best=-1;
|
||||
for(int x=96;x<=192;x+=24)for(int z=-72;z<=72;z+=24)for(int y=Math.min(208,s.terrain(x,z).top()-20);y>=112;y-=2){
|
||||
if(!rock(s,x,y,z)||rock(s,x,y+3,z)||rock(s,x,y+9,z))continue;
|
||||
int score=0;for(int a=-6;a<=6;a+=3)for(int b=-6;b<=6;b+=3)if(!rock(s,x+a,y+4,z+b))score++;
|
||||
if(score>best){best=score;origin=new BlockPos(x,y,z);}break;
|
||||
}
|
||||
if(origin==null)throw new IllegalStateException("No covered dungeon entrance chamber");
|
||||
int[][] layout={{0,0,0,0},{-28,8,0,0},{-52,-16,-2,1},{-76,-12,-2,2},{-24,-36,3,1},{4,-52,3,2},{32,-28,0,0},{44,4,-2,1},{16,36,0,1},{-16,52,-3,2},{-44,28,0,0},{-72,52,2,1},{-96,20,2,2}};
|
||||
var rooms=new ArrayList<Room>();
|
||||
int mirror=((s.field().peakZ()&8)==0)?1:-1;
|
||||
for(int i=0;i<layout.length;i++){var a=layout[i];rooms.add(new Room(i,origin.getX()+a[0],origin.getY()+a[2],origin.getZ()+mirror*a[1],a[3]==0?8:6,a[3]==0?7:5,a[3]));}
|
||||
int[][] edges={{0,1},{1,2},{2,3},{1,4},{4,5},{5,6},{0,6},{6,7},{0,8},{8,9},{9,10},{10,1},{10,11},{11,12},{12,3},{8,7}};
|
||||
var paths=new LinkedHashMap<Long,Cell>();
|
||||
for(int i=0;i<edges.length;i++){var a=rooms.get(edges[i][0]);var b=rooms.get(edges[i][1]);link(paths,a.x(),a.y(),a.z(),b.x(),b.y(),b.z(),i%2==0);}
|
||||
int exitX=origin.getX()+36,exitZ=origin.getZ()+20*mirror;
|
||||
while(exitX<(int)(360*s.field().base().spread())&&s.terrain(exitX,exitZ).top()>origin.getY()+4)exitX+=4;
|
||||
// Daylight access bends twice instead of forming one unbroken tunnel.
|
||||
link(paths,origin.getX(),origin.getY(),origin.getZ(),origin.getX()+28,origin.getY(),exitZ,false);
|
||||
link(paths,origin.getX()+28,origin.getY(),exitZ,exitX+3,origin.getY(),exitZ,true);
|
||||
BlockPos hut=null;best=-1;
|
||||
for(int x=-224;x<=224;x+=16)for(int z=-224;z<=224;z+=16)for(int y=Math.min(216,s.terrain(x,z).top()-20);y>=96;y-=2){
|
||||
if(!AdventureCaves180.biome(s,x,y,z).equals("adventure_marsh")||!rock(s,x,y,z)||rock(s,x,y+1,z)||rock(s,x,y+8,z))continue;
|
||||
int n=0;for(int a=-4;a<=4;a+=2)for(int b=-4;b<=4;b+=2)if(!rock(s,x+a,y+5,z+b)&&rock(s,x+a,y-3,z+b))n++;
|
||||
if(n>best && (!s.polish197() || Details197.hutFits(s,new BlockPos(x,y,z)))){best=n;hut=new BlockPos(x,y,z);}break;
|
||||
}
|
||||
return new Plan(List.copyOf(rooms),Map.copyOf(paths),hut,exitX+3,exitZ);
|
||||
}
|
||||
private static void link(Map<Long,Cell> map,int x,int y,int z,int tx,int ty,int tz,boolean xFirst){
|
||||
int length=Math.abs(tx-x)+Math.abs(tz-z),startY=y;
|
||||
for(int step=0;step<=length;step++){
|
||||
int dx=0,dz=0;
|
||||
if(xFirst?x!=tx:z==tz){dx=Integer.signum(tx-x);}else dz=Integer.signum(tz-z);
|
||||
if(dx==0&&dz==0){if(x!=tx)dx=Integer.signum(tx-x);else dz=Integer.signum(tz-z);}
|
||||
int floor=startY+(int)Math.round((ty-startY)*step/(double)Math.max(1,length));
|
||||
map.putIfAbsent(key(x,z),new Cell(x,floor,z,dx,dz,step));x+=dx;z+=dz;
|
||||
}
|
||||
}
|
||||
public static boolean near(AdventureBiomes180 s,int x,int z,int margin){
|
||||
var p=plan(s);if(p.hut()!=null&&Math.hypot(x-p.hut().getX(),z-p.hut().getZ())<margin+9)return true;
|
||||
for(var r:p.rooms())if(Math.hypot(x-r.x(),z-r.z())<margin+10)return true;
|
||||
for(var c:p.paths().values())if(Math.abs(c.x()-x)<margin&&Math.abs(c.z()-z)<margin)return true;
|
||||
return false;
|
||||
}
|
||||
public static boolean protectedAt(AdventureBiomes180 s,int x,int y,int z){
|
||||
var p=plan(s);
|
||||
if(p.hut()!=null&&Math.abs(x-p.hut().getX())<=7&&Math.abs(z-p.hut().getZ())<=7&&Math.abs(y-p.hut().getY())<10)return true;
|
||||
for(var r:p.rooms())if(Math.abs(y-r.y())<13&&Math.abs(x-r.x())<=r.rx()+3&&Math.abs(z-r.z())<=r.rz()+3)return true;
|
||||
for(int dx=-3;dx<=3;dx++)for(int dz=-3;dz<=3;dz++){var c=p.paths().get(key(x+dx,z+dz));if(c!=null&&y>=c.y()-2&&y<=c.y()+6)return true;}
|
||||
return false;
|
||||
}
|
||||
static Room roomAt(Plan p,int x,int z){
|
||||
for(var r:p.rooms())if((x-r.x())*(double)(x-r.x())/(r.rx()*r.rx())+(z-r.z())*(double)(z-r.z())/(r.rz()*r.rz())<=1.08)return r;return null;
|
||||
}
|
||||
private static void set(ChunkAccess c,int x,int y,int z,Block b){c.setBlockState(new BlockPos(x,y,z),b.defaultBlockState());}
|
||||
public static void place(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();var p=plan(source);
|
||||
for(int x=c.getPos().getMinBlockX();x<=c.getPos().getMaxBlockX();x++)for(int z=c.getPos().getMinBlockZ();z<=c.getPos().getMaxBlockZ();z++){
|
||||
var r=roomAt(p,x,z);
|
||||
if(r!=null){
|
||||
double dist=Math.pow((x-r.x())/(double)r.rx(),2)+Math.pow((z-r.z())/(double)r.rz(),2);
|
||||
int h=4+(int)(4*Math.sqrt(Math.max(0,1-dist)));
|
||||
boolean ground=AdventureCaves180.natural(c.getBlockState(new BlockPos(x,r.y(),z)));
|
||||
set(c,x,r.y(),z,ground?(source.polish197()
|
||||
? (EcologyBiomes176.noise(source.seed()^0x197D17L,x/9.0,z/9.0)>.32?Blocks.COARSE_DIRT:Blocks.STONE)
|
||||
: (Math.floorMod(x*31+z*17,7)==0?Blocks.COARSE_DIRT:Blocks.STONE)):Blocks.DARK_OAK_PLANKS);
|
||||
for(int dy=1;dy<=h;dy++)set(c,x,r.y()+dy,z,Blocks.AIR);
|
||||
if(r.kind()==1&&!c.getBlockState(new BlockPos(x,r.y()+h+1,z)).isSolidRender())set(c,x,r.y()+h+1,z,Blocks.MOSSY_COBBLESTONE);
|
||||
if(r.kind()==2&&x==r.x()+3&&z==r.z()){
|
||||
set(c,x,r.y()+1,z,Blocks.BARREL);set(c,x,r.y()+2,z,Blocks.LANTERN);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Cell cell=p.paths().get(key(x,z));int offset=0;
|
||||
for(int a=-2;a<=2&&cell==null;a++)for(int b=-2;b<=2;b++){
|
||||
var q=p.paths().get(key(x+a,z+b));if(q!=null&&((q.dx()!=0&&a==0)||(q.dz()!=0&&b==0))){cell=q;offset=Math.abs(a)+Math.abs(b);break;}
|
||||
}
|
||||
if(cell==null)continue;
|
||||
int y=cell.y();boolean border=offset==2,frame=cell.step()%7==0;
|
||||
if(!border){
|
||||
if(!AdventureCaves180.natural(c.getBlockState(new BlockPos(x,y,z))))set(c,x,y,z,Blocks.DARK_OAK_PLANKS);
|
||||
for(int dy=1;dy<=4;dy++)set(c,x,y+dy,z,Blocks.AIR);
|
||||
if(frame)set(c,x,y+4,z,Blocks.DARK_OAK_LOG);
|
||||
if(offset==0&&cell.step()%28==0)c.setBlockState(new BlockPos(x,y+3,z),Blocks.LANTERN.defaultBlockState().setValue(LanternBlock.HANGING,true));
|
||||
if(offset==0&&cell.step()%7!=0&&cell.dx()!=0)c.setBlockState(new BlockPos(x,y+1,z),Blocks.RAIL.defaultBlockState().setValue(RailBlock.SHAPE,RailShape.EAST_WEST));
|
||||
}else if(frame){for(int dy=0;dy<=4;dy++)set(c,x,y+dy,z,Blocks.DARK_OAK_LOG);}
|
||||
}
|
||||
if(p.hut()!=null){if(source.polish197())Details197.hut(c,p.hut());else hut(c,p.hut());}
|
||||
}
|
||||
private static void hut(ChunkAccess c,BlockPos p){
|
||||
for(int x=Math.max(c.getPos().getMinBlockX(),p.getX()-4);x<=Math.min(c.getPos().getMaxBlockX(),p.getX()+4);x++)for(int z=Math.max(c.getPos().getMinBlockZ(),p.getZ()-5);z<=Math.min(c.getPos().getMaxBlockZ(),p.getZ()+5);z++){
|
||||
int u=x-p.getX(),v=z-p.getZ(),y=p.getY()+1;boolean corner=Math.abs(u)==3&&Math.abs(v)==4;
|
||||
if(corner)for(int dy=-4;dy<=3;dy++)set(c,x,y+dy,z,Blocks.SPRUCE_LOG);
|
||||
if(Math.abs(u)<=3&&Math.abs(v)<=4){set(c,x,y,z,Blocks.SPRUCE_PLANKS);for(int dy=1;dy<=4;dy++)set(c,x,y+dy,z,Blocks.AIR);
|
||||
if((Math.abs(u)==3||Math.abs(v)==4)&&!(v==-4&&u==0))for(int dy=1;dy<=3;dy++)set(c,x,y+dy,z,corner?Blocks.SPRUCE_LOG:dy==2&&u!=0&&v==0?Blocks.GLASS_PANE:Blocks.SPRUCE_PLANKS);
|
||||
}
|
||||
set(c,x,y+4+(4-Math.abs(u))/2,z,Blocks.DARK_OAK_SLAB);
|
||||
if(v==-5&&Math.abs(u)<=2)set(c,x,y,z,Blocks.SPRUCE_PLANKS);
|
||||
if(u==2&&v==2)set(c,x,y+1,z,Blocks.CAULDRON);if(u==-2&&v==2)set(c,x,y+1,z,Blocks.CRAFTING_TABLE);if(u==0&&v==3)set(c,x,y+1,z,Blocks.LANTERN);
|
||||
}
|
||||
}
|
||||
public static void populate(WorldGenLevel level,ChunkGenerator generator,ChunkAccess c){
|
||||
if(!WorldgenLab.ecology180(generator))return;var source=(AdventureBiomes180)((NoiseBasedChunkGenerator)generator).getBiomeSource();var p=plan(source);
|
||||
for(var r:p.rooms()){
|
||||
var at=new BlockPos(r.x(),r.y()+1,r.z());if(!c.getPos().contains(at))continue;
|
||||
var random=RandomSource.create(level.getSeed()^at.asLong()^0x1801007L);
|
||||
if(r.kind()==1){
|
||||
level.setBlock(at,Blocks.SPAWNER.defaultBlockState(),2);
|
||||
if(!(level.getBlockEntity(at) instanceof SpawnerBlockEntity spawner))throw new IllegalStateException("Missing native spawner block entity");
|
||||
spawner.setEntityId(switch(r.id()%3){case 0->EntityTypes.ZOMBIE;case 1->EntityTypes.SKELETON;default->EntityTypes.CAVE_SPIDER;},random);
|
||||
}else if(r.kind()==2){
|
||||
for(int dx=-1;dx<=1;dx++)if(c.getPos().contains(at.offset(dx,0,0)))c.setBlockState(at.offset(dx,0,0),Blocks.RAIL.defaultBlockState().setValue(RailBlock.SHAPE,RailShape.EAST_WEST));
|
||||
var cart=EntityTypes.CHEST_MINECART.create(level.getLevel(),EntitySpawnReason.STRUCTURE);
|
||||
if(cart==null)throw new IllegalStateException("Missing chest minecart");
|
||||
cart.snapTo(r.x()+.5,r.y()+1.0625,r.z()+.5,90,0);cart.setLootTable((WorldgenLab.details188(generator)||WorldgenLab.finishedTerrain(generator))?ResourceKey.create(Registries.LOOT_TABLE,Identifier.fromNamespaceAndPath("sanctuary_test","chests/mine_cache_188")):LOOT,level.getSeed()^at.asLong());
|
||||
cart.setCustomName(Component.translatable("entity.sanctuary_test.treasure_cart"));
|
||||
cart.setUUID(UUID.nameUUIDFromBytes(("sanctuary_test:dungeon180:"+level.getSeed()+":"+r.id()).getBytes(StandardCharsets.UTF_8)));
|
||||
level.addFreshEntity(cart);
|
||||
}
|
||||
}
|
||||
if(p.hut()!=null&&c.getPos().contains(p.hut())){
|
||||
var random=RandomSource.create(level.getSeed()^0x179A11L);AdventureCaves180.spawn(level,p.hut().above(2),EntityTypes.WITCH,random);AdventureCaves180.spawn(level,p.hut().offset(1,2,0),EntityTypes.CAT,random);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)||WorldgenLab.ecology180(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,124 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||
|
||||
/** Bounded summit erosion over the original 3D field; no island-wide terraces or water solver. */
|
||||
final class Erosion192 {
|
||||
record Crown(int x,int z,double height,double dx,double dz){}
|
||||
private final ReliefEcology177.Field original;
|
||||
private final boolean coherent;
|
||||
private final List<Crown> crowns;
|
||||
private final Map<Long,Double> oldTops=new ConcurrentHashMap<>(),roofs=new ConcurrentHashMap<>();
|
||||
private final Map<Long,Integer> surfaces=new ConcurrentHashMap<>();
|
||||
private final Map<Long,Float> slopes=new ConcurrentHashMap<>();
|
||||
Erosion192(ReliefEcology177.Field original,boolean coherent){
|
||||
this.original=original;this.coherent=coherent;
|
||||
var candidates=new ArrayList<int[]>();
|
||||
for(int x=original.peakX()-64;x<=original.peakX()+64;x+=8)
|
||||
for(int z=original.peakZ()-64;z<=original.peakZ()+64;z+=8){
|
||||
int y=original.surface(x,z);if(y>=(coherent?262:278))candidates.add(new int[]{x,y,z});
|
||||
}
|
||||
candidates.sort(Comparator.<int[]>comparingInt(a->a[1]).reversed().thenComparingInt(a->a[0]).thenComparingInt(a->a[2]));
|
||||
var chosen=new ArrayList<Crown>();
|
||||
for(var p:candidates){
|
||||
if(chosen.stream().anyMatch(c->Math.hypot(c.x-p[0],c.z-p[2])<34))continue;
|
||||
double dx=1,dz=0;int low=1000;
|
||||
for(int i=0;i<16;i++){
|
||||
double angle=i*Math.PI/8;int y=original.surface(p[0]+(int)(28*Math.cos(angle)),p[2]+(int)(28*Math.sin(angle)));
|
||||
if(y<low){low=y;dx=Math.cos(angle);dz=Math.sin(angle);}
|
||||
}
|
||||
chosen.add(new Crown(p[0],p[2],oldTop(p[0],p[2])-5.5,dx,dz));if(chosen.size()==3)break;
|
||||
}
|
||||
crowns=List.copyOf(chosen);
|
||||
}
|
||||
boolean coherent(){return coherent;}
|
||||
List<Crown> crowns(){return crowns;}
|
||||
private static long key(int x,int z){return ((long)x<<32)^(z&0xffffffffL);}
|
||||
private boolean inMassif(int x,int z){return Math.hypot(x-original.peakX(),z-original.peakZ())<132;}
|
||||
private static double smooth(double t){t=Math.clamp(t,0,1);return t*t*(3-2*t);}
|
||||
private double oldTop(int x,int z){
|
||||
return oldTops.computeIfAbsent(key(x,z),k->{
|
||||
for(int y=316;y>=240;y--){
|
||||
float d=original.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z);
|
||||
if(d>0){float above=original.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y+1,z);return y+d/(double)(d-above);}
|
||||
}
|
||||
return 239.0;
|
||||
});
|
||||
}
|
||||
double roof(int x,int z){
|
||||
if(!inMassif(x,z))return Double.POSITIVE_INFINITY;
|
||||
return roofs.computeIfAbsent(key(x,z),k->{
|
||||
double old=oldTop(x,z);if(old<260)return Double.POSITIVE_INFINITY;
|
||||
double cut=0,first=Double.POSITIVE_INFINITY,second=Double.POSITIVE_INFINITY;
|
||||
for(var c:crowns){
|
||||
double ox=x-c.x,oz=z-c.z,distance=Math.hypot(ox,oz);
|
||||
if(distance<first){second=first;first=distance;}else if(distance<second)second=distance;
|
||||
double u=ox*c.dx+oz*c.dz,v=-ox*c.dz+oz*c.dx;
|
||||
// Rounded footprint, independent of chunks. Leave gentler back slopes and connecting saddles.
|
||||
double width=23+2*EcologyBiomes176.noise(192,x/13.0,z/13.0);
|
||||
double weight=smooth((width-Math.abs(v))/8)*smooth((u+22)/12)*smooth((42-u)/14);
|
||||
if(weight<=0)continue;
|
||||
double lip=9+1.5*EcologyBiomes176.noise(c.x*7919L+c.z,x/9.0,z/9.0);
|
||||
double plane=c.height+.035*u+.6*EcologyBiomes176.noise(1921,x/21.0,z/21.0)-4.6*Math.max(0,u-lip);
|
||||
cut=Math.max(cut,weight*Math.clamp(old-plane,0,24));
|
||||
}
|
||||
// A narrow, softened Voronoi seam separates neighbouring crowns; never repeats across the island.
|
||||
double seam=5*smooth((4-(second-first))/4)*smooth((31-first)/10);
|
||||
cut=Math.max(cut,seam)*smooth((old-260)/15);
|
||||
if(coherent){
|
||||
// Shallow irregular gullies interrupt long contours, leaving the crown centres intact.
|
||||
double edge=smooth((first-12)/9)*smooth((124-Math.hypot(x-original.peakX(),z-original.peakZ()))/16);
|
||||
double grain=EcologyBiomes176.noise(193L^original.peakX()*7919L^original.peakZ(),x/11.0,z/13.0);
|
||||
cut+=3.2*edge*smooth((grain+.12)/.7)*smooth((old-254)/18);
|
||||
}
|
||||
return cut>.01?Math.max(241,old-cut):Double.POSITIVE_INFINITY;
|
||||
});
|
||||
}
|
||||
int surface(int x,int z){
|
||||
// Neighbour samples need a real height beyond the mask, not an artificial cliff at its edge.
|
||||
if(!inMassif(x,z))return Math.hypot(x-original.peakX(),z-original.peakZ())<140?(int)Math.floor(oldTop(x,z)):239;
|
||||
return surfaces.computeIfAbsent(key(x,z),k->{
|
||||
int start=(int)Math.min(316,Math.min(oldTop(x,z),Math.ceil(roof(x,z))-1));
|
||||
for(int y=start;y>=240;y--)if(original.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)>0 && y<roof(x,z))return y;
|
||||
return 239;
|
||||
});
|
||||
}
|
||||
float slope(int x,int z){
|
||||
if(!inMassif(x,z)||surface(x,z)<248)return 0;
|
||||
return slopes.computeIfAbsent(key(x,z),k->{
|
||||
int center=surface(x,z);double slope=0;
|
||||
// World-space columns cross chunk boundaries and ignore the separate aerial islands.
|
||||
for(int[] d:new int[][]{{2,0},{-2,0},{0,2},{0,-2},{4,4},{-4,4},{4,-4},{-4,-4}})
|
||||
slope=Math.max(slope,(center-surface(x+d[0],z+d[1]))/Math.hypot(d[0],d[1]));
|
||||
double mask=smooth((132-Math.hypot(x-original.peakX(),z-original.peakZ()))/12);
|
||||
return (float)(Math.min(8,slope)*mask);
|
||||
});
|
||||
}
|
||||
static Map<String,Object> check(ServerLevel level,AdventureBiomes180 s){
|
||||
var f=s.field();var e=f.erosion();int changed=0,deep=0,steep=0,flat=0,maxDrop=0;var rows=new ArrayList<List<Integer>>();
|
||||
for(int x=-320;x<=320;x+=8)for(int z=-320;z<=320;z+=8){
|
||||
for(int y:new int[]{72,144,208,240,352,448}){
|
||||
if(f.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)!=e.original.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z))throw new IllegalStateException("Erosion changed deep/sky density");deep++;
|
||||
}
|
||||
if(!e.inMassif(x,z))for(int y=248;y<320;y+=8)
|
||||
if(f.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z)!=e.original.sampleValue(SamplerContext.EMPTY_UNCACHED,x,y,z))throw new IllegalStateException("Erosion escaped massif");
|
||||
}
|
||||
int rock=0,soil=0;
|
||||
for(int x=f.peakX()-96;x<=f.peakX()+96;x+=2)for(int z=f.peakZ()-96;z<=f.peakZ()+96;z+=2){
|
||||
int old=(int)Math.floor(e.oldTop(x,z)),now=e.surface(x,z);if(old<260)continue;
|
||||
if(old!=now){changed++;maxDrop=Math.max(maxDrop,old-now);}
|
||||
float slope=e.slope(x,z);if(slope>.95)steep++;else if(now>=(e.coherent?250:274) && slope<=.5)flat++;
|
||||
if(slope>.95 && now>=260){
|
||||
var b=level.getBlockState(new net.minecraft.core.BlockPos(x,now,z));
|
||||
if(b.is(net.minecraft.world.level.block.Blocks.DIRT)||b.is(net.minecraft.world.level.block.Blocks.GRASS_BLOCK))soil++;
|
||||
else if(AdventureCaves180.natural(b))rock++;
|
||||
}
|
||||
rows.add(List.of(x,z,old,now));
|
||||
}
|
||||
if(changed<40||flat<20||steep<40||(e.crowns.size()< (e.coherent?2:3)||e.crowns.size()>3)||rock<20||soil>rock)throw new IllegalStateException("Missing crown/face erosion: "+changed+" flat="+flat+" steep="+steep+" rock="+rock+" soil="+soil);
|
||||
return Map.of("crowns",e.crowns,"changedColumnsAt2BlockSpacing",changed,"flatSummitSamples",flat,"steepSamples",steep,"steepRockSamples",rock,"steepSoilSamples",soil,"maxSurfaceDrop",maxDrop,"unchangedDeepAndSkySamples",deep,"heightSamples",rows);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package fr.koka.sanctuarytest;
|
||||
|
||||
import fr.koka.sanctuary.expansion.*;
|
||||
import fr.koka.sanctuary.palace.*;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.item.*;
|
||||
import net.minecraft.world.level.block.*;
|
||||
|
||||
/** Opt-in disposable native-world test: offers, FULL completion, a chained relay and reload. */
|
||||
final class Expansion199Checks {
|
||||
private static Run run;
|
||||
private record Run(MinecraftServer server,Path output,Map<String,Object> result,long started,boolean fresh,
|
||||
CompletableFuture<ExpansionIsland> request,ItemStack payment,int stage) {}
|
||||
static void register(){ServerTickEvents.END_SERVER_TICK.register(Expansion199Checks::tick);}
|
||||
static void begin(MinecraftServer server,Path output,Map<String,Object> result,long started,boolean fresh){
|
||||
var level=server.overworld();var adapter=LabExpansions199.get(level.getChunkSource().getGenerator());
|
||||
if(adapter==null)throw new IllegalStateException("expansion199 profile required");
|
||||
var session=ExpansionRuntime.session(level);
|
||||
result.put("verificationScope","expansion199");
|
||||
if(fresh){
|
||||
var sites=Anchors189.plan(Anchors189.source(level.getChunkSource().getGenerator()),level.getSeed()).sites();
|
||||
var pos=sites.get(0).anchor();level.getChunkAt(pos);level.getChunkAt(AnchorOffers189.CORE);
|
||||
var core=level.getBlockState(AnchorOffers189.CORE);
|
||||
if(!core.is(OriginBlock.BLOCK)||!level.getBlockState(pos).is(ExpansionAnchorBlock.BLOCK))throw new AssertionError("Missing native root/core");
|
||||
level.setBlock(AnchorOffers189.CORE,core.setValue(OriginBlock.LIT,true),Block.UPDATE_ALL);
|
||||
var wrong=new ItemStack(Items.DIRT,64);
|
||||
if(!RelayOffers199.offer(level,pos,wrong,()->true).isCompletedExceptionally()||wrong.getCount()!=64)throw new AssertionError("Wrong offering accepted");
|
||||
var demand=AnchorOffers189.DEMANDS.get(0);var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
var request=RelayOffers199.offer(level,pos,held,()->true);
|
||||
if(!RelayOffers199.offer(level,pos,held,()->true).isCompletedExceptionally())throw new AssertionError("Duplicate search accepted");
|
||||
run=new Run(server,output,result,started,true,request,held,0);
|
||||
}else{
|
||||
if(session.islands().size()!=3)throw new AssertionError("Journal did not retain both expansions");
|
||||
run=new Run(server,output,result,started,false,CompletableFuture.completedFuture(session.islands().get(2)),ItemStack.EMPTY,2);
|
||||
}
|
||||
}
|
||||
private static void tick(MinecraftServer server){
|
||||
var r=run;if(r==null||r.server()!=server)return;
|
||||
try{
|
||||
if(System.nanoTime()-r.started()>java.util.concurrent.TimeUnit.MINUTES.toNanos(8))throw new AssertionError("Expansion test timed out");
|
||||
if(!r.request().isDone())return;
|
||||
var admitted=r.request().join();var level=server.overworld();var session=ExpansionRuntime.session(level);
|
||||
var current=session.island(admitted.id());
|
||||
if(!current.status().equals("ready")){
|
||||
if(r.stage()==1){
|
||||
// Persist a partially prepared reservation; the warm run must complete it.
|
||||
r.result().put("reservedChild",current.id());finish(r);return;
|
||||
}
|
||||
if(session.progress().contains(": ")&&!session.progress().matches(".*: [0-9]+/[0-9]+"))throw new AssertionError(session.progress());
|
||||
return;
|
||||
}
|
||||
var adapter=LabExpansions199.get(level.getChunkSource().getGenerator());var relay=adapter.relay(current);
|
||||
if(!level.getBlockState(relay).is(ExpansionAnchorBlock.BLOCK))throw new AssertionError("Ready before next relay exists");
|
||||
if(r.stage()==0){
|
||||
if(r.payment().getCount()!=1)throw new AssertionError("Offer charged incorrectly");
|
||||
var rootSite=Anchors189.plan(Anchors189.source(level.getChunkSource().getGenerator()),level.getSeed()).sites().get(0).anchor();
|
||||
if(RelayOffers199.offer(level,rootSite,r.payment(),()->true).join().id().equals(current.id())&&r.payment().getCount()!=1)throw new AssertionError("Repeated offer charged");
|
||||
r.result().put("firstExpansion",Map.of("id",current.id(),"x",current.centerX(),"z",current.centerZ(),"relayY",relay.getY()));
|
||||
int sector=level.getBlockState(relay).getValue(ExpansionAnchorBlock.SECTOR);var demand=AnchorOffers189.DEMANDS.get(sector);
|
||||
var held=new ItemStack(demand.item(),demand.count()+1);
|
||||
var request=RelayOffers199.offer(level,relay,held,()->true);
|
||||
run=new Run(server,r.output(),r.result(),r.started(),true,request,held,1);
|
||||
}else{
|
||||
int columns=0,solid=0;int low=1600,high=0;
|
||||
for(int x=-64;x<=64;x+=16)for(int z=-64;z<=64;z+=16){
|
||||
var chunk=level.getChunk((current.centerX()+x)>>4,(current.centerZ()+z)>>4);columns++;
|
||||
for(int y=0;y<384;y++)if(!chunk.getBlockState(new BlockPos(current.centerX()+x,y,current.centerZ()+z)).isAir()){solid++;low=Math.min(low,y);high=Math.max(high,y);}
|
||||
}
|
||||
if(solid<columns*4||solid>columns*110||low<140||high>325)throw new AssertionError("Unexpected sheet terrain "+solid+" / "+columns+" y="+low+".."+high);
|
||||
r.result().put("resumedExpansion",Map.of("id",current.id(),"columns",columns,"solid",solid,"minY",low,"maxY",high));
|
||||
finish(r);
|
||||
}
|
||||
}catch(Throwable e){run=null;fr.koka.sanctuary.SanctuaryMod.LOGGER.error("EXPANSION199_CHECK_FAILED",e);server.halt(false);}
|
||||
}
|
||||
private static void finish(Run r){run=null;r.result().put("readyForVisitMs",(System.nanoTime()-r.started())/1_000_000);WorldgenLab.writeReport(r.output(),r.result());}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user