chore(prototype): per-tick trace after rebuild, multilane only; record two-way result

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
haylanandClaude Bot committed 2026-10-05 23:14:52 +02:00
1 parent d4fab92fd4
commit 90829ee9b9
2 files changed
+28 -1

No files matched your search

+5
View File
@@ -75,3 +75,8 @@ Scanned: the 9 mods in `C:\Users\Public\mod.io\10640\mods\` (e.g. `devers_1`, `m
- Likely cause (human's hypothesis): the vehicles' `MovePath` refers to lanes of the removed segment, so the engine must re-attach them to the new segment and re-plan, and a vehicle can land in a different lane.
- **Debug `MovePath` exchange is unusable from engine state**: `api.cmd.sendCommand(api.cmd.debug.makeComponentExchangeCmd(v, movePath))` fails with `Error decoding argument #3` even **without** a callback. So the earlier error wasn't caused by the callback. The only untried route is the GUI thread.
- Not yet tested: a rebuild on a two-way (one lane per direction) road.
## Rebuild on a two-way road (run 2026-10-05 ~21:12 UTC)
- 3 `replaceSegment` rebuilds on `country_new_small` (one lane per direction), segments 30213, 29776 and 30215, follower 25.8 m behind leader. **No effect**: the next snapshot had the same gap (22.6/48.4 → 49.3/75.1), both vehicles stayed in lane 2, same speeds, same order for 10+ snapshots. Rebuilding does not move vehicles past each other on single-lane roads.
- How the multilane lane change happens is still uncaught: snapshots every 2 s only show before (both lane 4) and after (the leader in lane 3 two segments later). The probe now traces the two vehicles on every update for ~30 s after a rebuild (`TRACE` lines), and rebuilds only multilane segments.
+23 -1
View File
@@ -12,6 +12,7 @@ local MAX_REBUILDS = 3 -- hand-built proposals were silently rejected; now via a
local MAX_SETLINES = 0 -- set-line disabled: lanes ahead get re-planned by the engine anyway, no forced lane change
local MAX_JUMPS = 0 -- dead: sendCommand(makeComponentExchangeCmd(...)) fails in engine state even without a callback
local JUMP_AHEAD = 15.0
local WATCH_TICKS = 150 -- ~30 s of game time at dt=0.2: log watched vehicles every update after a rebuild
local elapsed = 0.0
local ticks = 0
@@ -45,6 +46,7 @@ local record ProbeState
setlines : integer
jumps : integer
watch : {integer} -- vehicles involved in the last rebuild, flagged in later dumps
watchLeft : integer -- updates left for per-tick logging of the watched vehicles
end
-- No edge objects (stops, signals),
@@ -61,7 +63,8 @@ local function rebuildable(seg : integer) : boolean, string
if on and (tonumber(tostring(k)) or 0) >= 2 then driving = driving + 1 break end
end
end
-- any lane count: what a rebuild does to vehicles on the segment doesn't depend on it
-- one lane per direction: 3 rebuilds there changed nothing (same gap, same lane), so trace multilane only
if driving < 4 then return false, "driving lanes=" .. driving .. " (single-lane rebuilds showed no effect)" end
return true, string.format("drivingLanes=%d template=%s length=%.1f", driving, tostring(be.roadTemplate), be.distance)
end
@@ -176,6 +179,7 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
rebuild(seg)
st.rebuilds = st.rebuilds + 1
st.watch = { a.v, b.v }
st.watchLeft = WATCH_TICKS
state:set(st)
p(string.format("REBUILD_TEST sent #%d seg=%d", st.rebuilds, seg))
return -- one rebuild per dump
@@ -186,10 +190,28 @@ local function dump(dt : number, state : GameScriptState<ProbeState>)
end
end
-- per-update trace of the watched vehicles, to catch the moment a lane changes
local function trace(state : GameScriptState<ProbeState>)
local st = state:get()
if (st.watchLeft or 0) <= 0 or st.watch == nil then return end
for _, v in ipairs(st.watch) do
local mp = api.engine.getComponent(v, api.type.ComponentType.MOVE_PATH) as Engine.Component.MovePath
if mp ~= nil then
local edge, _dir = edgeAt(mp)
p(string.format("TRACE t=%d veh=%d seg=%s lane=%s pos=%.1f v=%.2f ahead=%s",
WATCH_TICKS - st.watchLeft, v, tostring(edge and edge.entity), tostring(edge and edge.index),
mp.dyn.pathPos.pos, mp.dyn.speed, lanesAhead(mp)))
end
end
st.watchLeft = st.watchLeft - 1
state:set(st)
end
local ret : GameScript<ProbeState> = {
update = function(_userParams : table, state : GameScriptState<ProbeState>, dt : number) : nil
ticks = ticks + 1
elapsed = elapsed + dt
if dt > 0 then xpcall(trace, function(err : any) p("ERROR trace " .. tostring(err)) end, state) end
if ticks == 1 then p("update running, first dt=" .. tostring(dt)) end
if elapsed < INTERVAL then return end
elapsed = 0.0