Files
jesse 8808b919c3 Rebuild Daikoku Futo PA from measured real-world data
The PA was a placeholder: 9 painted stripes and 4 lights on a slab.
Rebuilt it against sourced figures rather than recollection.

Sources:
  * OpenStreetMap way 59608205 (大黒ふ頭パーキングエリア) -- the apron is
    181 m x 174 m / 1.6 ha, i.e. essentially SQUARE, not circular.
  * ja.wikipedia 大黒パーキングエリア -- 首都高速で最大のPA, opened
    1989-09-27. Capacity 小型 341 + 大型 59 + 身障者用 4 = 404, now
    reproduced EXACTLY (11 rows x 31, 20/20/19, 1 row x 4).
  * 本館 on the west side, ベイサイドテラス二番館 on the north, per the
    same article. EV rapid charger (first on a Japanese expressway,
    2008-10-10) and the 段差 bumps retrofitted to deter lowered cars.

Stall dimensions (小型 2.5x5.0 m, 大型 3.3x12 m, 6 m aisles) are
conventional JP parking-design values and are commented as NOT
primary-sourced, unlike the 全標協 road markings. Building footprints
are art-directed to the aerial and flagged as such.

tools/verify_daikoku_pa.luau asserts capacity, containment, block
separation and building placement in-engine: 20 passed, 0 failed.

Two bugs found and fixed by that harness:
  * The hand-placed 小型 Z origin overlapped the 大型 bays by 42 studs.
    The origin is now DERIVED from the 二番館 frontage; clearance is
    61.4 studs. The first version of the overlap test compared
    consecutive head lines (always 0 for back-to-back rows), which was
    meaningless and hid this.
  * push_mapbuilder.py lost a byte whenever a chunk began with a
    newline -- Lua long-bracket literals swallow a newline directly
    after the opening bracket (assembled=65158 vs 65159). It now emits
    a deliberate leading newline; the push verifies match=true.
2026-08-01 19:26:48 -04:00

95 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Push MapBuilder.luau source into a LIVE (Play-mode) Studio via the MCP bridge.
Rojo cannot reach the running server datamodel in Play mode, so the source is
shipped as ~9000-char chunks into named StringValue children (p0, p1, ...) under
ReplicatedStorage, then concatenated and assigned to the ModuleScript's .Source.
Named children matter: a retried chunk overwrites its slot instead of appending
a duplicate. Bridge calls fail intermittently ("Unable to find an active Studio
instance") even while Studio is up, so every call is retried.
"""
import sys, time
sys.path.insert(0, "/tmp")
import rbx
SRC_PATH = "/home/jesse/roblox/tokyo-wangan-racing/src/server/MapBuilder.luau"
MODULE = 'game:GetService("ServerScriptService"):FindFirstChild("Server", true)'
CHUNK = 9000
def call(code, tries=4):
last = None
for a in range(tries):
try:
r = rbx.call(
"execute_luau",
{"code": code, "datamodel_type": "Server"},
warmup=6,
wait=45,
timeout=300,
)
if r and "Unable to find an active Studio" not in str(r):
return r
last = r
except Exception as e: # noqa: BLE001
last = e
time.sleep(5)
raise RuntimeError(f"bridge failed after {tries} tries: {last}")
src = open(SRC_PATH, encoding="utf-8").read()
nbytes = len(src.encode())
print(f"source: {len(src)} chars, {nbytes} utf-8 bytes")
# Pick a long-bracket level that does not occur in the source.
lvl = 0
while ("]" + "=" * lvl + "]") in src or ("[" + "=" * lvl + "[") in src:
lvl += 1
eq = "=" * lvl
print(f"long-bracket level: {lvl}")
chunks = [src[i : i + CHUNK] for i in range(0, len(src), CHUNK)]
print(f"chunks: {len(chunks)}")
print(call('local rs=game:GetService("ReplicatedStorage")\n'
'local b=rs:FindFirstChild("MBBuf") if b then b:Destroy() end\n'
'b=Instance.new("Folder") b.Name="MBBuf" b.Parent=rs\n'
'return "buffer reset"'))
for i, c in enumerate(chunks):
# Lua long-bracket literals SWALLOW a newline that immediately follows the
# opening bracket. When a chunk happened to start with "\n" that byte was
# silently lost, giving assembled=65158 vs expected=65159. Emit a deliberate
# leading newline for Lua to eat so the payload always survives intact.
code = (
'local rs=game:GetService("ReplicatedStorage")\n'
'local b=rs:WaitForChild("MBBuf")\n'
f'local n="p{i}"\n'
"local sv=b:FindFirstChild(n) or Instance.new(\"StringValue\")\n"
"sv.Name=n sv.Parent=b\n"
f"sv.Value=[{eq}[\n{c}]{eq}]\n"
f'return "p{i} "..#sv.Value'
)
print(f" chunk {i+1}/{len(chunks)}: {call(code)}")
assemble = f"""
local rs=game:GetService("ReplicatedStorage")
local b=rs:WaitForChild("MBBuf")
local parts={{}}
for i=0,{len(chunks)-1} do
local sv=b:FindFirstChild("p"..i)
if not sv then return "MISSING CHUNK p"..i end
parts[#parts+1]=sv.Value
end
local s=table.concat(parts)
local sss=game:GetService("ServerScriptService")
local m=sss:FindFirstChild("MapBuilder", true)
if not m then return "MapBuilder ModuleScript NOT FOUND" end
m.Source=s
return ("assembled=%d expected={nbytes} match=%s class=%s path=%s"):format(
#s, tostring(#s=={nbytes}), m.ClassName, m:GetFullName())
"""
print(call(assemble))