Render C1 structural variety: girder bridges, Ginza overpasses, JCT speeds, 3+3 widening
Four fidelity fixes, all verified in a live Studio datamodel (not just built): 1. Wire up the dead `bridge` flag. Circuit.luau marks 474 samples as bridge spans but nothing ever read the field, so every elevated metre rendered as the same slab on square columns. Flagged spans now get a steel box girder (鋼箱桁橋) with stiffener ribs under the deck, carried on a transverse pier cap and round columns at a wider pitch than the viaduct pillars. 2. Ginza 跨道橋. The Ginza section sits below street level in the filled-in 楓川・築地川 river beds and the surviving city bridges cross over it, their piers landing in the median. Adds the crossing decks, parapets and centre piers -- which is the physical reason lane changes are banned there, not just a paint choice. 3. Junction speed plates. 江戸橋JCT内 / 浜崎橋JCT内 / 京橋JCT-旧白魚橋乗継所 are posted 40 km/h, not the 50 km/h mainline limit; plates repeat more often through the short restricted stretches. 4. Shiodome JCT -> Hamazakibashi JCT widened to 3+3 lanes via widthAt(), with a taper (max ~1.7 studs/sample) instead of a step. Every fixed-width consumer -- deck, median, parapets, lane marks, signage offsets, skyline setback, finish line -- now reads widthAt() so nothing floats or clips. Verified headlessly (tools/verify_c1_fixes*.luau): girders 488 / 474 flagged samples, pier caps 85, columns 80 Ginza overpasses 7, piers 7, lateral offset from centreline 0.000 studs speed plates 40km/h x7, 50km/h x8 deck width Shiodome 81.5 vs Kasumigaseki 60.0 seam lip max 0.0006, road-surface raycast step max 0.260, 0 deck holes Two earlier "failures" were bad tests, not bad geometry, and the fixed harnesses document the traps: deck parts are pitched by CFrame.lookAt so Position.Y+Size.Y/2 is not the surface height (and LookVector is -Z, so the end faces are easy to swap), and a seam raycast must filter to the Expressway folder or it hits TunnelCeiling/MedianFence and reports a phantom 19-stud step.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
#!/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):
|
||||
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}[{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))
|
||||
Reference in New Issue
Block a user