Phase 6: shell preview, core tooltips, machine test world, faster furnace

Adrian (2026-11-02): double the furnace speed; there must be a way in
game to see how a machine is built; a test world with the machines.

- Large Furnace speed doubled (2 + 0.08 x level + 0.02 x SpA).
- Ghost preview: placing a core, using it with an incomplete shell, or
  sneak-using it empty shows every wrong spot as a small glowing block
  display for 10 s (never saved), in the core's orientation, with a
  chat line counting what's missing.
- Core tooltips: what the machine does, what its shell is made of.
- /cobblesurvival machines showcase builds every machine three times
  (working with a Showcase-owned Pokemon, empty, bare core) with signs,
  hoppers, chests and cables; tools/make_machine_world.py makes a
  superflat creative world from it (level.dat via tools/nbtfile.py)
  and --prism installs it as "Machine Test (Phase 6)".
- Self-tests machine_preview_shows_the_shell and
  machine_showcase_builds_every_machine; docs and checklist.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
claudeandClaude Opus 5.5 committed 2026-11-02 15:42:14 +01:00
1 parent 0877a6a179
commit c2a87f178e
17 files changed
+558 -27

No files matched your search

+91
View File
@@ -0,0 +1,91 @@
#!/usr/bin/env python3
"""Make the machine test world (Phase 6; Adrian, 2026-11-02): a superflat creative world, cheats on, with every
machine built by `/cobblesurvival machines showcase` (working with a Pokémon inside, empty, and the bare core),
no wild Pokémon spawning and fixed daytime. It lands in build/machine-world; with --prism it is also copied into the
Prism instance as the save "Machine Test (Phase 6)", replacing an older copy.
Needs the test server (tools/build.py first; the EULA accepted as for the other tools).
Usage: tools/make_machine_world.py [--prism] [--instance NAME]
"""
import argparse
import shutil
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
import nbtfile # noqa: E402
from make_world import SKIP # noqa: E402
from mcserver import BUILD, SERVER, Server, eula_accepted, prepare, scan_log # noqa: E402
from prism_instance import PRISM_DIRS # noqa: E402
LEVEL = "machines"
NAME = "Machine Test (Phase 6)"
OUT = BUILD / "machine-world"
FLAT = ('{"layers":[{"block":"minecraft:bedrock","height":1},{"block":"minecraft:stone","height":3},'
'{"block":"minecraft:grass_block","height":1}],"biome":"minecraft:plains"}')
# Ground is y -60; the showcase starts at the first air above it.
ORIGIN = (0, -59, 0)
SPAWN = (24, -59, 8)
COMMANDS = [
"gamerule doPokemonSpawning false",
"gamerule doDaylightCycle false",
"gamerule doWeatherCycle false",
"time set day",
f"setworldspawn {SPAWN[0]} {SPAWN[1]} {SPAWN[2]}",
f"execute positioned {ORIGIN[0]} {ORIGIN[1]} {ORIGIN[2]} run cobblesurvival machines showcase",
]
def make_creative(level_dat):
"""Creative mode, cheats on, and a name the world list can tell apart."""
name, root = nbtfile.load(level_dat)
data = root["Data"][1]
data["LevelName"] = (nbtfile.STRING, NAME)
data["GameType"] = (nbtfile.INT, 1)
data["allowCommands"] = (nbtfile.BYTE, 1)
nbtfile.save(level_dat, name, root)
def main():
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument("--prism", action="store_true", help="copy the world into the Prism instance")
parser.add_argument("--instance", default="Cobblemon Survival Pack (dev)")
args = parser.parse_args()
if not eula_accepted():
sys.exit("build/server/eula.txt does not accept the Minecraft EULA yet (see tools/selftest.py --accept-eula).")
args_file = prepare()
world = SERVER / LEVEL
shutil.rmtree(world, ignore_errors=True)
props = {"level-name": LEVEL, "level-type": "minecraft:flat", "generator-settings": FLAT, "gamemode": "creative"}
with Server(args_file, props, stdout_name="machine-world-stdout.log") as server:
print(f"server started in {server.wait_ready():.0f}s")
for command in COMMANDS:
print(f" {command}: {server.command(command).strip()}")
server.command("save-all flush")
server.stop()
problems = scan_log(server.log)
if problems:
sys.exit("server log problems:\n" + "\n".join(f" - {p}" for p in problems))
shutil.rmtree(OUT, ignore_errors=True)
shutil.copytree(world, OUT, ignore=SKIP)
make_creative(OUT / "level.dat")
print(f"machine world: {OUT.relative_to(BUILD.parent)}")
if args.prism:
prism = next((d for d in PRISM_DIRS if d.exists()), None)
if prism is None:
sys.exit("no Prism Launcher data folder found")
saves = prism / "instances" / args.instance / "minecraft" / "saves"
if not saves.parent.exists():
sys.exit(f"no Prism instance {args.instance!r}; run tools/prism_instance.py first")
target = saves / NAME
shutil.rmtree(target, ignore_errors=True)
shutil.copytree(OUT, target)
print(f"installed as the save {NAME!r} in {args.instance}")
if __name__ == "__main__":
main()