mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-29 20:02:12 +00:00
thread split: get units working
This commit is contained in:
@@ -128,6 +128,12 @@ export class ClientGameRunner {
|
||||
this.gameView.update(gu)
|
||||
this.renderer.tick()
|
||||
})
|
||||
const worker = this.worker
|
||||
const keepWorkerAlive = () => {
|
||||
worker.sendHeartbeat
|
||||
requestAnimationFrame(keepWorkerAlive)
|
||||
}
|
||||
requestAnimationFrame(keepWorkerAlive)
|
||||
|
||||
const onconnect = () => {
|
||||
consolex.log('Connected to game server!');
|
||||
|
||||
@@ -28,12 +28,10 @@ export class UnitLayer implements Layer {
|
||||
|
||||
private oldShellTile = new Map<Unit, Tile>()
|
||||
|
||||
|
||||
constructor(private game: GameView, private eventBus: EventBus, private clientID: ClientID) {
|
||||
this.theme = game.config().theme();
|
||||
}
|
||||
|
||||
|
||||
shouldTransform(): boolean {
|
||||
return true;
|
||||
}
|
||||
@@ -42,8 +40,9 @@ export class UnitLayer implements Layer {
|
||||
if (this.myPlayer == null) {
|
||||
this.myPlayer = this.game.playerByClientID(this.clientID)
|
||||
}
|
||||
for (const unit of this.game.recentlyUpdatedUnits()) {
|
||||
this.onUnitEvent(unit)
|
||||
for (const unit of this.game.units()) {
|
||||
if (unit.wasUpdated())
|
||||
this.onUnitEvent(unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-13
@@ -4,7 +4,7 @@ import { getConfig } from "./configuration/Config";
|
||||
import { EventBus } from "./EventBus";
|
||||
import { Executor } from "./execution/ExecutionManager";
|
||||
import { WinCheckExecution } from "./execution/WinCheckExecution";
|
||||
import { Cell, DisplayMessageUpdate, Game, GameUpdateType, MessageType, MutableGame, MutableTile, NameViewData, Player, PlayerActions, PlayerID, Tile, UnitType } from "./game/Game";
|
||||
import { Cell, DisplayMessageUpdate, Game, GameUpdateType, MessageType, MutableGame, MutableTile, NameViewData, Player, PlayerActions, PlayerID, Tile, TileUpdate, UnitType, UnitUpdate } from "./game/Game";
|
||||
import { createGame } from "./game/GameImpl";
|
||||
import { loadTerrainMap } from "./game/TerrainMapLoader";
|
||||
import { GameConfig, Turn } from "./Schemas";
|
||||
@@ -26,7 +26,7 @@ export class GameRunner {
|
||||
private currTurn = 0
|
||||
private isExecuting = false
|
||||
|
||||
private playerToName = new Map<PlayerID, NameViewData>()
|
||||
private playerViewData: Record<PlayerID, NameViewData> = {}
|
||||
|
||||
constructor(
|
||||
public game: MutableGame,
|
||||
@@ -63,22 +63,20 @@ export class GameRunner {
|
||||
const updates = this.game.executeNextTick()
|
||||
|
||||
if (this.game.inSpawnPhase() || this.game.ticks() % 20 == 0) {
|
||||
this.game.players()
|
||||
.forEach(p => this.playerToName.set(p.id(), placeName(this.game, p)))
|
||||
this.game.players().forEach(p => {
|
||||
this.playerViewData[p.id()] = placeName(this.game, p)
|
||||
})
|
||||
}
|
||||
|
||||
const playerViewData = {}
|
||||
for (const player of this.game.allPlayers()) {
|
||||
const viewData = player.toUpdate()
|
||||
viewData.nameViewData = this.playerToName.get(player.id())
|
||||
playerViewData[player.id()] = viewData
|
||||
}
|
||||
// Many tiles are updated to pack it into an array
|
||||
const packedTileUpdates = updates[GameUpdateType.Tile].map(u => packTileData(u as TileUpdate))
|
||||
updates[GameUpdateType.Tile] = []
|
||||
|
||||
this.callBack({
|
||||
tick: this.game.ticks(),
|
||||
units: updates.filter(u => u.type == GameUpdateType.Unit),
|
||||
packedTileUpdates: updates.filter(u => u.type == GameUpdateType.Tile).map(u => packTileData(u)),
|
||||
players: playerViewData
|
||||
packedTileUpdates: packedTileUpdates,
|
||||
updates: updates,
|
||||
playerNameViewData: this.playerViewData
|
||||
})
|
||||
this.isExecuting = false
|
||||
}
|
||||
|
||||
+42
-22
@@ -1,4 +1,4 @@
|
||||
import { GameUpdateType, MapPos, MessageType, NameViewData, Player, PlayerActions, PlayerUpdate, Tile, TileUpdate, Unit, UnitUpdate } from './game/Game';
|
||||
import { GameUpdates, GameUpdateType, MapPos, MessageType, NameViewData, Player, PlayerActions, PlayerUpdate, Tile, TileUpdate, Unit, UnitUpdate } from './game/Game';
|
||||
import { Config } from "./configuration/Config";
|
||||
import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage, Execution, ExecutionView, Game, Gold, MutableTile, Nation, PlayerID, PlayerInfo, PlayerType, Relation, TerrainMap, TerrainTile, TerrainType, TerraNullius, Tick, UnitInfo, UnitType } from "./game/Game";
|
||||
import { ClientID } from "./Schemas";
|
||||
@@ -58,13 +58,31 @@ export class TileView {
|
||||
}
|
||||
|
||||
export class UnitView implements Unit {
|
||||
constructor(private gameView: GameView, private data: UnitUpdate) { }
|
||||
public _wasUpdated = true
|
||||
public lastPos: MapPos[] = []
|
||||
|
||||
constructor(private gameView: GameView, private data: UnitUpdate) {
|
||||
this.lastPos.push(data.pos)
|
||||
}
|
||||
|
||||
wasUpdated(): boolean {
|
||||
return this._wasUpdated
|
||||
}
|
||||
|
||||
lastTiles(): Tile[] {
|
||||
return this.lastPos.map(pos => this.gameView.tile(new Cell(pos.x, pos.y)))
|
||||
}
|
||||
|
||||
lastTile(): Tile {
|
||||
return this.gameView.tile(new Cell(this.data.lastPos.x, this.data.lastPos.y))
|
||||
if (this.lastPos.length == 0) {
|
||||
return this.gameView.tile(new Cell(this.data.pos.x, this.data.pos.y))
|
||||
}
|
||||
return this.gameView.tile(new Cell(this.lastPos[0].x, this.lastPos[0].y))
|
||||
}
|
||||
|
||||
update(data: UnitUpdate) {
|
||||
this.lastPos.push(data.pos)
|
||||
this._wasUpdated = true
|
||||
this.data = data
|
||||
}
|
||||
|
||||
@@ -96,14 +114,15 @@ export class UnitView implements Unit {
|
||||
}
|
||||
|
||||
export class PlayerView implements Player {
|
||||
constructor(private game: GameView, public data: PlayerUpdate) { }
|
||||
|
||||
constructor(private game: GameView, public data: PlayerUpdate, public nameData: NameViewData) { }
|
||||
|
||||
async actions(tile: Tile): Promise<PlayerActions> {
|
||||
return this.game.worker.playerInteraction(this.id(), tile)
|
||||
}
|
||||
|
||||
nameLocation(): NameViewData {
|
||||
return this.data.nameViewData
|
||||
return this.nameData
|
||||
}
|
||||
|
||||
smallID(): number {
|
||||
@@ -220,9 +239,9 @@ export class PlayerView implements Player {
|
||||
|
||||
export interface GameUpdateViewData {
|
||||
tick: number
|
||||
units: UnitUpdate[]
|
||||
players: Record<PlayerID, PlayerUpdate>
|
||||
updates: GameUpdates
|
||||
packedTileUpdates: Uint16Array[]
|
||||
playerNameViewData: Record<number, NameViewData>
|
||||
}
|
||||
|
||||
export class GameView {
|
||||
@@ -232,7 +251,6 @@ export class GameView {
|
||||
private _players = new Map<PlayerID, PlayerView>()
|
||||
private _units = new Map<number, UnitView>()
|
||||
private updatedTiles: TileView[] = []
|
||||
private updatedUnits: UnitView[] = []
|
||||
|
||||
constructor(public worker: WorkerClient, private _config: Config, private _terrainMap: TerrainMap) {
|
||||
// Initialize the 2D array
|
||||
@@ -246,9 +264,10 @@ export class GameView {
|
||||
}
|
||||
this.lastUpdate = {
|
||||
tick: 0,
|
||||
units: [],
|
||||
packedTileUpdates: [],
|
||||
players: {}
|
||||
// TODO: make this empty map instead of null?
|
||||
updates: null,
|
||||
playerNameViewData: {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,30 +281,31 @@ export class GameView {
|
||||
})
|
||||
this.updatedTiles = Array.from(updated).map(pos => this.tiles[pos.x][pos.y])
|
||||
|
||||
Object.entries(gu.players).forEach(([key, value]) => {
|
||||
this.smallIDToID.set(value.smallID, key);
|
||||
if (this._players.has(key)) {
|
||||
this._players.get(key).data = value
|
||||
gu.updates[GameUpdateType.Player].forEach((pu) => {
|
||||
this.smallIDToID.set(pu.smallID, pu.id);
|
||||
if (this._players.has(pu.id)) {
|
||||
this._players.get(pu.id).data = pu
|
||||
this._players.get(pu.id).nameData = gu.playerNameViewData[pu.id]
|
||||
} else {
|
||||
this._players.set(key, new PlayerView(this, value))
|
||||
this._players.set(pu.id, new PlayerView(this, pu, gu.playerNameViewData[pu.id]))
|
||||
}
|
||||
});
|
||||
gu.units.forEach(unit => {
|
||||
for (const unit of this._units.values()) {
|
||||
unit._wasUpdated = false
|
||||
unit.lastPos = unit.lastPos.slice(-1)
|
||||
}
|
||||
gu.updates[GameUpdateType.Unit].forEach(unit => {
|
||||
if (this._units.has(unit.id)) {
|
||||
this._units.get(unit.id).update(unit)
|
||||
} else {
|
||||
this._units.set(unit.id, new UnitView(this, unit))
|
||||
}
|
||||
})
|
||||
this.updatedUnits = gu.units.map(u => this._units.get(u.id))
|
||||
}
|
||||
|
||||
recentlyUpdatedTiles(): TileView[] {
|
||||
return this.updatedTiles
|
||||
}
|
||||
recentlyUpdatedUnits(): UnitView[] {
|
||||
return this.updatedUnits
|
||||
}
|
||||
|
||||
player(id: PlayerID): PlayerView {
|
||||
if (this._players.has(id)) {
|
||||
@@ -347,7 +367,7 @@ export class GameView {
|
||||
config(): Config {
|
||||
return this._config
|
||||
}
|
||||
units(...types: UnitType[]): Unit[] {
|
||||
units(...types: UnitType[]): UnitView[] {
|
||||
return Array.from(this._units.values())
|
||||
}
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
@@ -384,4 +404,4 @@ export function unpackTileData(packed: Uint16Array): TileUpdate {
|
||||
hasDefenseBonus: !!(packed[3] & 2),
|
||||
isBorder: !!(packed[3] & 4),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -9,6 +9,15 @@ export type Gold = number
|
||||
|
||||
export const AllPlayers = "AllPlayers" as const;
|
||||
|
||||
// export type GameUpdates = Record<GameUpdateType, GameUpdate[]>;
|
||||
// Create a type that maps GameUpdateType to its corresponding update type
|
||||
type UpdateTypeMap<T extends GameUpdateType> = Extract<GameUpdate, { type: T }>;
|
||||
|
||||
// Then use it to create the record type
|
||||
export type GameUpdates = {
|
||||
[K in GameUpdateType]: UpdateTypeMap<K>[];
|
||||
}
|
||||
|
||||
export interface MapPos {
|
||||
x: number
|
||||
y: number
|
||||
@@ -83,6 +92,7 @@ export class EmojiMessage {
|
||||
}
|
||||
|
||||
export class Cell {
|
||||
public index: number
|
||||
|
||||
private strRepr: string
|
||||
|
||||
@@ -347,7 +357,7 @@ export interface Game {
|
||||
forEachTile(fn: (tile: Tile) => void): void
|
||||
executions(): ExecutionView[]
|
||||
terraNullius(): TerraNullius
|
||||
executeNextTick(): GameUpdate[]
|
||||
executeNextTick(): GameUpdates
|
||||
ticks(): Tick
|
||||
inSpawnPhase(): boolean
|
||||
addExecution(...exec: Execution[]): void
|
||||
|
||||
+40
-21
@@ -1,5 +1,5 @@
|
||||
import { Config } from "../configuration/Config";
|
||||
import { Cell, Execution, MutableGame, Game, MutablePlayer, PlayerID, PlayerInfo, Player, TerraNullius, Tile, Unit, MutableAllianceRequest, Alliance, Nation, UnitType, UnitInfo, TerrainMap, DefenseBonus, MutableTile, GameUpdate, GameUpdateType, AllPlayers } from "./Game";
|
||||
import { Cell, Execution, MutableGame, Game, MutablePlayer, PlayerID, PlayerInfo, Player, TerraNullius, Tile, Unit, MutableAllianceRequest, Alliance, Nation, UnitType, UnitInfo, TerrainMap, DefenseBonus, MutableTile, GameUpdate, GameUpdateType, AllPlayers, GameUpdates } from "./Game";
|
||||
import { TerrainMapImpl } from "./TerrainMapLoader";
|
||||
import { PlayerImpl } from "./PlayerImpl";
|
||||
import { TerraNulliusImpl } from "./TerraNulliusImpl";
|
||||
@@ -23,7 +23,6 @@ export class GameImpl implements MutableGame {
|
||||
|
||||
private unInitExecs: Execution[] = []
|
||||
|
||||
// idCounter: PlayerID = 1; // Zero reserved for TerraNullius
|
||||
map: TileImpl[][]
|
||||
|
||||
private nations_: Nation[] = []
|
||||
@@ -41,7 +40,7 @@ export class GameImpl implements MutableGame {
|
||||
private nextPlayerID = 1
|
||||
private _nextUnitID = 1
|
||||
|
||||
private updates: GameUpdate[] = []
|
||||
private updates: GameUpdates = createGameUpdatesMap()
|
||||
|
||||
constructor(
|
||||
private _terrainMap: TerrainMapImpl,
|
||||
@@ -67,6 +66,11 @@ export class GameImpl implements MutableGame {
|
||||
))
|
||||
}
|
||||
|
||||
addUpdate(update: GameUpdate) {
|
||||
(this.updates[update.type] as any[]).push(update);
|
||||
}
|
||||
|
||||
|
||||
nextUnitID(): number {
|
||||
const old = this._nextUnitID
|
||||
this._nextUnitID++
|
||||
@@ -79,20 +83,20 @@ export class GameImpl implements MutableGame {
|
||||
throw Error(`cannot set fallout, tile ${tile} has owner`)
|
||||
}
|
||||
ti._hasFallout = true
|
||||
this.updates.push(ti.toUpdate())
|
||||
this.addUpdate(ti.toUpdate())
|
||||
}
|
||||
|
||||
addTileDefenseBonus(tile: Tile, unit: Unit, amount: number): DefenseBonus {
|
||||
const df = { unit: unit, tile: tile, amount: amount };
|
||||
(tile as TileImpl)._defenseBonuses.push(df)
|
||||
this.updates.push((tile as TileImpl).toUpdate())
|
||||
this.addUpdate((tile as TileImpl).toUpdate())
|
||||
return df
|
||||
}
|
||||
|
||||
removeTileDefenseBonus(bonus: DefenseBonus): void {
|
||||
const t = bonus.tile as TileImpl
|
||||
t._defenseBonuses = t._defenseBonuses.filter(db => db != bonus)
|
||||
this.updates.push(t.toUpdate())
|
||||
this.addUpdate(t.toUpdate())
|
||||
}
|
||||
|
||||
units(...types: UnitType[]): UnitImpl[] {
|
||||
@@ -122,7 +126,7 @@ export class GameImpl implements MutableGame {
|
||||
}
|
||||
const ar = new AllianceRequestImpl(requestor, recipient, this._ticks, this)
|
||||
this.allianceRequests.push(ar)
|
||||
this.updates.push(ar.toUpdate())
|
||||
this.addUpdate(ar.toUpdate())
|
||||
return ar
|
||||
}
|
||||
|
||||
@@ -131,7 +135,7 @@ export class GameImpl implements MutableGame {
|
||||
const alliance = new AllianceImpl(this, request.requestor() as PlayerImpl, request.recipient() as PlayerImpl, this._ticks)
|
||||
this.alliances_.push(alliance);
|
||||
(request.requestor() as PlayerImpl).pastOutgoingAllianceRequests.push(request)
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.AllianceRequestReply,
|
||||
request: request.toUpdate(),
|
||||
accepted: true,
|
||||
@@ -142,7 +146,7 @@ export class GameImpl implements MutableGame {
|
||||
rejectAllianceRequest(request: AllianceRequestImpl) {
|
||||
this.allianceRequests = this.allianceRequests.filter(ar => ar != request);
|
||||
(request.requestor() as PlayerImpl).pastOutgoingAllianceRequests.push(request)
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.AllianceRequestReply,
|
||||
request: request.toUpdate(),
|
||||
accepted: true
|
||||
@@ -164,8 +168,8 @@ export class GameImpl implements MutableGame {
|
||||
return this._ticks
|
||||
}
|
||||
|
||||
executeNextTick(): GameUpdate[] {
|
||||
this.updates = []
|
||||
executeNextTick(): GameUpdates {
|
||||
this.updates = createGameUpdatesMap()
|
||||
this.execs.forEach(e => {
|
||||
if (e.isActive() && (!this.inSpawnPhase() || e.activeDuringSpawnPhase())) {
|
||||
e.tick(this._ticks)
|
||||
@@ -194,6 +198,10 @@ export class GameImpl implements MutableGame {
|
||||
})
|
||||
consolex.log(`tick ${this._ticks}: hash ${hash}`)
|
||||
}
|
||||
for (const player of this._players.values()) {
|
||||
// Players change each to so always add them
|
||||
this.addUpdate(player.toUpdate())
|
||||
}
|
||||
return this.updates
|
||||
}
|
||||
|
||||
@@ -357,7 +365,7 @@ export class GameImpl implements MutableGame {
|
||||
owner._lastTileChange = this._ticks
|
||||
this.updateBorders(tile)
|
||||
tileImpl._hasFallout = false
|
||||
this.updates.push((tile as TileImpl).toUpdate())
|
||||
this.addUpdate((tile as TileImpl).toUpdate())
|
||||
}
|
||||
|
||||
relinquish(tile: Tile) {
|
||||
@@ -377,7 +385,7 @@ export class GameImpl implements MutableGame {
|
||||
|
||||
tileImpl._owner = this._terraNullius
|
||||
this.updateBorders(tile)
|
||||
this.updates.push(
|
||||
this.addUpdate(
|
||||
(tile as TileImpl).toUpdate()
|
||||
)
|
||||
}
|
||||
@@ -417,11 +425,11 @@ export class GameImpl implements MutableGame {
|
||||
}
|
||||
|
||||
public fireUnitUpdateEvent(unit: Unit) {
|
||||
this.updates.push((unit as UnitImpl).toUpdate())
|
||||
this.addUpdate((unit as UnitImpl).toUpdate())
|
||||
}
|
||||
|
||||
target(targeter: Player, target: Player) {
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.TargetPlayer,
|
||||
playerID: targeter.smallID(),
|
||||
targetID: target.smallID(),
|
||||
@@ -448,7 +456,7 @@ export class GameImpl implements MutableGame {
|
||||
throw new Error(`must have exactly one alliance, have ${alliances.length}`)
|
||||
}
|
||||
this.alliances_ = this.alliances_.filter(a => a != alliances[0])
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.BrokeAlliance,
|
||||
traitorID: breaker.smallID(),
|
||||
betrayedID: other.smallID()
|
||||
@@ -463,7 +471,7 @@ export class GameImpl implements MutableGame {
|
||||
throw new Error(`cannot expire alliance: must have exactly one alliance, have ${alliances.length}`)
|
||||
}
|
||||
this.alliances_ = this.alliances_.filter(a => a != alliances[0])
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.AllianceExpired,
|
||||
player1: alliance.requestor().smallID(),
|
||||
player2: alliance.recipient().smallID()
|
||||
@@ -473,7 +481,7 @@ export class GameImpl implements MutableGame {
|
||||
sendEmojiUpdate(sender: Player, recipient: Player | typeof AllPlayers, emoji: string): void {
|
||||
const recipientID = recipient === AllPlayers ? recipient : recipient.smallID();
|
||||
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.EmojiUpdate,
|
||||
message: emoji,
|
||||
senderID: sender.smallID(),
|
||||
@@ -483,7 +491,7 @@ export class GameImpl implements MutableGame {
|
||||
}
|
||||
|
||||
setWinner(winner: Player): void {
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.WinUpdate,
|
||||
winnerID: winner.smallID()
|
||||
})
|
||||
@@ -502,11 +510,22 @@ export class GameImpl implements MutableGame {
|
||||
if (playerID != null) {
|
||||
id = this.player(playerID).smallID()
|
||||
}
|
||||
this.updates.push({
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.DisplayEvent,
|
||||
messageType: type,
|
||||
message: message,
|
||||
playerID: id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Or a more dynamic approach that will catch new enum values:
|
||||
const createGameUpdatesMap = (): GameUpdates => {
|
||||
const map = {} as GameUpdates;
|
||||
Object.values(GameUpdateType)
|
||||
.filter(key => !isNaN(Number(key))) // Filter out reverse mappings
|
||||
.forEach(key => {
|
||||
map[key as GameUpdateType] = [];
|
||||
});
|
||||
return map;
|
||||
};
|
||||
@@ -25,6 +25,8 @@ ctx.addEventListener('message', async (e: MessageEvent<MainThreadMessage>) => {
|
||||
const message = e.data;
|
||||
|
||||
switch (message.type) {
|
||||
case 'heartbeat':
|
||||
break
|
||||
case 'init':
|
||||
try {
|
||||
gameRunner = createGameRunner(
|
||||
|
||||
@@ -85,6 +85,12 @@ export class WorkerClient {
|
||||
});
|
||||
}
|
||||
|
||||
sendHeartbeat() {
|
||||
this.worker.postMessage({
|
||||
type: 'heartbeat'
|
||||
});
|
||||
}
|
||||
|
||||
playerInteraction(playerID: PlayerID, tile: Tile): Promise<PlayerActions> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.isInitialized) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { GameConfig, GameID, Turn } from "../Schemas";
|
||||
import { PlayerActions, PlayerID } from "../game/Game";
|
||||
|
||||
export type WorkerMessageType =
|
||||
| 'heartbeat'
|
||||
| 'init'
|
||||
| 'initialized'
|
||||
| 'turn'
|
||||
@@ -16,6 +17,10 @@ interface BaseWorkerMessage {
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface HeartbeatMessage extends BaseWorkerMessage {
|
||||
type: 'heartbeat'
|
||||
}
|
||||
|
||||
// Messages from main thread to worker
|
||||
export interface InitMessage extends BaseWorkerMessage {
|
||||
type: 'init';
|
||||
@@ -51,7 +56,7 @@ export interface PlayerActionsResultMessage extends BaseWorkerMessage {
|
||||
}
|
||||
|
||||
// Union types for type safety
|
||||
export type MainThreadMessage = InitMessage | TurnMessage | PlayerActionsMessage
|
||||
export type MainThreadMessage = HeartbeatMessage | InitMessage | TurnMessage | PlayerActionsMessage
|
||||
|
||||
// Message send from worker
|
||||
export type WorkerMessage = InitializedMessage | GameUpdateMessage | PlayerActionsResultMessage;
|
||||
Reference in New Issue
Block a user