diff --git a/src/core/execution/TradeShipExecution.ts b/src/core/execution/TradeShipExecution.ts index 9b6d932b9..0538d42e5 100644 --- a/src/core/execution/TradeShipExecution.ts +++ b/src/core/execution/TradeShipExecution.ts @@ -48,9 +48,6 @@ export class TradeShipExecution implements Execution { targetUnit: this._dstPort, lastSetSafeFromPirates: ticks, }); - // This unit can move immediately, but plan-driven units don't emit per-step Unit updates. - // Mark it plan-driven up-front so its first move doesn't generate redundant traffic. - this.mg.markUnitPlanDriven(this.tradeShip.id()); this.mg.stats().boatSendTrade(this.origOwner, this._dstPort.owner()); } @@ -109,12 +106,49 @@ export class TradeShipExecution implements Execution { return; } - const result = this.pathFinder.next(curTile, this._dstPort.tile()); + const dst = this._dstPort.tile(); + const result = this.pathFinder.next(curTile, dst); switch (result.status) { case PathStatus.PENDING: + if (dst !== this.motionPlanDst) { + this.motionPlanId++; + const from = curTile; + const path = this.pathFinder.findPath(from, dst) ?? [from]; + if (path.length === 0 || path[0] !== from) { + path.unshift(from); + } + + this.mg.recordMotionPlan({ + kind: "grid", + unitId: this.tradeShip.id(), + planId: this.motionPlanId, + startTick: ticks + 1, + ticksPerStep: 1, + path, + }); + this.motionPlanDst = dst; + } break; case PathStatus.NEXT: + if (dst !== this.motionPlanDst) { + this.motionPlanId++; + const from = result.node; + const path = this.pathFinder.findPath(from, dst) ?? [from]; + if (path.length === 0 || path[0] !== from) { + path.unshift(from); + } + + this.mg.recordMotionPlan({ + kind: "grid", + unitId: this.tradeShip.id(), + planId: this.motionPlanId, + startTick: ticks + 1, + ticksPerStep: 1, + path, + }); + this.motionPlanDst = dst; + } // Update safeFromPirates status if (this.mg.isWater(result.node) && this.mg.isShoreline(result.node)) { this.tradeShip.setSafeFromPirates(); @@ -133,26 +167,6 @@ export class TradeShipExecution implements Execution { this.active = false; return; } - - const dst = this._dstPort.tile(); - if (dst !== this.motionPlanDst) { - this.motionPlanId++; - const from = this.tradeShip.tile(); - const path = this.pathFinder.findPath(from, dst) ?? [from]; - if (path.length === 0 || path[0] !== from) { - path.unshift(from); - } - - this.mg.recordMotionPlan({ - kind: "grid", - unitId: this.tradeShip.id(), - planId: this.motionPlanId, - startTick: ticks + 1, - ticksPerStep: 1, - path, - }); - this.motionPlanDst = dst; - } } private complete() { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 206a761be..c44705d37 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -778,11 +778,6 @@ export interface Game extends GameMap { inSpawnPhase(): boolean; executeNextTick(): GameUpdates; drainPackedTileUpdates(): Uint32Array; - /** - * Marks a unit as "plan-driven" so its per-tile `Unit` updates can be suppressed. - * The client is expected to advance the unit position via motion plans. - */ - markUnitPlanDriven(unitId: number): void; recordMotionPlan(record: MotionPlanRecord): void; drainPackedMotionPlans(): Uint32Array | null; setWinner(winner: Player | Team, allPlayersStats: AllPlayersStats): void; diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 4b0506ee9..ab2a179f5 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -447,10 +447,6 @@ export class GameImpl implements Game { return packed; } - markUnitPlanDriven(unitId: number): void { - this.planDrivenUnitIds.add(unitId); - } - recordMotionPlan(record: MotionPlanRecord): void { switch (record.kind) { case "grid": @@ -466,10 +462,21 @@ export class GameImpl implements Game { this.motionPlanRecords.push(record); } - isUnitPlanDriven(unitId: number): boolean { + private isUnitPlanDriven(unitId: number): boolean { return this.planDrivenUnitIds.has(unitId); } + maybeAddUnitUpdate(unit: Unit): void { + if (!this.isUnitPlanDriven(unit.id())) { + this.addUpdate(unit.toUpdate()); + } + } + + onUnitMoved(unit: Unit): void { + this.updateUnitTile(unit); + this.maybeAddUnitUpdate(unit); + } + drainPackedMotionPlans(): Uint32Array | null { const records = this.motionPlanRecords; if (records.length === 0) { diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 930cc6142..161e4aa7f 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -159,10 +159,7 @@ export class UnitImpl implements Unit { } this._lastTile = this._tile; this._tile = tile; - this.mg.updateUnitTile(this); - if (!this.mg.isUnitPlanDriven(this._id)) { - this.mg.addUpdate(this.toUpdate()); - } + this.mg.onUnitMoved(this); } setTroops(troops: number): void {