Skip to content
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/core/configuration/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Game,
GameMapType,
GameMode,
GameType,
Gold,
Player,
PlayerInfo,
Expand All @@ -28,6 +29,7 @@ export enum GameEnv {

export interface ServerConfig {
turnIntervalMs(): number;
startDelay(gameType: GameType): number;
gameCreationRate(): number;
lobbyMaxPlayers(
map: GameMapType,
Expand Down Expand Up @@ -72,6 +74,7 @@ export interface NukeMagnitude {
}

export interface Config {
turnIntervalMs(): number;
samHittingChance(): number;
samWarheadHittingChance(): number;
spawnImmunityDuration(): Tick;
Expand All @@ -90,6 +93,12 @@ export interface Config {
instantBuild(): boolean;
isRandomSpawn(): boolean;
numSpawnPhaseTurns(): number;
numGracePeriodTurns(): number;
isSpawnPhase(
ticks: number,
gameType: GameType,
firstHumanSpawnTick?: number,
): boolean;
userSettings(): UserSettings;
playerTeams(): TeamCountConfig;

Expand Down
30 changes: 30 additions & 0 deletions src/core/configuration/DefaultConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ export abstract class DefaultServerConfig implements ServerConfig {
turnIntervalMs(): number {
return 100;
}
// Start game immediately for single player games, with delay for multiplayer games
startDelay(gameType: GameType): number {
return gameType === GameType.Singleplayer ? 0 : 2000;
}
gameCreationRate(): number {
return 60 * 1000;
}
Expand Down Expand Up @@ -273,6 +277,10 @@ export class DefaultConfig implements Config {
return this._serverConfig;
}

turnIntervalMs(): number {
return this._serverConfig.turnIntervalMs();
}

userSettings(): UserSettings {
if (this._userSettings === null) {
throw new Error("userSettings is null");
Expand Down Expand Up @@ -625,9 +633,31 @@ export class DefaultConfig implements Config {
boatMaxNumber(): number {
return 3;
}

numSpawnPhaseTurns(): number {
return this._gameConfig.gameType === GameType.Singleplayer ? 100 : 300;
}
// Amount of ticks for player to change spawn placement in singleplayer
numGracePeriodTurns(): number {
return 15;
}
isSpawnPhase(
ticks: number,
gameType: GameType,
firstHumanSpawnTick?: number,
): boolean {
if (ticks > this.numSpawnPhaseTurns()) {
return false;
}
if (gameType !== GameType.Singleplayer) {
return true;
}
if (!firstHumanSpawnTick) {
return true;
}
return ticks <= firstHumanSpawnTick + this.numGracePeriodTurns();
}

numBots(): number {
return this.bots();
}
Expand Down
3 changes: 2 additions & 1 deletion src/core/execution/FakeHumanExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ export class FakeHumanExecution implements Execution {
this.trackTradeShipsAndRetaliate();
}

if (ticks % this.attackRate !== this.attackTick) {
// Tries to spawn nations on the first tick of the match
if (ticks > 1 && ticks % this.attackRate !== this.attackTick) {
return;
}

Expand Down
23 changes: 13 additions & 10 deletions src/core/execution/SpawnExecution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,21 @@ export class SpawnExecution implements Execution {
player = this.mg.addPlayer(this.playerInfo);
}

player.tiles().forEach((t) => player.relinquish(t));
getSpawnTiles(this.mg, this.tile).forEach((t) => {
player.conquer(t);
});

if (!player.hasSpawned()) {
this.mg.addExecution(new PlayerExecution(player));
if (player.type() === PlayerType.Bot) {
this.mg.addExecution(new BotExecution(player));
if (player) {
player.tiles().forEach((t) => player.relinquish(t));
getSpawnTiles(this.mg, this.tile).forEach((t) => {
player.conquer(t);
});

if (player && !player.hasSpawned()) {
this.mg.addExecution(new PlayerExecution(player));
if (player.type() === PlayerType.Bot) {
this.mg.addExecution(new BotExecution(player));
}

player.setHasSpawned(true);
}
}
player.setHasSpawned(true);
}

isActive(): boolean {
Expand Down
15 changes: 14 additions & 1 deletion src/core/game/GameImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class GameImpl implements Game {
private _height: number;
_terraNullius: TerraNulliusImpl;

private firstHumanSpawnTick: number;

allianceRequests: AllianceRequestImpl[] = [];
alliances_: AllianceImpl[] = [];

Expand Down Expand Up @@ -338,7 +340,18 @@ export class GameImpl implements Game {
}

inSpawnPhase(): boolean {
return this._ticks <= this.config().numSpawnPhaseTurns();
if (!this.firstHumanSpawnTick) {
const humanSpawned = Array.from(this._players.values()).some(
(p) => p.type() === PlayerType.Human && p.hasSpawned(),
);
if (humanSpawned) this.firstHumanSpawnTick = this._ticks;
}
return this._config.isSpawnPhase(
this.ticks(),
this.config().gameConfig().gameType,
this.firstHumanSpawnTick,
);
return true;
}

ticks(): number {
Expand Down
14 changes: 13 additions & 1 deletion src/core/game/GameView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,8 @@ export class GameView implements GameMap {

private _map: GameMap;

private firstHumanSpawnTick: number;

constructor(
public worker: WorkerClient,
private _config: Config,
Expand Down Expand Up @@ -655,7 +657,17 @@ export class GameView implements GameMap {
return this.lastUpdate.tick;
}
inSpawnPhase(): boolean {
return this.ticks() <= this._config.numSpawnPhaseTurns();
if (!this.firstHumanSpawnTick) {
const humanSpawned = this.playerViews().some(
(p) => p.type() === PlayerType.Human && p.hasSpawned(),
);
if (humanSpawned) this.firstHumanSpawnTick = this.ticks();
}
return this.config().isSpawnPhase(
this.ticks(),
this.config().gameConfig().gameType,
this.firstHumanSpawnTick,
);
}
config(): Config {
return this._config;
Expand Down
5 changes: 3 additions & 2 deletions src/server/GameManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,15 @@ export class GameManager {
if (!game.hasStarted()) {
// Prestart tells clients to start loading the game.
game.prestart();
// Start game on delay to allow time for clients to connect.
// Start game immediately for single player games, with delay for multiplayer games
const startDelay = this.config.startDelay(game.gameConfig.gameType);
setTimeout(() => {
try {
game.start();
} catch (error) {
this.log.error(`error starting game ${id}: ${error}`);
}
}, 2000);
}, startDelay);
}
}

Expand Down
5 changes: 4 additions & 1 deletion tests/util/TestServerConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { JWK } from "jose";
import { GameEnv, ServerConfig } from "../../src/core/configuration/Config";
import { GameMapType } from "../../src/core/game/Game";
import { GameMapType, GameType } from "../../src/core/game/Game";
import { GameID } from "../../src/core/Schemas";

export class TestServerConfig implements ServerConfig {
Expand Down Expand Up @@ -64,6 +64,9 @@ export class TestServerConfig implements ServerConfig {
numWorkers(): number {
throw new Error("Method not implemented.");
}
startDelay(gameType: GameType): number {
throw new Error("Method not implemented.");
}
workerIndex(gameID: GameID): number {
throw new Error("Method not implemented.");
}
Expand Down
Loading