use GameMap for storage in worker thread

This commit is contained in:
evanpelle
2025-01-14 15:48:04 -08:00
committed by Evan
parent b22532d41f
commit 2068e42982
10 changed files with 147 additions and 156 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ export async function createClientGame(lobbyConfig: LobbyConfig, gameConfig: Gam
const terrainMap = await loadTerrainMap(gameConfig.gameMap);
const worker = new WorkerClient(lobbyConfig.gameID, gameConfig)
await worker.initialize()
const gameView = new GameView(worker, config, terrainMap.map)
const gameView = new GameView(worker, config, terrainMap.terrain)
consolex.log('going to init path finder')
+1 -1
View File
@@ -14,7 +14,7 @@ import { GameUpdateViewData, packTileData } from "./GameView";
export async function createGameRunner(gameID: string, gameConfig: GameConfig, callBack: (gu: GameUpdateViewData) => void): Promise<GameRunner> {
const config = getConfig(gameConfig)
const terrainMap = await loadTerrainMap(gameConfig.gameMap);
const game = createGame(terrainMap.gameMap, terrainMap.miniGameMap, terrainMap.map, terrainMap.miniMap, config)
const game = createGame(terrainMap.gameMap, terrainMap.miniGameMap, terrainMap.nationMap, config)
const gr = new GameRunner(game as MutableGame, new Executor(game, gameID), callBack)
gr.init()
return gr
+1 -1
View File
@@ -4,7 +4,7 @@ import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage
import { ClientID } from "./Schemas";
import { TerraNulliusImpl } from './game/TerraNulliusImpl';
import { WorkerClient } from './worker/WorkerClient';
import { TileRef } from './game/GameMap';
import { GameMap, TileRef } from './game/GameMap';
export class TileView {
+1 -1
View File
@@ -27,7 +27,7 @@ export class WinCheckExecution implements Execution {
return
}
const max = sorted[0]
if (max.numTilesOwned() / this.mg.terrainMap().numLandTiles() * 100 > this.mg.config().percentageTilesOwnedToWin()) {
if (max.numTilesOwned() / this.mg.map().numLandTiles() * 100 > this.mg.config().percentageTilesOwnedToWin()) {
this.mg.setWinner(max)
this.active = false
}
+1 -3
View File
@@ -203,7 +203,6 @@ export interface TerrainTile {
cell(): Cell
neighbors(): TerrainTile[]
cost(): number
key(): TerrainTileKey
}
export interface DefenseBonus {
@@ -260,6 +259,7 @@ export interface TerraNullius {
isPlayer(): false
id(): PlayerID // always zero, maybe make it TerraNulliusID?
clientID(): ClientID
smallID(): number
}
export interface Player {
@@ -376,8 +376,6 @@ export interface Game {
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void
units(...types: UnitType[]): Unit[]
unitInfo(type: UnitType): UnitInfo
terrainMap(): TerrainMap
terrainMiniMap(): TerrainMap
map(): GameMap
miniMap(): GameMap
+34 -63
View File
@@ -1,6 +1,6 @@
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, GameUpdates } from "./Game";
import { TerrainMapImpl } from "./TerrainMapLoader";
import { NationMap, TerrainMapImpl } from "./TerrainMapLoader";
import { PlayerImpl } from "./PlayerImpl";
import { TerraNulliusImpl } from "./TerraNulliusImpl";
import { TileImpl } from "./TileImpl";
@@ -10,11 +10,10 @@ import { ClientID, GameConfig } from "../Schemas";
import { MessageType } from './Game';
import { UnitImpl } from "./UnitImpl";
import { consolex } from "../Consolex";
import { string } from "zod";
import { GameMap } from "./GameMap";
import { GameMap, TileRef } from "./GameMap";
export function createGame(gameMap: GameMap, miniGameMap: GameMap, terrainMap: TerrainMapImpl, miniMap: TerrainMap, config: Config): Game {
return new GameImpl(terrainMap, miniMap, gameMap, miniGameMap, config)
export function createGame(gameMap: GameMap, miniGameMap: GameMap, nationMap: NationMap, config: Config): Game {
return new GameImpl(gameMap, miniGameMap, nationMap, config)
}
export type CellString = string
@@ -24,7 +23,6 @@ export class GameImpl implements MutableGame {
private unInitExecs: Execution[] = []
_map: TileImpl[][]
private nations_: Nation[] = []
@@ -32,7 +30,6 @@ export class GameImpl implements MutableGame {
private execs: Execution[] = []
private _width: number
private _height: number
private _numLandTiles: number
_terraNullius: TerraNulliusImpl
allianceRequests: AllianceRequestImpl[] = []
@@ -44,24 +41,15 @@ export class GameImpl implements MutableGame {
private updates: GameUpdates = createGameUpdatesMap()
constructor(
private _terrainMap: TerrainMapImpl,
private _miniMap: TerrainMap,
private gameMap: GameMap,
private miniGameMap: GameMap,
nationMap: NationMap,
private _config: Config,
) {
this._terraNullius = new TerraNulliusImpl()
this._width = _terrainMap.width();
this._height = _terrainMap.height();
this._map = new Array(this._width);
for (let x = 0; x < this._width; x++) {
this._map[x] = new Array(this._height);
for (let y = 0; y < this._height; y++) {
let cell = new Cell(x, y);
this._map[x][y] = new TileImpl(this, this._terraNullius, cell, _terrainMap);
}
}
this.nations_ = _terrainMap.nationMap.nations
this._width = gameMap.width();
this._height = gameMap.height();
this.nations_ = nationMap.nations
.map(n => new Nation(
n.name,
new Cell(n.coordinates[0], n.coordinates[1]),
@@ -91,21 +79,23 @@ export class GameImpl implements MutableGame {
if (tile.hasOwner()) {
throw Error(`cannot set fallout, tile ${tile} has owner`)
}
ti._hasFallout = true
this.gameMap.setFallout(tile.ref(), true)
this.addUpdate(ti.toUpdate())
}
addTileDefenseBonus(tile: Tile, unit: Unit, amount: number): DefenseBonus {
// TODO!!
const df = { unit: unit, tile: tile, amount: amount };
(tile as TileImpl)._defenseBonuses.push(df)
this.addUpdate((tile as TileImpl).toUpdate())
// (tile as TileImpl)._defenseBonuses.push(df)
// 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.addUpdate(t.toUpdate())
// TODO!!
// const t = bonus.tile as TileImpl
// t._defenseBonuses = t._defenseBonuses.filter(db => db != bonus)
// this.addUpdate(t.toUpdate())
}
units(...types: UnitType[]): UnitImpl[] {
@@ -305,7 +295,7 @@ export class GameImpl implements MutableGame {
tile(cell: Cell): MutableTile {
this.assertIsOnMap(cell)
return this._map[cell.x][cell.y] as MutableTile
return new TileImpl(this, this.gameMap.ref(cell.x, cell.y))
}
isOnMap(cell: Cell): boolean {
@@ -315,36 +305,25 @@ export class GameImpl implements MutableGame {
&& cell.y < this._height
}
fromRef(ref: TileRef): Tile {
return new TileImpl(this, ref)
}
neighbors(tile: Tile): Tile[] {
const x = tile.cell().x
const y = tile.cell().y
const ns: TileImpl[] = []
if (y > 0) {
ns.push(this._map[x][y - 1])
}
if (y < this._height - 1) {
ns.push(this._map[x][y + 1])
}
if (x > 0) {
ns.push(this._map[x - 1][y])
}
if (x < this._width - 1) {
ns.push(this._map[x + 1][y])
}
return ns
return this.gameMap.neighbors(tile.ref()).map(tr => new TileImpl(this, tr))
}
neighborsWithDiag(tile: Tile): Tile[] {
const x = tile.cell().x
const y = tile.cell().y
const ns: TileImpl[] = []
const ns: Tile[] = []
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue // Skip the center tile
const newX = x + dx
const newY = y + dy
if (newX >= 0 && newX < this._width && newY >= 0 && newY < this._height) {
ns.push(this._map[newX][newY])
ns.push(this.fromRef(this.gameMap.ref(newX, newY)))
}
}
}
@@ -362,18 +341,18 @@ export class GameImpl implements MutableGame {
throw Error(`cannot conquer water`)
}
const tileImpl = tile as TileImpl
let previousOwner = tileImpl._owner
let previousOwner = tileImpl.owner() as TerraNullius | PlayerImpl
if (previousOwner.isPlayer()) {
previousOwner._lastTileChange = this._ticks
previousOwner._tiles.delete(tile.cell().toString())
previousOwner._borderTiles.delete(tileImpl)
tileImpl._isBorder = false
this.gameMap.setBorder(tileImpl.ref(), false)
}
tileImpl._owner = owner
this.gameMap.setPlayerId(tileImpl.ref(), owner.smallID())
owner._tiles.set(tile.cell().toString(), tile)
owner._lastTileChange = this._ticks
this.updateBorders(tile)
tileImpl._hasFallout = false
this.gameMap.setFallout(tileImpl.ref(), false)
this.addUpdate((tile as TileImpl).toUpdate())
}
@@ -386,13 +365,13 @@ export class GameImpl implements MutableGame {
}
const tileImpl = tile as TileImpl
let previousOwner = tileImpl._owner as PlayerImpl
let previousOwner = tileImpl.owner() as PlayerImpl
previousOwner._lastTileChange = this._ticks
previousOwner._tiles.delete(tile.cell().toString())
previousOwner._borderTiles.delete(tileImpl)
tileImpl._isBorder = false
this.gameMap.setBorder(tileImpl.ref(), false)
tileImpl._owner = this._terraNullius
this.gameMap.setPlayerId(tileImpl.ref(), 0)
this.updateBorders(tile)
this.addUpdate(
(tile as TileImpl).toUpdate()
@@ -406,15 +385,15 @@ export class GameImpl implements MutableGame {
for (const t of tiles) {
if (!t.hasOwner()) {
t._isBorder = false
this.gameMap.setBorder(t.ref(), false)
continue
}
if (this.isBorder(t)) {
(t.owner() as PlayerImpl)._borderTiles.add(t);
t._isBorder = true
this.gameMap.setBorder(t.ref(), true)
} else {
(t.owner() as PlayerImpl)._borderTiles.delete(t);
t._isBorder = false
this.gameMap.setBorder(t.ref(), false)
}
// this.updates.push(t.toUpdate())
}
@@ -506,14 +485,6 @@ export class GameImpl implements MutableGame {
})
}
public terrainMap(): TerrainMapImpl {
return this._terrainMap
}
public terrainMiniMap(): TerrainMap {
return this._miniMap
}
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void {
let id = null
if (playerID != null) {
+2 -1
View File
@@ -6,6 +6,7 @@ import { UnitImpl } from "./UnitImpl";
import { TileImpl } from "./TileImpl";
import { MessageType } from './Game';
import { renderTroops } from "../../client/Utils";
import { TerraNulliusImpl } from "./TerraNulliusImpl";
interface Target {
tick: Tick
@@ -135,7 +136,7 @@ export class PlayerImpl implements MutablePlayer {
for (const border of this.borderTiles()) {
for (const neighbor of border.neighbors()) {
if (neighbor.terrain().isLand() && neighbor.owner() != this) {
ns.add((neighbor as TileImpl)._owner);
ns.add(neighbor.owner() as PlayerImpl | TerraNulliusImpl);
}
}
}
+3
View File
@@ -9,6 +9,9 @@ export class TerraNulliusImpl implements TerraNullius {
constructor() {
}
smallID(): number {
return 0
}
clientID(): ClientID {
return "TERRA_NULLIUS_CLIENT_ID"
}
+9 -9
View File
@@ -3,7 +3,7 @@ import { Cell, GameMapType, TerrainMap, TerrainTile, TerrainType } from './Game'
import { GameMap } from './GameMap';
import { terrainMapFileLoader } from './TerrainMapFileLoader';
const loadedMaps = new Map<GameMapType, { map: TerrainMapImpl, miniMap: TerrainMapImpl, gameMap: GameMap, miniGameMap: GameMap }>()
const loadedMaps = new Map<GameMapType, { nationMap: NationMap, gameMap: GameMap, miniGameMap: GameMap, terrain: TerrainMap }>()
export interface NationMap {
name: string;
@@ -149,20 +149,20 @@ export class TerrainMapImpl implements TerrainMap {
}
export async function loadTerrainMap(map: GameMapType): Promise<{ map: TerrainMapImpl, miniMap: TerrainMapImpl, gameMap: GameMap, miniGameMap: GameMap }> {
export async function loadTerrainMap(map: GameMapType): Promise<{ nationMap: NationMap, gameMap: GameMap, miniGameMap: GameMap, terrain: TerrainMap }> {
if (loadedMaps.has(map)) {
return loadedMaps.get(map)
}
const mapFiles = await terrainMapFileLoader.getMapData(map)
const { terrain: mainMap, gameMap: mainGameMap } = await loadTerrainFromFile(mapFiles.mapBin)
mainMap.nationMap = mapFiles.nationMap
const { terrain: mini, gameMap: miniMap } = await loadTerrainFromFile(mapFiles.miniMapBin)
loadedMaps.set(map, { map: mainMap, miniMap: mini, gameMap: mainGameMap, miniGameMap: miniMap })
return { map: mainMap, miniMap: mini, gameMap: mainGameMap, miniGameMap: miniMap }
const gameMap = await loadTerrainFromFile(mapFiles.mapBin)
const miniGameMap = await loadTerrainFromFile(mapFiles.miniMapBin)
const result = { nationMap: mapFiles.nationMap, gameMap: gameMap.map, miniGameMap: miniGameMap.map, terrain: gameMap.terrain }
loadedMaps.set(map, result)
return result
}
export async function loadTerrainFromFile(fileData: string): Promise<{ terrain: TerrainMapImpl, gameMap: GameMap }> {
export async function loadTerrainFromFile(fileData: string): Promise<{ map: GameMap, terrain: TerrainMap }> {
const width = (fileData.charCodeAt(1) << 8) | fileData.charCodeAt(0);
const height = (fileData.charCodeAt(3) << 8) | fileData.charCodeAt(2);
@@ -187,7 +187,7 @@ export async function loadTerrainFromFile(fileData: string): Promise<{ terrain:
const gm = new GameMap(width, height, m.rawData, numLand)
m._numLandTiles = numLand;
return { terrain: m, gameMap: gm };
return { map: gm, terrain: m }
}
+94 -76
View File
@@ -1,51 +1,51 @@
import { Tile, Cell, TerrainType, Player, TerraNullius, MutablePlayer, TerrainTile, DefenseBonus, MutableTile, TileUpdate, GameUpdateType } from "./Game";
import { Tile, Cell, TerrainType, Player, TerraNullius, MutablePlayer, TerrainTile, DefenseBonus, MutableTile, TileUpdate, GameUpdateType, TerrainTileKey } from "./Game";
import { TerrainMapImpl, TerrainTileImpl } from "./TerrainMapLoader";
import { GameImpl } from "./GameImpl";
import { PlayerImpl } from "./PlayerImpl";
import { TerraNulliusImpl } from "./TerraNulliusImpl";
import { TileRef } from "./GameMap";
import { GameMap, TileRef } from "./GameMap";
export class TileImpl implements MutableTile {
public _isBorder = false;
private _neighbors: Tile[] = null;
public _defenseBonuses: DefenseBonus[] = []
public _hasFallout = false
constructor(
private readonly gs: GameImpl,
public _owner: PlayerImpl | TerraNulliusImpl,
private readonly _cell: Cell,
private terrainMap: TerrainMapImpl
private ref_: TileRef
) { }
terrain(): TerrainTile {
return new TerrainRef(this.gs.map(), this.ref_)
}
neighborsWrapped(): Tile[] {
// TODO: implement!
return this.neighbors()
}
ref(): TileRef {
return this.gs.map().ref(this._cell.x, this._cell.y)
return this.ref_
}
toUpdate(): TileUpdate {
return {
type: GameUpdateType.Tile,
pos: {
x: this._cell.x,
y: this._cell.y
x: this.x(),
y: this.y()
},
ownerID: this._owner.isPlayer() ? this._owner.smallID() : 0,
hasFallout: this._hasFallout,
ownerID: this.owner().smallID(),
hasFallout: this.hasFallout(),
hasDefenseBonus: this.hasDefenseBonus(),
isBorder: this.isBorder(),
}
}
hasFallout(): boolean {
return this._hasFallout
return this.gs.map().hasFallout(this.ref_)
}
type(): TerrainType {
return this.terrainMap.terrain(this._cell)._type
return this.gs.map().getTerrainType(this.ref_)
}
hasDefenseBonus(): boolean {
@@ -53,54 +53,23 @@ export class TileImpl implements MutableTile {
}
defenseBonus(player: Player): number {
if (this.owner() == player) {
throw Error(`cannot get defense bonus of tile already owned by player, ${player}`)
}
let bonusAmount = 0
for (const bonus of this._defenseBonuses) {
if (bonus.unit.owner() != player) {
bonusAmount += bonus.amount
}
}
return Math.max(bonusAmount, 1)
// TODO!
return 0
// if (this.owner() == player) {
// throw Error(`cannot get defense bonus of tile already owned by player, ${player}`)
// }
// let bonusAmount = 0
// for (const bonus of this._defenseBonuses) {
// if (bonus.unit.owner() != player) {
// bonusAmount += bonus.amount
// }
// }
// return Math.max(bonusAmount, 1)
}
defenseBonuses(): DefenseBonus[] {
return this._defenseBonuses
}
neighborsWrapped(): Tile[] {
const x = this._cell.x;
const y = this._cell.y;
const ns: Tile[] = [];
// Check top neighbor
if (y > 0) {
ns.push(this.gs._map[x][y - 1]);
}
// Check bottom neighbor
if (y < this.gs.height() - 1) {
ns.push(this.gs._map[x][y + 1]);
}
// Check left neighbor (wrap around)
if (x > 0) {
ns.push(this.gs._map[x - 1][y]);
} else {
ns.push(this.gs._map[this.gs.width() - 1][y]);
}
// Check right neighbor (wrap around)
if (x < this.gs.width() - 1) {
ns.push(this.gs._map[x + 1][y]);
} else {
ns.push(this.gs._map[0][y]);
}
return ns;
}
terrain(): TerrainTile {
return this.terrainMap.terrain(this._cell)
// TODO!
return []
}
borders(other: Player | TerraNullius): boolean {
@@ -112,25 +81,74 @@ export class TileImpl implements MutableTile {
return false;
}
hasOwner(): boolean { return this._owner != this.gs._terraNullius; }
owner(): MutablePlayer | TerraNullius { return this._owner; }
isBorder(): boolean { return this._isBorder; }
cell(): Cell { return this._cell; }
hasOwner(): boolean { return this.owner().smallID() != 0 }
owner(): MutablePlayer | TerraNullius {
const ownerID = this.gs.map().playerId(this.ref_)
if (ownerID == 0) {
return this.gs.terraNullius()
}
}
isBorder(): boolean { return this.gs.map().isBorder(this.ref_); }
cell(): Cell { return new Cell(this.x(), this.y()); }
x(): number {
return this._cell.x
return this.gs.map().x(this.ref_)
}
y(): number {
return this._cell.y
return this.gs.map().y(this.ref_)
}
neighbors(): Tile[] {
if (this._neighbors == null) {
this._neighbors = this.gs.neighbors(this);
}
return this._neighbors;
return this.gs.neighbors(this)
}
cost(): number {
return this.terrain().magnitude() < 10 ? 2 : 1
};
}
export class TerrainRef implements TerrainTile {
constructor(private map: GameMap, private ref: TileRef) { }
isLand(): boolean {
return this.map.isLand(this.ref)
}
isShore(): boolean {
return this.map.isShore(this.ref)
}
isOceanShore(): boolean {
return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > 0;
}
isWater(): boolean {
return !this.map.isLand(this.ref)
}
isShorelineWater(): boolean {
return this.isWater() && this.isShore()
}
isOcean(): boolean {
return this.map.isOcean(this.ref)
}
isLake(): boolean {
return this.isWater() && !this.isOcean()
}
type(): TerrainType {
return this.map.getTerrainType(this.ref)
}
magnitude(): number {
return this.map.magnitude(this.ref)
}
equals(other: TerrainTile): boolean {
return this.ref == (other as TerrainRef).ref
}
cell(): Cell {
return this.map.cell(this.ref)
}
neighbors(): TerrainTile[] {
return this.map.neighbors(this.ref).map(tr => new TerrainRef(this.map, tr))
}
cost(): number {
return this.map.cost(this.ref)
}
}