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.
385 lines
20 KiB
Python
385 lines
20 KiB
Python
#!/usr/bin/env python3
|
|
"""Read-only proof of a closed, observed full-cube Minecraft zone enclosure.
|
|
|
|
The membrane is derived independently from an extruded XZ mask minus explicit
|
|
excluded voxels: every six-neighbor exterior shell voxel must be a full cube. An
|
|
exterior flood treats every unknown/partial block as empty, overestimating escape
|
|
routes. Swept 0.6 x 1.8 player AABB probes additionally test cardinal/diagonal
|
|
crossings, half-block stair rises, floor drops and roof ascent. These sample
|
|
probes supplement the complete membrane proof; they are not the proof itself.
|
|
|
|
This verifies static continuous movement, not spectator, commands, breaking,
|
|
plugin teleportation, or arbitrary discontinuous teleport/pearl behavior.
|
|
"""
|
|
import argparse
|
|
from collections import Counter, deque
|
|
import hashlib
|
|
import importlib.util
|
|
import itertools
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
import unittest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SPEC = importlib.util.spec_from_file_location('containment_base', ROOT / 'scripts/verify-balustrade.py')
|
|
base = importlib.util.module_from_spec(SPEC)
|
|
SPEC.loader.exec_module(base)
|
|
survey, plaza = base.survey, base.plaza
|
|
FULL = plaza.STATIC_CUBES | {'barrier'}
|
|
N2 = ((1, 0), (-1, 0), (0, 1), (0, -1))
|
|
N3 = ((1, 0, 0), (-1, 0, 0), (0, 1, 0), (0, -1, 0), (0, 0, 1), (0, 0, -1))
|
|
WIDTH, HEIGHT = .6, 1.8
|
|
|
|
|
|
def full_cube(state):
|
|
return plaza.parse(state)[0] in FULL
|
|
|
|
|
|
def mask_geometry(metadata):
|
|
raw = metadata['interior_columns']
|
|
if (not isinstance(raw, list) or not raw or any(not isinstance(p, list) or len(p) != 2
|
|
or any(type(v) is not int for v in p) for p in raw)):
|
|
raise ValueError('interior_columns must be nonempty integer [x,z] pairs')
|
|
interior = set(map(tuple, raw))
|
|
if len(interior) != len(raw):
|
|
raise ValueError('Duplicate interior column')
|
|
floor, roof = metadata['floor_y'], metadata['roof_y']
|
|
if type(floor) is not int or type(roof) is not int or roof - floor < 4:
|
|
raise ValueError('Integer floor_y and roof_y need at least three free interior rows')
|
|
shell = {(x + dx, z + dz) for x, z in interior for dx, dz in N2} - interior
|
|
footprint = interior | shell
|
|
reached, queue = set(), deque([next(iter(interior))])
|
|
while queue:
|
|
p = queue.popleft()
|
|
if p in reached or p not in interior:
|
|
continue
|
|
reached.add(p)
|
|
queue.extend((p[0] + dx, p[1] + dz) for dx, dz in N2)
|
|
if reached != interior:
|
|
raise ValueError('Interior mask must be one cardinally connected component')
|
|
for key in ('wall_columns', 'shell_columns'):
|
|
if ('interior_excluded_voxels' not in metadata and key in metadata
|
|
and set(map(tuple, metadata[key])) != shell):
|
|
raise ValueError(f'Claimed {key} differ from the independently derived complete shell')
|
|
bounds = ((min(x for x, z in footprint) - 1, floor - 1, min(z for x, z in footprint) - 1),
|
|
(max(x for x, z in footprint) + 1, roof + 1, max(z for x, z in footprint) + 1))
|
|
if math.prod(hi - lo + 1 for lo, hi in zip(*bounds)) > survey.MAX_VOXELS:
|
|
raise ValueError('Enclosure verification exceeds the bounded snapshot voxel limit')
|
|
raw_excluded = metadata.get('interior_excluded_voxels', [])
|
|
if (not isinstance(raw_excluded, list) or any(not isinstance(p, list) or len(p) != 3
|
|
or any(type(v) is not int for v in p) for p in raw_excluded)):
|
|
raise ValueError('interior_excluded_voxels must contain integer [x,y,z] triples')
|
|
excluded = set(map(tuple, raw_excluded))
|
|
if len(excluded) != len(raw_excluded):
|
|
raise ValueError('Duplicate excluded interior voxel')
|
|
volume = {(x, y, z) for x, z in interior for y in range(floor + 1, roof)}
|
|
if not excluded <= volume:
|
|
raise ValueError('Excluded voxel is outside the base extruded interior')
|
|
volume -= excluded
|
|
if not volume:
|
|
raise ValueError('Excluded voxels remove the whole enclosure interior')
|
|
membrane_voxels = {(x + dx, y + dy, z + dz) for x, y, z in volume for dx, dy, dz in N3} - volume
|
|
return interior, shell, footprint, floor, roof, bounds, volume, membrane_voxels, excluded
|
|
|
|
|
|
def membrane(reader, geometry):
|
|
interior, _, _, floor, roof, _ = geometry[:6]
|
|
failures, materials, counts = [], Counter(), Counter()
|
|
for x, y, z in sorted(geometry[7]):
|
|
kind = ('floor' if y == floor else 'roof' if y == roof else
|
|
'wall' if (x, z) not in interior else 'folded_boundary')
|
|
counts[kind] += 1
|
|
state = reader.state(x, y, z)
|
|
materials[plaza.parse(state)[0]] += 1
|
|
if not full_cube(state):
|
|
failures.append({'at': [x, y, z], 'part': kind, 'actual': state})
|
|
return {'passed': not failures, 'required_voxels': sum(counts.values()), 'parts': dict(counts),
|
|
'observed_materials': dict(materials), 'nonfull_voxels': len(failures), 'examples': failures[:30],
|
|
'method': 'Every voxel in N6(interior volume) minus interior volume must be an observed full collision cube.'}
|
|
|
|
|
|
def conservative_exterior_flood(reader, geometry, source):
|
|
lo, hi = geometry[5]
|
|
blocked, passable, unknown = set(), set(), Counter()
|
|
boundary = []
|
|
for x in range(lo[0], hi[0] + 1):
|
|
for y in range(lo[1], hi[1] + 1):
|
|
for z in range(lo[2], hi[2] + 1):
|
|
p = x, y, z
|
|
state = reader.state(*p)
|
|
if full_cube(state):
|
|
blocked.add(p)
|
|
continue
|
|
passable.add(p)
|
|
if state not in survey.AIR:
|
|
unknown[plaza.parse(state)[0]] += 1
|
|
if any(v in (low, high) for v, low, high in zip(p, lo, hi)):
|
|
boundary.append(p)
|
|
reached, queue = set(boundary), deque(boundary)
|
|
while queue:
|
|
x, y, z = queue.popleft()
|
|
for dx, dy, dz in N3:
|
|
p = x + dx, y + dy, z + dz
|
|
if p in passable and p not in reached:
|
|
reached.add(p)
|
|
queue.append(p)
|
|
leaks = sorted(reached & geometry[6])
|
|
source_cell = tuple(math.floor(v) for v in source)
|
|
return {'passed': not leaks, 'observed_voxels': len(blocked) + len(passable),
|
|
'exterior_reached_voxels': len(reached), 'interior_reached_from_exterior': len(leaks),
|
|
'source_reached_from_exterior': source_cell in reached,
|
|
'unknown_or_partial_blocks_treated_as_empty': dict(unknown), 'leak_examples': leaks[:30],
|
|
'method': 'Six-neighbor free-voxel exterior flood; only known full collision cubes obstruct it.'}
|
|
|
|
|
|
def segment_box(start, finish, low, high):
|
|
"""Slab intersection with the interior of an expanded block AABB.
|
|
|
|
Face contact is legal: a player's feet resting exactly on paving must not
|
|
make every horizontal probe appear blocked before reaching the guard.
|
|
"""
|
|
enter, leave = 0., 1.
|
|
for a, b, lo, hi in zip(start, finish, low, high):
|
|
lo, hi = lo + 1e-9, hi - 1e-9
|
|
delta = b - a
|
|
if abs(delta) < 1e-12:
|
|
if a < lo or a > hi:
|
|
return False
|
|
continue
|
|
first, last = sorted(((lo - a) / delta, (hi - a) / delta))
|
|
enter, leave = max(enter, first), min(leave, last)
|
|
if enter > leave:
|
|
return False
|
|
return True
|
|
|
|
|
|
def swept_player_hits_full_cube(reader, start, finish):
|
|
radius = WIDTH / 2
|
|
low = [math.floor(min(start[i], finish[i]) - (radius if i != 1 else 0)) for i in range(3)]
|
|
high = [math.floor(max(start[i], finish[i]) + (radius if i != 1 else HEIGHT)) for i in range(3)]
|
|
for x in range(low[0], high[0] + 1):
|
|
for y in range(low[1], high[1] + 1):
|
|
for z in range(low[2], high[2] + 1):
|
|
if full_cube(reader.state(x, y, z)) and segment_box(
|
|
start, finish, (x - radius, y - HEIGHT, z - radius),
|
|
(x + 1 + radius, y + 1, z + 1 + radius)):
|
|
return True
|
|
return False
|
|
|
|
|
|
def player_probes(reader, geometry, source):
|
|
interior, _, _, floor, roof, _ = geometry[:6]
|
|
counts, missed = Counter(), []
|
|
source_clear = all(reader.state(x, y, z) in survey.AIR
|
|
for x in range(math.floor(source[0] - WIDTH / 2), math.floor(source[0] + WIDTH / 2) + 1)
|
|
for y in range(math.floor(source[1]), math.ceil(source[1] + HEIGHT))
|
|
for z in range(math.floor(source[2] - WIDTH / 2), math.floor(source[2] + WIDTH / 2) + 1))
|
|
# Cross each boundary in cardinal and diagonal directions, including half-
|
|
# block stair rises/drops. Fixed-height samples also cover free flight rows.
|
|
heights = sorted({float(floor + 1), float(source[1]), roof - HEIGHT - .05})
|
|
for x, z in sorted(interior):
|
|
for dx, dz in itertools.product((-1, 0, 1), repeat=2):
|
|
if (dx == dz == 0) or (x + dx, z + dz) in interior:
|
|
continue
|
|
for y in heights:
|
|
for dy in (-.5, 0., .5):
|
|
start = (x + .5, y, z + .5)
|
|
finish = (x + dx + .5, y + dy, z + dz + .5)
|
|
kind = 'diagonal' if dx and dz else 'cardinal'
|
|
counts[kind] += 1
|
|
if not swept_player_hits_full_cube(reader, start, finish):
|
|
missed.append({'kind': kind, 'from': start, 'to': finish})
|
|
# Test the locally folded boundary as well as the original outer perimeter.
|
|
# The complete membrane check covers every height even when a sample starts
|
|
# in an already obstructed partial-height pocket.
|
|
for x, y, z in sorted(geometry[8] & geometry[7]):
|
|
for dx, dy, dz in N3:
|
|
p = x + dx, y + dy, z + dz
|
|
if p not in geometry[6]:
|
|
continue
|
|
start, finish = (p[0] + .5, float(p[1]), p[2] + .5), (x + .5, float(y), z + .5)
|
|
counts['folded_boundary'] += 1
|
|
if not swept_player_hits_full_cube(reader, start, finish):
|
|
missed.append({'kind': 'folded_boundary', 'from': start, 'to': finish})
|
|
for kind, start, finish in [('floor', (source[0], floor + 1.1, source[2]),
|
|
(source[0], floor - .5, source[2])),
|
|
('roof', (source[0], roof - HEIGHT - .1, source[2]),
|
|
(source[0], roof - .5, source[2]))]:
|
|
counts[kind] += 1
|
|
if not swept_player_hits_full_cube(reader, start, finish):
|
|
missed.append({'kind': kind, 'from': start, 'to': finish})
|
|
return {'passed': source_clear and not missed, 'player_width': WIDTH, 'player_height': HEIGHT,
|
|
'source_headroom_observed_air': source_clear, 'swept_crossing_probes': dict(counts),
|
|
'unblocked_probes': len(missed), 'examples': missed[:30],
|
|
'note': 'Supplementary swept-AABB boundary probes; the independently derived full membrane is the complete static containment criterion.'}
|
|
|
|
|
|
def volume_components(reader, geometry, source):
|
|
"""Report geometric components; every one is still checked for containment."""
|
|
pending, components = set(geometry[6]), []
|
|
source_cell = tuple(math.floor(v) for v in source)
|
|
while pending:
|
|
queue, count, nonfull, contains_source = deque([next(iter(pending))]), 0, 0, False
|
|
while queue:
|
|
p = queue.popleft()
|
|
if p not in pending:
|
|
continue
|
|
pending.remove(p)
|
|
count += 1
|
|
nonfull += not full_cube(reader.state(*p))
|
|
contains_source |= p == source_cell
|
|
queue.extend((p[0] + dx, p[1] + dy, p[2] + dz) for dx, dy, dz in N3)
|
|
components.append({'voxels': count, 'nonfull_voxels': nonfull, 'contains_source': contains_source})
|
|
return {'components': sorted(components, key=lambda c: c['voxels'], reverse=True),
|
|
'source_in_volume': any(c['contains_source'] for c in components),
|
|
'note': 'Disconnected sealed components are reported, not treated as escapes. The entire required membrane and exterior flood are checked.'}
|
|
|
|
|
|
def preserved_decor(before, reader, desired):
|
|
protected, failures = 0, []
|
|
for at, state in base.captured_voxels(before):
|
|
name, _ = plaza.parse(state)
|
|
if (name not in plaza.FLOWERS | {'lantern', 'spruce_fence', 'spruce_stairs', 'iron_chain'}
|
|
and not any(suffix in name for suffix in ('_leaves', '_log', 'waxed_oxidized_cut_copper'))):
|
|
continue
|
|
protected += 1
|
|
if reader.state(*at) != state:
|
|
failures.append({'at': list(at), 'before': state, 'after': reader.state(*at), 'in_plan': at in desired})
|
|
return {'passed': not failures, 'protected_observed_states': protected, 'changed_states': len(failures),
|
|
'examples': failures[:30]}
|
|
|
|
|
|
def audit(before, layout, metadata, after=None):
|
|
scope = survey.scope_of(layout['scope'])
|
|
if scope != before.scope or scope != metadata['scope'] or (after and after.scope != scope):
|
|
raise ValueError('Project/world/epoch differs between containment inputs')
|
|
desired = base.coordinates(layout)
|
|
for row in layout['blocks']:
|
|
p = tuple(row[a] for a in ('x', 'y', 'z'))
|
|
if before.state(*p) != row['expected']:
|
|
raise ValueError(f'Expected plan state differs from observed baseline at {p}')
|
|
source = tuple(metadata['source'][axis] for axis in ('x', 'y', 'z'))
|
|
if any(type(v) not in (int, float) or not math.isfinite(v) for v in source):
|
|
raise ValueError('Source must contain finite x/y/z player feet coordinates')
|
|
geometry = mask_geometry(metadata)
|
|
if tuple(math.floor(v) for v in source) not in geometry[6] or not geometry[3] < source[1] < geometry[4] - HEIGHT:
|
|
raise ValueError('Source is outside the enclosure interior')
|
|
reader = plaza.Reader(after or before, None if after else desired)
|
|
closed = membrane(reader, geometry)
|
|
flood = conservative_exterior_flood(reader, geometry, source)
|
|
probes = player_probes(reader, geometry, source)
|
|
components = volume_components(reader, geometry, source)
|
|
decor = preserved_decor(before, reader, desired)
|
|
volume = base.compare_volume(before, after, desired) if after else None
|
|
return {'version': 1, 'scope': scope, 'world_edits': 0,
|
|
'mode': 'actual_after_snapshot' if after else 'candidate_overlay',
|
|
'desired_blocks': len(desired), 'interior_columns': len(geometry[0]),
|
|
'independently_derived_wall_columns': len(geometry[1]),
|
|
'interior_voxels': len(geometry[6]), 'interior_excluded_voxels': len(geometry[8]),
|
|
'volume_components': components,
|
|
'membrane': closed, 'exterior_flood': flood, 'player_probes': probes,
|
|
'preserved_decor': decor, 'volume': volume,
|
|
'passed': all(r['passed'] for r in (closed, flood, probes, decor)) and (volume is None or volume['passed']),
|
|
'limitations': 'Static continuous collision containment for normal nonspectator players only. No protection against spectator, breaking/removing blocks, operator commands, plugin teleports or arbitrary discontinuous teleport/pearl behavior. Actual snapshots are sequential, not atomic.'}
|
|
|
|
|
|
class ContainmentTests(unittest.TestCase):
|
|
def scene(self):
|
|
meta = {'interior_columns': [[0, 0], [1, 0], [0, 1], [1, 1]], 'floor_y': 0, 'roof_y': 5}
|
|
geo = mask_geometry(meta)
|
|
states = {}
|
|
for x, z in geo[2]:
|
|
for y in ([0, 5] if (x, z) in geo[0] else range(6)):
|
|
states[x, y, z] = 'minecraft:barrier'
|
|
reader = type('Reader', (), {'state': lambda self, x, y, z: states.get((x, y, z), 'minecraft:air')})()
|
|
return meta, geo, states, reader
|
|
|
|
def test_complete_shell_blocks_flight_diagonals_stairs_and_drops(self):
|
|
_, geo, _, reader = self.scene()
|
|
self.assertTrue(membrane(reader, geo)['passed'])
|
|
self.assertTrue(conservative_exterior_flood(reader, geo, (.5, 1., .5))['passed'])
|
|
self.assertTrue(player_probes(reader, geo, (.5, 1., .5))['passed'])
|
|
|
|
def test_one_missing_wall_or_roof_voxel_leaks(self):
|
|
for at in [(-1, 2, 0), (0, 5, 0), (0, 0, 0)]:
|
|
_, geo, states, reader = self.scene()
|
|
del states[at]
|
|
self.assertFalse(membrane(reader, geo)['passed'])
|
|
self.assertFalse(conservative_exterior_flood(reader, geo, (.5, 1., .5))['passed'])
|
|
|
|
def test_floor_need_not_extend_under_exterior_wall(self):
|
|
_, geo, states, reader = self.scene()
|
|
for x, z in geo[1]:
|
|
states.pop((x, 0, z), None)
|
|
self.assertTrue(membrane(reader, geo)['passed'])
|
|
self.assertTrue(conservative_exterior_flood(reader, geo, (.5, 1., .5))['passed'])
|
|
|
|
def test_inward_fold_preserves_partial_stair_outside_and_missing_fold_leaks(self):
|
|
meta, _, states, reader = self.scene()
|
|
meta['interior_excluded_voxels'] = [[0, 2, 0]]
|
|
geo = mask_geometry(meta)
|
|
states[-1, 2, 0] = 'minecraft:stone_brick_stairs[facing=east,half=bottom,shape=straight,waterlogged=false]'
|
|
states[0, 2, 0] = 'minecraft:barrier'
|
|
self.assertNotIn((-1, 2, 0), geo[7])
|
|
self.assertTrue(membrane(reader, geo)['passed'])
|
|
self.assertTrue(conservative_exterior_flood(reader, geo, (1.5, 1., 1.5))['passed'])
|
|
self.assertTrue(player_probes(reader, geo, (1.5, 1., 1.5))['passed'])
|
|
del states[0, 2, 0]
|
|
self.assertFalse(membrane(reader, geo)['passed'])
|
|
self.assertFalse(conservative_exterior_flood(reader, geo, (1.5, 1., 1.5))['passed'])
|
|
|
|
def test_exclusions_must_be_unique_voxels_inside_original_volume(self):
|
|
meta, _, _, _ = self.scene()
|
|
for exclusions in [[[0, 2, 0], [0, 2, 0]], [[100, 2, 100]], [[0, 0, 0]]]:
|
|
meta['interior_excluded_voxels'] = exclusions
|
|
with self.assertRaises(ValueError):
|
|
mask_geometry(meta)
|
|
|
|
def test_stair_or_slab_cannot_substitute_for_solid_membrane(self):
|
|
_, geo, states, reader = self.scene()
|
|
states[-1, 2, 0] = 'minecraft:stone_brick_slab[type=bottom,waterlogged=false]'
|
|
self.assertFalse(membrane(reader, geo)['passed'])
|
|
self.assertFalse(conservative_exterior_flood(reader, geo, (.5, 1., .5))['passed'])
|
|
states[-1, 2, 0] = 'minecraft:stone'
|
|
self.assertTrue(membrane(reader, geo)['passed'])
|
|
|
|
def test_swept_aabb_detects_diagonal_corner_and_vertical_collision(self):
|
|
reader = type('Reader', (), {'state': lambda self, x, y, z: 'minecraft:barrier' if (x, y, z) == (1, 1, 1) else 'minecraft:air'})()
|
|
self.assertTrue(swept_player_hits_full_cube(reader, (.5, 1., .5), (2.5, 1., 2.5)))
|
|
self.assertFalse(swept_player_hits_full_cube(reader, (.2, 1., .2), (.2, 2., .2)))
|
|
|
|
def test_standing_contact_with_floor_does_not_mask_an_open_horizontal_route(self):
|
|
reader = type('Reader', (), {'state': lambda self, x, y, z: 'minecraft:stone' if y == 0 else 'minecraft:air'})()
|
|
self.assertFalse(swept_player_hits_full_cube(reader, (.5, 1., .5), (2.5, 1., 2.5)))
|
|
self.assertTrue(swept_player_hits_full_cube(reader, (.5, 1., .5), (.5, .5, .5)))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--before', type=Path)
|
|
parser.add_argument('--after', type=Path)
|
|
parser.add_argument('--layout', type=Path)
|
|
parser.add_argument('--metadata', type=Path)
|
|
parser.add_argument('--report', type=Path)
|
|
parser.add_argument('--self-test', action='store_true')
|
|
args = parser.parse_args()
|
|
if args.self_test:
|
|
result = unittest.TextTestRunner().run(unittest.defaultTestLoader.loadTestsFromTestCase(ContainmentTests))
|
|
raise SystemExit(0 if result.wasSuccessful() else 1)
|
|
if any(getattr(args, key) is None for key in ('before', 'layout', 'metadata', 'report')):
|
|
parser.error('--before, --layout, --metadata and --report are required')
|
|
before = survey.load_snapshot(args.before)
|
|
after = survey.load_snapshot(args.after, before.scope) if args.after else None
|
|
report = audit(before, json.loads(args.layout.read_text()), json.loads(args.metadata.read_text()), after)
|
|
paths = {key: getattr(args, key) for key in ('before', 'after', 'layout', 'metadata') if getattr(args, key)}
|
|
report['input_sha256'] = {key: hashlib.sha256(path.read_bytes()).hexdigest() for key, path in paths.items()}
|
|
args.report.parent.mkdir(parents=True, exist_ok=True)
|
|
survey.terrain.save(args.report, report)
|
|
print(json.dumps(report, indent=2))
|
|
raise SystemExit(0 if report['passed'] else 1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|