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.
95 lines
3.3 KiB
Python
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))
|