From 20f58fa0a43da9725e5455501d8e452e3c7dd970 Mon Sep 17 00:00:00 2001 From: scamiv <6170744+scamiv@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:12:33 +0100 Subject: [PATCH] perf(core): avoid double allocation in packMotionPlans Rewrite MotionPlans.packMotionPlans to precompute total word count and fill a single pre-sized Uint32Array with an index pointer (no intermediate number[]). --- src/core/game/MotionPlans.ts | 97 +++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 41 deletions(-) diff --git a/src/core/game/MotionPlans.ts b/src/core/game/MotionPlans.ts index ae0e3c292..fbcf0858f 100644 --- a/src/core/game/MotionPlans.ts +++ b/src/core/game/MotionPlans.ts @@ -41,68 +41,83 @@ export type MotionPlanRecord = GridPathPlan | TrainRailPathPlan; export function packMotionPlans( records: readonly MotionPlanRecord[], ): Uint32Array { - const out: number[] = [MOTION_PLANS_SCHEMA_VERSION, records.length]; - + let totalWords = 2; for (const record of records) { switch (record.kind) { case "grid": { - const path = - record.path instanceof Uint32Array - ? record.path - : Uint32Array.from(record.path); + const pathLen = (record.path.length >>> 0) as number; + totalWords += 2 + 5 + pathLen; + break; + } + case "train": { + const carCount = (record.carUnitIds.length >>> 0) as number; + const pathLen = (record.path.length >>> 0) as number; + totalWords += 2 + 7 + carCount + pathLen; + break; + } + } + } + + const out = new Uint32Array(totalWords); + out[0] = MOTION_PLANS_SCHEMA_VERSION >>> 0; + out[1] = records.length >>> 0; + + let offset = 2; + for (const record of records) { + switch (record.kind) { + case "grid": { + const path = record.path as ArrayLike; const pathLen = path.length >>> 0; const wordCount = 2 + 5 + pathLen; - out.push( - PackedMotionPlanKind.GridPathSet, - wordCount, - record.unitId >>> 0, - record.planId >>> 0, - record.startTick >>> 0, - record.ticksPerStep >>> 0, - pathLen, - ); - for (let i = 0; i < path.length; i++) { - out.push(path[i] >>> 0); + + out[offset++] = PackedMotionPlanKind.GridPathSet; + out[offset++] = wordCount >>> 0; + out[offset++] = record.unitId >>> 0; + out[offset++] = record.planId >>> 0; + out[offset++] = record.startTick >>> 0; + out[offset++] = record.ticksPerStep >>> 0; + out[offset++] = pathLen >>> 0; + + for (let i = 0; i < pathLen; i++) { + out[offset++] = path[i] >>> 0; } break; } case "train": { - const carUnitIds = - record.carUnitIds instanceof Uint32Array - ? record.carUnitIds - : Uint32Array.from(record.carUnitIds); + const carUnitIds = record.carUnitIds as ArrayLike; const carCount = carUnitIds.length >>> 0; - const path = - record.path instanceof Uint32Array - ? record.path - : Uint32Array.from(record.path); + const path = record.path as ArrayLike; const pathLen = path.length >>> 0; const wordCount = 2 + 7 + carCount + pathLen; - out.push( - PackedMotionPlanKind.TrainRailPathSet, - wordCount, - record.engineUnitId >>> 0, - record.planId >>> 0, - record.startTick >>> 0, - record.speed >>> 0, - record.spacing >>> 0, - carCount, - pathLen, - ); - for (let i = 0; i < carUnitIds.length; i++) { - out.push(carUnitIds[i] >>> 0); + out[offset++] = PackedMotionPlanKind.TrainRailPathSet; + out[offset++] = wordCount >>> 0; + out[offset++] = record.engineUnitId >>> 0; + out[offset++] = record.planId >>> 0; + out[offset++] = record.startTick >>> 0; + out[offset++] = record.speed >>> 0; + out[offset++] = record.spacing >>> 0; + out[offset++] = carCount >>> 0; + out[offset++] = pathLen >>> 0; + + for (let i = 0; i < carCount; i++) { + out[offset++] = carUnitIds[i] >>> 0; } - for (let i = 0; i < path.length; i++) { - out.push(path[i] >>> 0); + for (let i = 0; i < pathLen; i++) { + out[offset++] = path[i] >>> 0; } break; } } } - return new Uint32Array(out); + if (offset !== out.length) { + throw new Error( + `packMotionPlans size mismatch: wrote ${offset}, expected ${out.length}`, + ); + } + return out; } export function unpackMotionPlans(packed: Uint32Array): {