Daikoku PA: lift mainline over the apron, rebuild both ramps
The PA ring road and the expressway mainline were both at ground level, so the ring parapet stood INSIDE the carriageway (measured -10.0 studs of vertical overlap: a wall in the middle of the road). Reference photos of the real Daikoku Futo PA show the expressway flying OVER the parking apron on pillars, with the PA at ground level and ramps climbing up to meet it. Rebuilt to match: - mainline lifted 38 studs through the junction, eased in/out (max 5.26% grade, within real expressway limits) - PA ring + apron stay at ground level; legacy PARingPillar props removed - both on/off ramps regraded over the full rise with smoothstep easing, segments overlapped 3 studs so wheels can't catch a seam, plus support pillars and edge kerbs where the deck is high Verified in-engine against built parts (tools/diag_daikoku.luau), not just source: clearance now +28.0 studs, both ramps continuous (max step 5.62, no gaps). tools/plot_daikoku.py renders the plan/section for comparison.
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
#!/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 = sys.argv[1]
|
||||
MODULE_NAME = sys.argv[2]
|
||||
# Which datamodel to write into. "Server" is the PLAY-mode sandbox: Studio
|
||||
# DISCARDS it when Play stops, so pushes there are silently lost on the next
|
||||
# stop/start (this burned an entire debugging session -- pushes verified at the
|
||||
# correct byte length, then vanished). "Edit" is the persistent datamodel and
|
||||
# is what you almost always want; it survives into the next Play session.
|
||||
# Edit is only reachable while Studio is actually in Edit mode.
|
||||
DATAMODEL = sys.argv[3] if len(sys.argv) > 3 else "Edit"
|
||||
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": DATAMODEL},
|
||||
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)
|
||||
-- Search the WHOLE game, not just ServerScriptService: shared modules live in
|
||||
-- ReplicatedStorage and client ones in StarterPlayerScripts. Searching only
|
||||
-- SSS silently reported "NOT FOUND" for Chassis while the wrapper still
|
||||
-- exited 0, so a half-applied push looked like a success.
|
||||
local roots={{
|
||||
game:GetService("ServerScriptService"),
|
||||
game:GetService("ReplicatedStorage"),
|
||||
game:GetService("StarterPlayer"),
|
||||
game:GetService("Workspace"),
|
||||
}}
|
||||
local m=nil
|
||||
for _,r in ipairs(roots) do
|
||||
local hit=r:FindFirstChild("{MODULE_NAME}", true)
|
||||
if hit and hit:IsA("ModuleScript") then m=hit break end
|
||||
end
|
||||
if not m then return "{MODULE_NAME} 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())
|
||||
"""
|
||||
result = call(assemble)
|
||||
print(result)
|
||||
# Exit non-zero on any failure. Previously this always exited 0, so a
|
||||
# "NOT FOUND" or byte-count mismatch was reported as success by the caller.
|
||||
if "match=true" not in str(result):
|
||||
print("PUSH FAILED -- module not updated", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user