feat: add native Blocodex and material census for alpha23

This commit is contained in:
koka
2026-09-10 19:28:18 +02:00
parent c9d9dab1b5
commit e654d565f6
51 changed files with 4957 additions and 15 deletions
+255
View File
@@ -0,0 +1,255 @@
# Blocodex natif — connaissances, matière et stocks
Ticket META-01, branche `codex/blocodex-natif`, cible Minecraft **26.3-pre-2**.
Le Blocodex appartient au mod Sanctuary. Il est disponible sans appareil,
recette ni déblocage. La mémoire personnelle, le recensement du terrain et
celui des stocks forment un socle commun aux futures palettes de structures,
règles dexpansion et règles du shop. Les relevés datés ajoutent la temporalité
nécessaire à une future lecture de lhistoire économique du serveur.
## Contrat des informations
La mémoire est personnelle : une identité de joueur (UUID), dans une sauvegarde
de serveur, partagée entre ses dimensions. Les indicateurs sont indépendants.
| Information | Sens |
| --- | --- |
| Inconnu | Aucune observation, possession, pose ni statistique de minage ou de jet. C'est un état calculé, pas une étape enregistrée. |
| Vu | Le premier bloc rencontré par le regard du joueur, à au plus 32 blocs, observé côté serveur tous les 5 ticks. Les obstacles arrêtent le rayon ; les chunks absents ne sont pas chargés. Ce relevé ne couvre pas chaque pixel du champ de vision. |
| Miné | Compteur vanilla `mined` du bloc. Ses règles sont conservées : notamment pas de minage créatif, ni de minage sans l'outil requis. |
| Déjà possédé | Le bloc, sous forme d'item, a été présent dans l'inventaire personnel ou sur le curseur d'un menu. Les statistiques vanilla de ramassage et de jet prouvent aussi une possession passée. |
| Jeté | Compteur vanilla `dropped` de l'item du bloc. Il compte les unités volontairement jetées, pas les objets éjectés à la mort ni le butin produit par le bloc cassé. |
| Posé | Nombre de placements réussis par le joueur avec un item de bloc, y compris en créatif. Une tentative refusée, une interaction ou un placement par commande ne compte pas. |
| En inventaire | Quantité actuellement dans l'inventaire personnel, équipement et curseur compris. Les coffres ouverts, ender chests et contenus imbriqués des boîtes ne sont pas inclus. |
Les blocs sont identifiés par leur identifiant de registre ; les propriétés
d'état (orientation, humidité, etc.) ne créent pas une nouvelle fiche. Deux
identifiants restent distincts : une torche murale peut être vue et posée tandis
que l'item possédé correspond à la torche debout. Les noms des blocs inconnus
restent consultables, comme un catalogue ; cette version ne décide pas d'une
politique de secrets ou de verrouillage des recettes.
L'inventaire est observé chaque tick serveur, après les transactions de menus
et avant la consommation d'un item placé. Les statistiques de ramassage/jet
complètent ce relevé. Une mutation d'inventaire entièrement réalisée entre ces
points par un autre mod ne peut pas être reconstruite après coup.
Les spectateurs ne découvrent pas de blocs par le regard.
## Persistance et arrivée dans une sauvegarde antérieure
Contrat additif v1 : fichiers `data/sanctuary-blocodex/<uuid>.json`, enveloppe
avec `schema: 1` et UUID, puis observations, possessions et poses par identifiant.
Les statistiques vanilla restent dans leurs propres fichiers et sont seulement
lues. Aucune conversion du terrain, des paramètres de génération, des chunks,
du journal d'expansion ou des statistiques existantes n'est effectuée.
Sans fichier Blocodex, les observations et poses commencent vides. Le minage,
le jet et les preuves de possession sont disponibles depuis les statistiques
vanilla déjà présentes. On n'invente pas les observations ni les poses passées
à partir de `used`, qui compte aussi d'autres usages.
Les changements sont sauvegardés tous les 600 ticks, lors de la sauvegarde du
serveur, à la déconnexion et à l'arrêt. Un crash peut perdre les dernières
observations ou poses non sauvegardées. Un fichier invalide ou d'une version
inconnue désactive la mémoire concernée et n'est jamais remplacé silencieusement.
Les identifiants de mods absents sont conservés sur disque. Les anciennes
versions de Sanctuary ignorent ce répertoire additionnel ; les poses réalisées
pendant un retour à une ancienne version ne peuvent pas être récupérées.
## Réutilisation
`BlockKnowledgeService.knowledge(player, block)` et `snapshot(player)` renvoient
des valeurs immuables calculées côté serveur. Le service expose aussi sa santé ;
un consommateur doit refuser une condition de progression si la mémoire est
indisponible, plutôt que d'interpréter une panne comme une découverte vierge.
`BlockKnowledgeEvents.CHANGED` permet de réagir aux observations, possessions,
poses et changements de statistiques. Les écouteurs s'exécutent sur le thread
serveur ; ils ne constituent pas un journal transactionnel de récompenses.
Cette livraison ne verrouille aucune recette et n'accorde aucune récompense.
Palettes disponibles, recherches, progression et structures pourront ensuite
définir leurs propres conditions en utilisant cette API commune.
### Palettes de structures
`BlockKnowledgeService.palette(player, condition, tag)` croise les connaissances
du joueur avec une famille de matériaux. Les critères `BlockKnowledgeFacet`
sont combinables avec `and` et `or` : vus **ou** déjà possédés, minés **et**
actuellement portés, etc. Le résultat immuable garde les identifiants triés et
les quantités réellement portées.
```java
var candidates = BlockKnowledgeService.palette(player,
BlockKnowledgeFacet.SEEN.or(BlockKnowledgeFacet.POSSESSED),
BlockKnowledgeService.NATURAL_STRUCTURE_MATERIALS);
```
Le tag `sanctuary:structure_palette/natural` fournit une première famille de
pierres, terres, bois, feuilles et autres matériaux naturels, modifiable par
datapack. D'autres tags pourront définir des palettes minérales, végétales ou
propres à un biome. Le choix des couleurs, les contraintes physiques des blocs,
les quantités nécessaires et la construction de la structure appartiendront au
moteur de structures : une sélection de palette ne réserve aucun stock et ne
place aucun bloc.
## Recensement du terrain chargé
`TerrainCensus.snapshot(server)` expose une distribution des blocs par dimension ;
`TerrainCensus.chunkSnapshot(level, pos)` fournit le détail dun chunk déjà
chargé. Ces lectures se font sur le thread serveur et renvoient des valeurs
immuables. Elles parcourent les chunks `FULL` déjà accessibles, sans charger
de chunk absent ni attendre la fin dune génération.
Le comptage utilise les palettes des sections de 16 × 16 × 16 blocs. Il regroupe
les orientations et autres propriétés sous lidentifiant du bloc. Les blocs
dair sont exclus ; leau et la lave conservent leurs identifiants de blocs.
Le budget est de **16 sections non vides par tick serveur**, partagé entre les
dimensions et les demandes du même tick. Une section entièrement vide est
reconnue par ses métadonnées, sans parcourir sa palette.
Le relevé est progressif. Une section modifiée est retirée des totaux dès que
sa révision change, puis remise dans la file de comptage. Les sections et
chunks déchargés sortent du relevé. Un consommateur dispose, par dimension ou
chunk, du nombre de sections chargées et comptées, du tick de capture, du tick
du dernier comptage effectué et de l’âge de la plus ancienne attente.
`complete()` et `coverage()` décrivent la couverture **des zones chargées**.
Une couverture de 100 % ne signifie jamais que toute la sauvegarde a été lue.
Pendant un rattrapage, zéro bloc compté ne prouve donc pas labsence de ce bloc.
Les futures règles dexpansion devront fixer leur couverture minimale et leur
zone d’étude avant de prendre une décision. Les résultats en mémoire sont
reconstruits après redémarrage ; ce service n’écrit pas dans les chunks et ne
modifie pas leur génération.
## Recensement des stocks présents
`StockCensus.snapshot(server)` photographie les objets accessibles au tick de
la demande, sur le thread serveur. Le parcours des slots et des entités chargées
est effectué à la demande ; aucun abonnement ne le relance à chaque tick.
Les quantités sont des unités ditems, tous
types confondus : un lingot, un outil ou une graine sont comptés même sils ne
correspondent pas à un bloc. Les composants, lusure et les enchantements ne
créent pas de nouveaux identifiants de stock dans cette version.
| Catégorie | Couverture |
| --- | --- |
| `playerItems` | Joueurs connectés : inventaire personnel, équipement et curseur du menu. Le contenu du menu ouvert nest pas additionné. |
| `blockContainerItems` | Entités de blocs déjà instanciées dans les chunks chargés, qui exposent linterface `Container` : coffres, hoppers, fours, pots décorés, etc. Chaque moitié dun double coffre est comptée une fois. |
| `entityContainerItems` | Entités chargées qui exposent `Container`, notamment les wagonnets et bateaux à coffre. |
| `groundItems` | Quantités des `ItemEntity` chargées et encore présentes au sol. |
Le relevé ne déballe **aucune table de butin**. Tout conteneur dont la table
reste à résoudre est ignoré avant la lecture de ses slots ; son existence
augmente `pendingLootContainers`. Les entités de blocs encore stockées sous
forme de NBT et non instanciées sont également laissées intactes, avec leur
nombre dans `pendingBlockEntities`. Leur contenu nest pas remplacé par une
estimation.
La couverture exclut les joueurs hors ligne, les chunks et entités déchargés,
les ender chests, le contenu imbriqué des shulker boxes et bundles, les grilles
de fabrication en cours dans linventaire ou l’établi (`menuInputsIncluded()`
est faux), les inventaires et équipements des créatures. Le résultat de
fabrication affiché avant validation ne constitue pas un stock supplémentaire.
Elle exclut aussi les objets internes
des entités de blocs qui nexposent pas `Container`, comme les aliments dun feu
de camp ou le livre dun pupitre. Une shulker box portée compte comme un item ;
une shulker box posée et chargée expose son conteneur. Les exclusions figurent
dans le contrat de couverture ; `partial()` reste vrai.
Ce relevé indique où se trouvent les unités observées. Il ne les réserve pas,
ne les transfère pas et n’évalue pas leur prix. Ajouter le terrain aux stocks
ne donne pas une quantité immédiatement vendable : une roche dans un chunk
reste différente dun item dans un coffre.
## Consultation et futurs consommateurs communs
`WorldCensusService.capture(server)` rassemble les deux relevés pour une même
lecture côté serveur. Le consommateur réutilise cet instantané pendant son
calcul et conserve ses informations de couverture. Le shop et les expansions
pourront ainsi consulter la même base, avec leurs propres règles de fraîcheur,
de portée géographique, de prix, de consommation et de réservation.
Les diagnostics suivants sont réservés aux opérateurs :
```text
/sanctuary census
/sanctuary census block minecraft:stone
/sanctuary census item minecraft:gold_ingot
```
Le résumé présente la couverture du terrain par dimension, les quatre catégories
de stocks et les conteneurs au butin encore indéterminé. Le Blocodex personnel
accessible par **B** conserve les données du joueur ; ces diagnostics ne diffusent
pas les inventaires agrégés aux autres joueurs.
## Relevés datés — portée de lalpha.23
La collecte regroupe les activités observées par **jour civil UTC**, pour
lensemble du serveur, sans attribution à un UUID ou à une dimension :
| Action | Registre et unité |
| --- | --- |
| `mined` | `block` : nombre de blocs minés, selon les règles des statistiques vanilla. |
| `placed` | `block` : placements réussis par le joueur, créatif inclus et spectateurs exclus. |
| `picked_up` | `item` : unités ramassées observées par les statistiques vanilla. |
| `dropped` | `item` : unités volontairement jetées observées par les statistiques vanilla. |
| `crafted` | `item` : unités fabriquées créditées au joueur par les statistiques vanilla. |
Chaque combinaison conserve son action, son registre, son identifiant et une
quantité entière sur 64 bits, saturée à la valeur maximale en cas de dépassement.
Un compteur de minage nest pas transformé en nombre ditems produits. Les actions
antérieures à linstallation ne sont pas réparties artificiellement entre des
dates à partir des statistiques cumulées de Minecraft.
`MaterialActivityHistory.snapshot(server, day)` expose un instantané immuable
avec le jour, les compteurs, `recorded` et `unsaved`. Un jour sans données
enregistrées demeure inconnu ; il ne constitue pas un historique dactivité nulle.
Les opérateurs peuvent consulter `/sanctuary history` ou préciser une date avec
`/sanctuary history 2026-09-10`. La commande affiche au plus les 16 lignes aux
quantités les plus élevées et indique combien ont été omises ; lAPI conserve
lensemble des compteurs du jour.
La persistance additionnelle utilise `data/sanctuary-activity/YYYY-MM-DD.json`,
avec un schéma 1 et des dates UTC. Elle est séparée de la mémoire personnelle
et des chunks. Les écritures ont lieu tous les **600 ticks**, à la sauvegarde
et à larrêt du serveur, ainsi quau changement de jour observé par le service.
Un crash peut perdre les derniers relevés non sauvegardés, soit normalement
jusqu’à 30 secondes de jeu à 20 ticks par seconde. Le fichier conserve les
identifiants de mods absents ; il refuse les schémas inconnus, doublons,
compteurs invalides et changements externes. Une erreur arrête la collecte et
rend le service indisponible jusqu’à réparation puis redémarrage, en conservant
le fichier en cause. Les limites sont de 100 000 tuples et 16 Mio par jour.
Ces relevés permettent de distinguer des périodes dactivité. Ils ne conservent
pas dhistorique des instantanés de stocks, ne couvrent pas la fabrication automatique des
machines et n’établissent ni consommation, ni perte, ni destination des objets.
Ils ne retracent pas chaque transfert entre coffres ou chaque changement de propriétaire, et
ne constituent pas un journal transactionnel dachats, de récompenses ou de
réservations. Le **ballast**, ses unités, ses conversions et sa traduction en
géologie restent à concevoir dans le [cahier de cosmologie](cosmologie.md).
Aucun objet nest consommé et aucune Backroom nest générée par ces mesures.
## Référence WSMC retrouvée
La recherche locale a retrouvé une partie du travail précédent dans
`/Users/koka/Documents/sanctuary/Gameplay/blocodex` :
`ClientVoxelProjectBuilder` convertit des apparences de blocs et ditems en
volumes/couches de blocs ; `BlocodexStudioPalettes` sélectionne des matériaux
opaques appris. Le projet conserve aussi un instantané WSMC de 1 881 entrées
(1 168 blocs, 490 items, 223 mobs), issu de Minecraft 1.21.11.
Cette référence reste historique. La nouvelle API fournit la connaissance et
les candidats de palette ; la traduction de modèles dentités en structures
(visage de creeper, squelette géant, etc.) reste un chantier distinct. Aucun
générateur dentités ni import de ce snapshot ancien nest ajouté au registre
de Minecraft 26.3 par le socle méta.
## Validation
Validation locale du 10 septembre 2026 : `./gradlew check build assemblePack`
passe, avec **24 GameTests natifs réussis**. Le client graphique vérifie les
widgets et les messages réels dans un nouveau monde intégré, avec B sans droit
opérateur, observation serveur et possession conservée après retrait du stock.
Les tests de stockage passent. Les [preuves et limites](testing-alpha23.md)
détaillent le protocole, lartefact et son empreinte. Alpha.23 assemblée localement ;
publication du MRpack et mise à jour du canal demandées, en préparation.