mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 11:00:43 +00:00
164 lines
4.8 KiB
TypeScript
164 lines
4.8 KiB
TypeScript
import {
|
|
Cell,
|
|
Execution,
|
|
Game,
|
|
Player,
|
|
PlayerInfo,
|
|
TerraNullius,
|
|
PlayerType,
|
|
Alliance,
|
|
UnitType,
|
|
} from "../game/Game";
|
|
import {
|
|
AttackIntent,
|
|
BoatAttackIntentSchema,
|
|
ClientID,
|
|
GameID,
|
|
Intent,
|
|
Turn,
|
|
} from "../Schemas";
|
|
import { AttackExecution } from "./AttackExecution";
|
|
import { SpawnExecution } from "./SpawnExecution";
|
|
import { BotSpawner } from "./BotSpawner";
|
|
import { TransportShipExecution } from "./TransportShipExecution";
|
|
import { PseudoRandom } from "../PseudoRandom";
|
|
import { FakeHumanExecution } from "./FakeHumanExecution";
|
|
import { sanitize, simpleHash } from "../Util";
|
|
import { AllianceRequestExecution } from "./alliance/AllianceRequestExecution";
|
|
import { AllianceRequestReplyExecution } from "./alliance/AllianceRequestReplyExecution";
|
|
import { BreakAllianceExecution } from "./alliance/BreakAllianceExecution";
|
|
import { TargetPlayerExecution } from "./TargetPlayerExecution";
|
|
import { EmojiExecution } from "./EmojiExecution";
|
|
import { DonateExecution } from "./DonateExecution";
|
|
import { SetTargetTroopRatioExecution } from "./SetTargetTroopRatioExecution";
|
|
import { ConstructionExecution } from "./ConstructionExecution";
|
|
import { fixProfaneUsername, isProfaneUsername } from "../validations/username";
|
|
import { NoOpExecution } from "./NoOpExecution";
|
|
|
|
export class Executor {
|
|
// private random = new PseudoRandom(999)
|
|
private random: PseudoRandom = null;
|
|
|
|
constructor(
|
|
private mg: Game,
|
|
private gameID: GameID,
|
|
private clientID: ClientID,
|
|
) {
|
|
// Add one to avoid id collisions with bots.
|
|
this.random = new PseudoRandom(simpleHash(gameID) + 1);
|
|
}
|
|
|
|
createExecs(turn: Turn): Execution[] {
|
|
return turn.intents.map((i) => this.createExec(i));
|
|
}
|
|
|
|
createExec(intent: Intent): Execution {
|
|
if (intent.type != "spawn") {
|
|
if (!this.mg.hasPlayer(intent.playerID)) {
|
|
console.warn(
|
|
`player ${intent.playerID} not found on intent ${intent.type}`,
|
|
);
|
|
return new NoOpExecution();
|
|
}
|
|
const player = this.mg.player(intent.playerID);
|
|
if (player.clientID() != intent.clientID) {
|
|
console.warn(
|
|
`intent ${intent.type} has incorrect clientID ${intent.clientID} for player ${player.name()} with clientID ${player.clientID()}`,
|
|
);
|
|
return new NoOpExecution();
|
|
}
|
|
}
|
|
switch (intent.type) {
|
|
case "attack": {
|
|
return new AttackExecution(
|
|
intent.troops,
|
|
intent.playerID,
|
|
intent.targetID,
|
|
null,
|
|
);
|
|
}
|
|
case "spawn":
|
|
return new SpawnExecution(
|
|
new PlayerInfo(
|
|
intent.flag,
|
|
// Players see their original name, others see a sanitized version
|
|
intent.clientID == this.clientID
|
|
? sanitize(intent.name)
|
|
: fixProfaneUsername(sanitize(intent.name)),
|
|
intent.playerType,
|
|
intent.clientID,
|
|
intent.playerID,
|
|
),
|
|
this.mg.ref(intent.x, intent.y),
|
|
);
|
|
case "boat":
|
|
return new TransportShipExecution(
|
|
intent.playerID,
|
|
intent.targetID,
|
|
this.mg.ref(intent.x, intent.y),
|
|
intent.troops,
|
|
);
|
|
case "allianceRequest":
|
|
return new AllianceRequestExecution(intent.playerID, intent.recipient);
|
|
case "allianceRequestReply":
|
|
return new AllianceRequestReplyExecution(
|
|
intent.requestor,
|
|
intent.playerID,
|
|
intent.accept,
|
|
);
|
|
case "breakAlliance":
|
|
return new BreakAllianceExecution(intent.playerID, intent.recipient);
|
|
case "targetPlayer":
|
|
return new TargetPlayerExecution(intent.playerID, intent.target);
|
|
case "emoji":
|
|
return new EmojiExecution(
|
|
intent.playerID,
|
|
intent.recipient,
|
|
intent.emoji,
|
|
);
|
|
case "donate":
|
|
return new DonateExecution(
|
|
intent.playerID,
|
|
intent.recipient,
|
|
intent.troops,
|
|
);
|
|
case "troop_ratio":
|
|
return new SetTargetTroopRatioExecution(intent.playerID, intent.ratio);
|
|
case "build_unit":
|
|
return new ConstructionExecution(
|
|
intent.playerID,
|
|
this.mg.ref(intent.x, intent.y),
|
|
intent.unit,
|
|
);
|
|
default:
|
|
throw new Error(`intent type ${intent} not found`);
|
|
}
|
|
}
|
|
|
|
spawnBots(numBots: number): Execution[] {
|
|
return new BotSpawner(this.mg, this.gameID)
|
|
.spawnBots(numBots)
|
|
.map((i) => this.createExec(i));
|
|
}
|
|
|
|
fakeHumanExecutions(): Execution[] {
|
|
const execs = [];
|
|
for (const nation of this.mg.nations()) {
|
|
execs.push(
|
|
new FakeHumanExecution(
|
|
this.gameID,
|
|
new PlayerInfo(
|
|
nation.flag || "",
|
|
nation.name,
|
|
PlayerType.FakeHuman,
|
|
null,
|
|
this.random.nextID(),
|
|
nation,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
return execs;
|
|
}
|
|
}
|