Compare commits

...
Author SHA1 Message Date
koka 6aaabf1cb9 Normalize sculpture delivery note formatting
Build Sanctuary / build (push) Canceled after 0s
2026-09-17 13:16:24 +02:00
koka 72e9263339 Record beta.119 release and verified Prism synchronization
Build Sanctuary / build (push) Canceled after 0s
2026-09-17 13:16:12 +02:00
koka 3fd64e1f2a Center sculptures on aimed block middle without half-voxel offsets (beta.119)
Build Sanctuary / build (push) Canceled after 0s
2026-09-17 13:14:54 +02:00
10 changed files with 140 additions and 15 deletions
+13 -6
View File
@@ -20,12 +20,12 @@ La [vision complète](docs/vision.md) conserve les intentions ; le
L’économie, les claims et les dimensions décrits dans la vision restent
à implémenter.
La version **beta.118** ajoute les seize murets en briques colorées, avec
leurs textures existantes, les raccords natifs et leurs recettes. Ils forment
une série de seize après les dalles dans l'onglet des blocs colorés.
Le [ticket de livraison](docs/colored-brick-walls-beta118.md) conserve les
contrôles et le suivi de distribution. La release est publiée et l'instance
**Sanctuary Beta** est synchronisée, sauvegardes et réglages conservés.
La version **beta.119** permet de centrer une sculpture en visant le milieu
du bloc, ou de la caler contre un bord en visant autour. La grille de 1/16
reste respectée, même pour les modèles de taille impaire.
Le [ticket de livraison](docs/sculpture-center-beta119.md) conserve les contrôles
et le suivi de distribution. La release est publiée et linstance **Sanctuary Beta**
est synchronisée, sauvegardes et réglages conservés.
La [livraison précédente](docs/clay-workshop-integration-beta110.md) réunit déjà
latelier, les œufs et la neige saisonnière dans linstance **Sanctuary Beta**.
Les [synchronisations antérieures](docs/git-sync-beta092.md) restent documentées.
@@ -36,6 +36,13 @@ La [carte native beta.005](docs/atlas-beta005.md) prend le relais après
l’étude de Xaero et des alternatives libres.
## beta.119 — Sculptures au centre ou au bord
Pose selon le point visé, centrage sur la grille entière et ancrage conservé
en sauvegarde. [Contrat et vérifications](docs/sculpture-center-beta119.md) ·
[Pack normal](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.119/Sanctuary-beta.119.mrpack) ·
[Pack de test](https://git.botsu.net/koka/sanctuary-beta/releases/download/beta.119/Sanctuary-Test-beta.119.mrpack).
## beta.118 — Murets en briques colorées
Seize couleurs, fabrication et tailleur de pierre, eau, raccords et collisions
+11
View File
@@ -14,6 +14,17 @@ JAR Sanctuary sont des pièces jointes de releases Gitea. Aucun JAR de mod ou
dépendance téléchargée n'entre dans l'historique Git. Le canal Beta est distinct
de l'ancien pack 26.2.
## beta.119 — pose centrée des sculptures
Centre accessible au point visé, bords conservés et grille entière de 1/16.
[Contrat et vérifications](sculpture-center-beta119.md).
[Release beta.119](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.119)
publiée ; canal `5e823af7807f524f6394acad402fe62328c7e4ab`. Deux
synchronisations isolées puis deux dans la même instance **Sanctuary Beta**
réussies. Les 923 fichiers personnels et réglages suivis sont inchangés ;
sauvegardes restées fermées. Copie préalable des fichiers remplacés dans
`sanctuary-backups/before-beta.119/`.
## beta.118 — murets en briques colorées
Seize murets natifs, recettes, tailleur, eau et série créative par couleurs.
+84
View File
@@ -0,0 +1,84 @@
# STAT-07 — Pose centrée des sculptures — beta.119
Contrat du 17 septembre 2026, branche `codex/sculpture-center-beta119`.
Viser le carré central de 8 × 8 pixels de la face supérieure ou inférieure
du bloc place la sculpture au centre. Les limites de cette zone sont incluses.
Autour, le bord horizontal le plus proche reste choisi ; une pose latérale
continue à s'appuyer contre le bloc support. L'orientation suit le regard.
L'aide du Clay Workshop explique les deux possibilités en français et anglais.
Le centrage utilise l'emprise réelle des voxels après rotation, en ignorant les
marges transparentes. Tous les sommets restent sur la grille de 1/16 de bloc.
Une dimension impaire ne peut pas être parfaitement symétrique dans un bloc de
16 pixels sans décaler ses voxels : elle prend donc le centrage entier le plus
proche, avec un pixel de différence entre les deux marges. Aucune position à
un demi-voxel n'est introduite.
## Compatibilité
La pose réutilise `anchor=center`, déjà présent depuis beta.113. Aucun nouvel
état, identifiant, composant ou format de sauvegarde. Les sculptures existantes
gardent leur ancrage ; casser et reposer choisit l'emplacement visé. Le rendu,
les couleurs, les chapeaux et les collisions restent inchangés. Aucun parcours
de monde existant, aucune régénération. Client et serveur doivent utiliser
ensemble la nouvelle version pour le choix de pose.
## Vérifications et livraison
Le parcours natif `Statuary106ClientChecks`, avec `Sculpture113ClientChecks`,
passe en **54 s** sur Minecraft 26.3/macOS :
- Vrais clics de pose centrée dans quatre orientations, sur client et serveur,
puis pose contre les quatre bords sans modifier les couleurs du modèle.
- Limites incluses et points immédiatement de part et d'autre de la zone,
faces supérieure/inférieure et positions positives/négatives ; faces
latérales toujours calées contre le support.
- 5 120 combinaisons de largeur/profondeur, orientation et ancrage : géométrie
effectivement rendue sur la grille exacte, emprise sans dépassement,
centrage pair/impair et marges transparentes.
- Sauvegarde/reconnexion des sculptures centrées et aux bords, sans fichier
source ; rotations/miroirs, anciens états, fabrication et hotbar vérifiés.
```sh
./gradlew :sanctuary:runClientGameTest \
-PsanctuaryClientTests=true -PsanctuaryStatuary106ClientTests=true \
-PsanctuaryClientNoVsync=true -PsanctuaryQuickTests=true
```
Marqueurs `SCULPTURE119_PASS`, `STATUARY113_PASS` et
`SCULPTURE113_GEOMETRY_PASS` dans `build/sculpture119-client.log`.
Captures conservées dans `build/sculpture119-evidence/`, vue centrée relue.
Monde plat jetable de graine 106 ; aucun monde personnel ouvert. Pas de
validation Windows ni de test depuis deux ordinateurs.
Le GameTest dédié reste exclu conformément au refus antérieur de son EULA ;
le parcours natif utilise le serveur intégré.
`./gradlew check build assemblePack assembleTestPack -x :sanctuary:runGameTest`
passe en **2 min 24 s**, 125 tâches. Les sources JAR correspondent aux sources
du dépôt ; seuls `SculptureAnchor.class` et le texte d'aide FR/EN diffèrent
parmi les classes et ressources de production de beta.118. Les deux packs
contiennent le même JAR, sans monde ni fixture de test.
- Sanctuary-beta.119.mrpack : 10216447 octets, SHA-256
`ce29702eaaac7301c77fb6af63a76c6bc1c0c704313ce1b8243c95b5fe59d893`.
- Sanctuary-Test-beta.119.mrpack : 10235369 octets, SHA-256
`a1a2845f9678b1144d7d9eab4867970cc1776fe0ad5d51722c8110b7c1d4c682`.
- JAR Sanctuary : SHA-256
`d353bd89b16c53ee1ee48836fa2ff4b311249b280992300c28bf03048d805bff`.
Reçu local : `build/sculpture119-artifact.json`.
## Publication et installation
La [release beta.119](https://git.botsu.net/koka/sanctuary-beta/releases/tag/beta.119)
est publiée depuis `3fd64e1f2a77ea2584b087d6339cf605d8ee0e9b`. Le tag exact et les
artefacts sont immuables ; leurs téléchargements publics sont vérifiés.
Le canal packwiz avance à `5e823af7807f524f6394acad402fe62328c7e4ab`.
Deux synchronisations isolées puis deux dans **Sanctuary Beta** réussissent.
Un seul JAR Sanctuary beta.119 est actif ; le second passage est identique.
Les **923 fichiers personnels et réglages suivis** restent inchangés, sans
ouvrir de monde personnel. Copie préalable dans
`sanctuary-backups/before-beta.119/` de l'instance existante. Reçus locaux :
`build/sculpture119-isolated.json` et `build/sculpture119-prism.json`.
+2 -2
View File
@@ -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.118
pack_version=beta.118
mod_version=beta.119
pack_version=beta.119
resource_pack_version=beta.090
maven_group=fr.koka.sanctuary
jei_version=30.32.0-sanctuary.3
@@ -18,7 +18,20 @@ import net.minecraft.world.phys.*;
final class Sculpture113ClientChecks {
static void placementContexts(ServerPlayer player){
for(var pos:List.of(new BlockPos(2,0,-2),new BlockPos(-2,0,2))){
for(var face:List.of(Direction.UP,Direction.DOWN)){
for(double x:new double[]{.25,.5,.75})for(double z:new double[]{.25,.5,.75}){
var hit=new Vec3(pos.getX()+x,pos.getY()+(face==Direction.UP?1:0),pos.getZ()+z);
var context=new BlockPlaceContext(player,InteractionHand.MAIN_HAND,new ItemStack(Statuary.ITEM),new BlockHitResult(hit,face,pos,false));
check(Statuary.BLOCK.getStateForPlacement(context).getValue(Statuary.ANCHOR)==SculptureAnchor.CENTER,"Middle square including boundaries selects center on top/bottom faces");
}
}
for(var edge:Direction.Plane.HORIZONTAL){
for(double offset:new double[]{.25-1e-6,.25+1e-6}){
var hit=new Vec3(pos.getX()+.5+edge.getStepX()*offset,pos.getY()+1,pos.getZ()+.5+edge.getStepZ()*offset);
var context=new BlockPlaceContext(player,InteractionHand.MAIN_HAND,new ItemStack(Statuary.ITEM),new BlockHitResult(hit,Direction.UP,pos,false));
var expected=offset<.25?SculptureAnchor.CENTER:SculptureAnchor.of(edge);
check(Statuary.BLOCK.getStateForPlacement(context).getValue(Statuary.ANCHOR)==expected,"Center boundary switches to the aimed edge");
}
var top=new Vec3(pos.getX()+.5+edge.getStepX()*.4,pos.getY()+1,pos.getZ()+.5+edge.getStepZ()*.4);
var context=new BlockPlaceContext(player,InteractionHand.MAIN_HAND,new ItemStack(Statuary.ITEM),new BlockHitResult(top,Direction.UP,pos,false));
var state=Statuary.BLOCK.getStateForPlacement(context);
@@ -119,7 +119,14 @@ public final class Statuary106ClientChecks implements FabricClientGameTest {
for(var direction:Direction.Plane.HORIZONTAL){
var pos=DIRECTIONS.get(direction);double px=pos.getX()+.5-direction.getStepX()*2,pz=pos.getZ()+.5-direction.getStepZ()*2;
server.runOnServer(s->{var p=player(s);p.teleportTo(px,1,pz);p.getInventory().setItem(p.getInventory().getSelectedSlot(),StatuaryEntity.item(source));p.inventoryMenu.broadcastFullState();});
c.waitFor(m->m.player.distanceToSqr(px,1,pz)<.05&&m.player.getMainHandItem().is(Statuary.ITEM),150);place(c,pos,direction.getOpposite());
c.waitFor(m->m.player.distanceToSqr(px,1,pz)<.05&&m.player.getMainHandItem().is(Statuary.ITEM),150);place(c,pos);
server.waitFor(s->player(s).level().getBlockState(pos).is(Statuary.BLOCK),100);
server.runOnServer(s->{var state=player(s).level().getBlockState(pos);check(state.getValue(Statuary.FACING)==direction&&state.getValue(Statuary.ANCHOR)==SculptureAnchor.CENTER,"Native center placement follows gaze on server");});
c.waitFor(m->m.level.getBlockEntity(pos) instanceof StatuaryEntity e&&source.equals(e.sculpture())&&e.getBlockState().getValue(Statuary.ANCHOR)==SculptureAnchor.CENTER,100);
c.runOnClient(m->Sculpture113ClientChecks.rendered((StatuaryEntity)m.level.getBlockEntity(pos)));
c.takeScreenshot("sculpture119-center-"+direction.getSerializedName());
server.runOnServer(s->{var p=player(s);p.level().removeBlock(pos,false);p.getInventory().setItem(p.getInventory().getSelectedSlot(),StatuaryEntity.item(source));p.inventoryMenu.broadcastFullState();});
c.waitFor(m->m.level.getBlockState(pos).isAir()&&m.player.getMainHandItem().is(Statuary.ITEM),100);place(c,pos,direction.getOpposite());
server.waitFor(s->player(s).level().getBlockState(pos).is(Statuary.BLOCK),100);
server.runOnServer(s->{var state=player(s).level().getBlockState(pos);check(state.getValue(Statuary.FACING)==direction,"Native placement faces gaze: "+direction);
check(state.getValue(Statuary.ANCHOR)==SculptureAnchor.of(direction.getOpposite()),"Aimed edge stored on server");
@@ -189,12 +196,14 @@ public final class Statuary106ClientChecks implements FabricClientGameTest {
server.runOnServer(s->check(player(s).level().getBlockState(pos).getValue(Statuary.ANCHOR)==SculptureAnchor.of(direction.getOpposite()),"Anchor survives server save/reload"));
c.waitFor(m->m.level.getBlockEntity(pos) instanceof StatuaryEntity e&&ORIGINAL.get().equals(e.sculpture())&&e.getBlockState().getValue(Statuary.FACING)==direction,150);
}
c.waitFor(m->m.level.getBlockEntity(SCULPTURE) instanceof StatuaryEntity e&&e.sculpture()!=null,150);c.runOnClient(m->check(((StatuaryEntity)m.level.getBlockEntity(SCULPTURE)).sculpture().equals(ORIGINAL.get()),"Fresh client receives server model without file"));
server.runOnServer(s->check(player(s).level().getBlockState(SCULPTURE).getValue(Statuary.ANCHOR)==SculptureAnchor.CENTER,"Centered placement survives server save/reload"));
c.waitFor(m->m.level.getBlockEntity(SCULPTURE) instanceof StatuaryEntity e&&e.sculpture()!=null,150);c.runOnClient(m->{check(((StatuaryEntity)m.level.getBlockEntity(SCULPTURE)).sculpture().equals(ORIGINAL.get()),"Fresh client receives server model without file");check(m.level.getBlockState(SCULPTURE).getValue(Statuary.ANCHOR)==SculptureAnchor.CENTER,"Fresh client receives centered anchor");});
c.getInput().lookAt(SCULPTURE);c.takeScreenshot("statuary106-reload-no-source");
}
System.out.println("STATUARY106_PASS GLB textured scene, bounded import, shared folder filters, native paid crafting, 16 clay colors, model ID, shift-click, place/drop/re-place, Metabli plan, cancellation, server save/reconnect without source");
System.out.println("STATUARY111_PASS nine-slice GUI FR/EN scales 2/3, four native gaze placements, renderer directions, structure rotation/mirror, legacy state default, drop/re-place and persisted orientation");
System.out.println("STATUARY113_PASS clay sculpture FR/EN, native aimed edges, voxel grid across every footprint/orientation/anchor, side placement, legacy states, rotated/mirrored anchor, save/reconnect");
System.out.println("SCULPTURE119_PASS native centered placements in four directions, center/edge boundaries, odd/even voxel grid, side anchoring and center save/reconnect");
System.out.println("STATUARY112_PASS fixed compact hotbar-only menu, native input transfer, hidden slots/offhand rejected, full hotbar no consumption, partial merge exact cost, closing restitution");
}catch(IOException e){throw new AssertionError(e);}finally{try{Files.deleteIfExists(file);}catch(IOException ignored){}}
}
@@ -7,7 +7,7 @@ import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
/** World-space edge of the occupied block; CENTER is the legacy placement. */
/** World-space anchor of the occupied block, always on the whole-voxel grid. */
public enum SculptureAnchor implements StringRepresentable {
CENTER(null), NORTH(Direction.NORTH), SOUTH(Direction.SOUTH), EAST(Direction.EAST), WEST(Direction.WEST);
@@ -23,7 +23,8 @@ public enum SculptureAnchor implements StringRepresentable {
public static SculptureAnchor placement(BlockPlaceContext context){
var pos=context.getClickedPos();var hit=context.getClickLocation();
double x=hit.x-pos.getX()-.5,z=hit.z-pos.getZ()-.5;
if(Math.abs(x)<1e-7&&Math.abs(z)<1e-7)return of(context.getHorizontalDirection().getOpposite());
// The middle 8 x 8 pixels select the existing grid-aligned center.
if(Math.abs(x)<=.25&&Math.abs(z)<=.25)return CENTER;
if(Math.abs(x)>Math.abs(z))return x<0?WEST:EAST;
return z<0?NORTH:SOUTH;
}
@@ -2162,7 +2162,7 @@
"sanctuary.statuary.folder": "Shared folder",
"sanctuary.statuary.refresh": "Refresh",
"sanctuary.statuary.models": "GLB models",
"sanctuary.statuary.placement": "The sculpture faces your gaze and snaps to the aimed block edge, on the pixel grid. Rotate adjusts the model before crafting.",
"sanctuary.statuary.placement": "Aim at the middle of the block to center the sculpture, or at an edge to snap to it. It faces your gaze and stays on the pixel grid. Rotate adjusts the model before crafting.",
"sanctuary.statuary.rotate": "Rotate",
"sanctuary.statuary.working": "Voxelizing to 16 × 16 × 16…",
"sanctuary.statuary.ready": "1 block = 1 sculpture. Shades come from your materials texture.",
@@ -2162,7 +2162,7 @@
"sanctuary.statuary.folder": "Dossier commun",
"sanctuary.statuary.refresh": "Actualiser",
"sanctuary.statuary.models": "Modèles GLB",
"sanctuary.statuary.placement": "La sculpture suit votre regard et se cale contre le bord visé du bloc, sur la grille de pixels. Tourner ajuste le modèle avant fabrication.",
"sanctuary.statuary.placement": "Visez le milieu du bloc pour centrer la sculpture, ou un bord pour ly caler. Elle suit votre regard et reste sur la grille de pixels. Tourner ajuste le modèle avant fabrication.",
"sanctuary.statuary.rotate": "Tourner",
"sanctuary.statuary.working": "Voxelisation en 16 × 16 × 16…",
"sanctuary.statuary.ready": "1 bloc = 1 sculpture. Les nuances viennent de la texture de votre matière.",
+1 -1
View File
@@ -1,6 +1,6 @@
name = "Sanctuary"
author = "KOKA99CAB"
version = "beta.118"
version = "beta.119"
pack-format = "packwiz:1.1.0"
[index]