Skip to content

Commit 60dedc0

Browse files
committed
Introduce playerBuildables to only get buildable units, because StructureIconsLayer and NukeTrajectoryPreviewLayer don't need all the player actions data. This speeds them up even more.
playerActions now has 3 modes: get all actions and all buildings (units undefined), get all actions and no buildings (units null), or get all actions and specific building (units contains Unit Types). playerBuildables has 2 modes: get all buildings (units undefined) or get specific buildings (units contains Unit Types).
1 parent cbb3586 commit 60dedc0

File tree

7 files changed

+111
-9
lines changed

7 files changed

+111
-9
lines changed

src/client/graphics/layers/NukeTrajectoryPreviewLayer.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class NukeTrajectoryPreviewLayer implements Layer {
2626
private lastTrajectoryUpdate: number = 0;
2727
private lastTargetTile: TileRef | null = null;
2828
private currentGhostStructure: UnitType | null = null;
29-
// Cache spawn tile to avoid expensive player.actions() calls
29+
// Cache spawn tile to avoid expensive player.buildables() calls
3030
private cachedSpawnTile: TileRef | null = null;
3131

3232
constructor(
@@ -75,7 +75,7 @@ export class NukeTrajectoryPreviewLayer implements Layer {
7575
}
7676

7777
/**
78-
* Update trajectory preview - called from tick() to cache spawn tile via expensive player.actions() call
78+
* Update trajectory preview - called from tick() to cache spawn tile via expensive player.buildables() call
7979
* This only runs when target tile changes, minimizing worker thread communication
8080
*/
8181
private updateTrajectoryPreview() {
@@ -138,14 +138,14 @@ export class NukeTrajectoryPreviewLayer implements Layer {
138138

139139
// Get buildable units to find spawn tile (expensive call - only on tick when tile changes)
140140
player
141-
.actions(targetTile, [ghostStructure])
142-
.then((actions) => {
141+
.buildables(targetTile, [ghostStructure])
142+
.then((buildables) => {
143143
// Ignore stale results if target changed
144144
if (this.lastTargetTile !== targetTile) {
145145
return;
146146
}
147147

148-
const buildableUnit = actions.buildableUnits.find(
148+
const buildableUnit = buildables.find(
149149
(bu) => bu.type === ghostStructure,
150150
);
151151

@@ -171,7 +171,7 @@ export class NukeTrajectoryPreviewLayer implements Layer {
171171

172172
/**
173173
* Update trajectory path - called from renderLayer() each frame for smooth visual feedback
174-
* Uses cached spawn tile to avoid expensive player.actions() calls
174+
* Uses cached spawn tile to avoid expensive player.buildables() calls
175175
*/
176176
private updateTrajectoryPath() {
177177
const ghostStructure = this.currentGhostStructure;

src/client/graphics/layers/StructureIconsLayer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ export class StructureIconsLayer implements Layer {
286286

287287
this.game
288288
?.myPlayer()
289-
?.actions(tileRef, [this.ghostUnit?.buildableUnit.type])
290-
.then((actions) => {
289+
?.buildables(tileRef, [this.ghostUnit?.buildableUnit.type])
290+
.then((buildables) => {
291291
if (this.potentialUpgrade) {
292292
this.potentialUpgrade.iconContainer.filters = [];
293293
this.potentialUpgrade.dotContainer.filters = [];
@@ -298,7 +298,7 @@ export class StructureIconsLayer implements Layer {
298298

299299
if (!this.ghostUnit) return;
300300

301-
const unit = actions.buildableUnits.find(
301+
const unit = buildables.find(
302302
(u) => u.type === this.ghostUnit!.buildableUnit.type,
303303
);
304304
const showPrice = this.game.config().userSettings().cursorCostLabel();

src/core/GameRunner.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { WinCheckExecution } from "./execution/WinCheckExecution";
66
import {
77
AllPlayers,
88
Attack,
9+
BuildableUnit,
910
Cell,
1011
Game,
1112
GameUpdates,
@@ -192,6 +193,18 @@ export class GameRunner {
192193
return Math.max(0, this.turns.length - this.currTurn);
193194
}
194195

196+
public playerBuildables(
197+
playerID: PlayerID,
198+
x?: number,
199+
y?: number,
200+
units?: readonly PlayerBuildableUnitType[],
201+
): BuildableUnit[] {
202+
const player = this.game.player(playerID);
203+
const tile =
204+
x !== undefined && y !== undefined ? this.game.ref(x, y) : null;
205+
return player.buildableUnits(tile, units);
206+
}
207+
195208
public playerActions(
196209
playerID: PlayerID,
197210
x?: number,

src/core/game/GameView.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ClientID, GameID, Player, PlayerCosmetics } from "../Schemas";
77
import { createRandomName } from "../Util";
88
import { WorkerClient } from "../worker/WorkerClient";
99
import {
10+
BuildableUnit,
1011
Cell,
1112
EmojiMessage,
1213
GameUpdates,
@@ -416,6 +417,18 @@ export class PlayerView {
416417
);
417418
}
418419

420+
async buildables(
421+
tile?: TileRef,
422+
units?: readonly PlayerBuildableUnitType[],
423+
): Promise<BuildableUnit[]> {
424+
return this.game.worker.playerBuildables(
425+
this.id(),
426+
tile && this.game.x(tile),
427+
tile && this.game.y(tile),
428+
units,
429+
);
430+
}
431+
419432
async borderTiles(): Promise<PlayerBorderTiles> {
420433
return this.game.worker.playerBorderTiles(this.id());
421434
}

src/core/worker/Worker.worker.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MainThreadMessage,
99
PlayerActionsResultMessage,
1010
PlayerBorderTilesResultMessage,
11+
PlayerBuildablesResultMessage,
1112
PlayerProfileResultMessage,
1213
TransportShipSpawnResultMessage,
1314
WorkerMessage,
@@ -106,6 +107,28 @@ ctx.addEventListener("message", async (e: MessageEvent<MainThreadMessage>) => {
106107
throw error;
107108
}
108109
break;
110+
case "player_buildables":
111+
if (!gameRunner) {
112+
throw new Error("Game runner not initialized");
113+
}
114+
115+
try {
116+
const actions = (await gameRunner).playerBuildables(
117+
message.playerID,
118+
message.x,
119+
message.y,
120+
message.units,
121+
);
122+
sendMessage({
123+
type: "player_buildables_result",
124+
id: message.id,
125+
result: actions,
126+
} as PlayerBuildablesResultMessage);
127+
} catch (error) {
128+
console.error("Failed to check borders:", error);
129+
throw error;
130+
}
131+
break;
109132
case "player_profile":
110133
if (!gameRunner) {
111134
throw new Error("Game runner not initialized");

src/core/worker/WorkerClient.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
BuildableUnit,
23
Cell,
34
PlayerActions,
45
PlayerBorderTiles,
@@ -195,6 +196,40 @@ export class WorkerClient {
195196
});
196197
}
197198

199+
playerBuildables(
200+
playerID: PlayerID,
201+
x?: number,
202+
y?: number,
203+
units?: readonly PlayerBuildableUnitType[] | null,
204+
): Promise<BuildableUnit[]> {
205+
return new Promise((resolve, reject) => {
206+
if (!this.isInitialized) {
207+
reject(new Error("Worker not initialized"));
208+
return;
209+
}
210+
211+
const messageId = generateID();
212+
213+
this.messageHandlers.set(messageId, (message) => {
214+
if (
215+
message.type === "player_buildables_result" &&
216+
message.result !== undefined
217+
) {
218+
resolve(message.result);
219+
}
220+
});
221+
222+
this.worker.postMessage({
223+
type: "player_buildables",
224+
id: messageId,
225+
playerID,
226+
x,
227+
y,
228+
units,
229+
});
230+
});
231+
}
232+
198233
attackAveragePosition(
199234
playerID: number,
200235
attackID: string,

src/core/worker/WorkerMessages.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
BuildableUnit,
23
PlayerActions,
34
PlayerBorderTiles,
45
PlayerBuildableUnitType,
@@ -17,6 +18,8 @@ export type WorkerMessageType =
1718
| "game_update"
1819
| "player_actions"
1920
| "player_actions_result"
21+
| "player_buildables"
22+
| "player_buildables_result"
2023
| "player_profile"
2124
| "player_profile_result"
2225
| "player_border_tiles"
@@ -71,6 +74,19 @@ export interface PlayerActionsResultMessage extends BaseWorkerMessage {
7174
result: PlayerActions;
7275
}
7376

77+
export interface PlayerBuildablesMessage extends BaseWorkerMessage {
78+
type: "player_buildables";
79+
playerID: PlayerID;
80+
x?: number;
81+
y?: number;
82+
units?: readonly PlayerBuildableUnitType[];
83+
}
84+
85+
export interface PlayerBuildablesResultMessage extends BaseWorkerMessage {
86+
type: "player_buildables_result";
87+
result: BuildableUnit[];
88+
}
89+
7490
export interface PlayerProfileMessage extends BaseWorkerMessage {
7591
type: "player_profile";
7692
playerID: number;
@@ -120,6 +136,7 @@ export type MainThreadMessage =
120136
| InitMessage
121137
| TurnMessage
122138
| PlayerActionsMessage
139+
| PlayerBuildablesMessage
123140
| PlayerProfileMessage
124141
| PlayerBorderTilesMessage
125142
| AttackAveragePositionMessage
@@ -130,6 +147,7 @@ export type WorkerMessage =
130147
| InitializedMessage
131148
| GameUpdateMessage
132149
| PlayerActionsResultMessage
150+
| PlayerBuildablesResultMessage
133151
| PlayerProfileResultMessage
134152
| PlayerBorderTilesResultMessage
135153
| AttackAveragePositionResultMessage

0 commit comments

Comments
 (0)