Expand material support and checkpoint the completed building toolkit

Expose the runtime block/item registry through compact material search and
single-material descriptions. Preserve private block-entity data through
checked edits and durable undo without sending payloads to model context.

Include the completed terrain tools, isolated world/map plugin, station lift,
ACP streaming and guidance fixes, local camera auto-connect, construction
scripts, and their public documentation, references and verification records.
Active station decoration and private runtime data remain outside this commit.

Validation: 145 Maven tests, 47 Bridge tests, successful camera Gradle build,
and isolated Paper verification of all 1,196 block defaults plus 5,392
independent property cases for placement, same-material edits and restoration.
This commit is contained in:
Emil
2026-09-13 16:59:13 +03:00
parent 0290559abf
commit 89c53d7058
179 changed files with 37719 additions and 162 deletions
+83
View File
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""Render a survey atlas from live surface data; optional blocks are a labelled preview."""
import argparse
import json
import math
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.colors import to_rgb, LightSource
ROOT=Path(__file__).resolve().parents[1]
COLOR={'01':'#98d84d','02':'#f6d34a','03':'#9460ce','04':'#23b6b6','05':'#e3544b',
'06':'#f78d27','07':'#f0f0e6','08':'#4a69d8','09':'#f394b5'}
BLOCK={'lime':'#98d84d','yellow':'#f6d34a','purple':'#9460ce','cyan':'#23b6b6',
'green':'#527c31','orange':'#f78d27','white':'#f0f0e6','blue':'#4a69d8',
'pink':'#f394b5','black':'#26282d','light_gray':'#a4aaa6','red':'#e3544b'}
def main():
p=argparse.ArgumentParser();p.add_argument('surface',type=Path);p.add_argument('--study',type=Path,required=True)
p.add_argument('--blocks',type=Path);p.add_argument('--output',type=Path,required=True)
args=p.parse_args();s=json.loads(args.surface.read_text());study=json.loads(args.study.read_text())
h=np.array(s['surface_y']).reshape(s['length'],s['width']).astype(float)
indexes=np.array(s['material_index']).reshape(h.shape);palette=list(s['palette']);colors=list(s['palette_rgb'])
if args.blocks:
for b in json.loads(args.blocks.read_text())['blocks']:
x,z=b['x']-s['min_x'],b['z']-s['min_z']
if b['y']<h[z,x]:continue
material=b['block']
if material not in palette:
palette.append(material)
colors.append(BLOCK.get(material.replace('minecraft:','').replace('_concrete',''),'#f4cf58'))
indexes[z,x]=palette.index(material);h[z,x]=b['y']
rgb=np.array([to_rgb(c) for c in colors])[indexes]
dz,dx=np.gradient(h);norm=np.sqrt(dx*dx+dz*dz+1)
shade=np.clip(.76+.36*(.55*dx+.55*dz+.63)/norm,.44,1.13)
categorical=np.array([m.endswith('_concrete') or m=='minecraft:glowstone' for m in palette])[indexes]
water=np.array([m=='minecraft:water' for m in palette])[indexes]
shade=np.where(categorical,1,np.where(water,.96,shade))
rgb=np.clip(rgb*shade[...,None],0,1)
bg='#14211e';fg='#e8eade';muted='#a9bcb0'
fig=plt.figure(figsize=(16,12),facecolor=bg)
ax=fig.add_axes([.045,.12,.67,.80],facecolor=bg)
ax.imshow(rgb,extent=(s['min_x']-.5,s['max_x']+.5,s['max_z']+.5,s['min_z']-.5),interpolation='nearest')
ax.set_xlim(-320,319);ax.set_ylim(384,-255)
ax.set_xticks(np.arange(-256,320,64));ax.set_yticks(np.arange(-192,384,64))
ax.tick_params(colors=muted,labelsize=9);ax.set_xlabel('X / blocks',color=muted,labelpad=8)
ax.set_ylabel('Z / blocks · north up',color=muted,labelpad=8)
for spine in ax.spines.values():spine.set_color('#607166')
for d in study['districts']:
x,z=d['label'];ident=d['id']
ax.text(x,z-13,ident,ha='center',va='center',fontsize=12,fontweight='bold',color='#14211e',
bbox=dict(boxstyle='circle,pad=.30',facecolor=COLOR[ident],edgecolor=fg,linewidth=1.1),zorder=9)
ax.annotate('N',xy=(287,-224),xytext=(287,-198),ha='center',color=fg,fontsize=12,
arrowprops=dict(arrowstyle='-|>',color=fg,lw=1.5))
ax.plot([-288,-224],[355,355],color=fg,lw=3);ax.text(-256,370,'64 blocks',color=fg,ha='center',fontsize=9)
fig.text(.06,.953,'SHACRAFT',color=fg,fontsize=28,fontweight='bold')
fig.text(.242,.958,'LOBBY / SITE MARKING',color=muted,fontsize=16)
fig.text(.746,.881,'DISTRICTS',color=muted,fontsize=12,fontweight='bold')
descriptions={
'01':'Hexagonal arrival plaza\nCentral Shacraft medallion',
'02':'Clock tower + station hall\nPavilions and forecourt',
'03':'Portal concourse\nSix individual portal bays',
'04':'Airship terminal\nThree piers + flagship reserve',
'05':'Palm house · winter garden\nObservatory and garden walks',
'06':'Market square\nSix separate building plots',
'07':'Arrival avenue and viaduct\nSouthern entrance to the valley',
'08':'Lake pumping house\nBoardwalk and viewing terrace',
'09':'Scenic overlooks\nSmall optional ridge trails'}
for i,d in enumerate(study['districts']):
y=.842-i*.067;ident=d['id']
fig.text(.749,y,ident,color=COLOR[ident],fontsize=16,fontweight='bold')
fig.text(.779,y,descriptions[ident],color=fg,fontsize=10,linespacing=1.55)
fig.text(.747,.20,'READING THE MARKS',color=muted,fontsize=11,fontweight='bold')
fig.text(.747,.169,'Color = building / courtyard boundary\nWhite = future path edges\nRaised ribs = bridge deck reservation\nLit stakes = corners and wayfinding',color=fg,fontsize=9,linespacing=1.7,va='top')
note='DESIGN PREVIEW · proposed blocks over a live survey' if args.blocks else 'ACTUAL WORLD SURFACE · captured after placement · no player camera required'
fig.text(.06,.055,note,color=fg,fontsize=10)
fig.text(.06,.035,'768 × 768 world · central development shown · terrain heights and water preserved · paths and stairs are still reservations',color=muted,fontsize=9)
args.output.parent.mkdir(parents=True,exist_ok=True);fig.savefig(args.output,dpi=150,facecolor=bg);plt.close(fig)
print(args.output)
if __name__=='__main__':main()