Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
platz1de committed Nov 15, 2024
2 parents 4ac09e1 + 176b2b1 commit ee84774
Show file tree
Hide file tree
Showing 22 changed files with 319 additions and 203 deletions.
6 changes: 3 additions & 3 deletions src/Loader.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {handlePath} from "./util/PathHandler";
import {gameRenderer} from "./renderer/GameRenderer";

window.addEventListener("load", () => {
handlePath();
});

import("./renderer/GameRenderer");
gameRenderer.startRendering();
});
87 changes: 87 additions & 0 deletions src/game/BorderManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {gameMap} from "./GameData";
import {onNeighbors} from "../util/MathUtil";
import {territoryManager} from "./TerritoryManager";
import {playerNameRenderingManager, PlayerNameUpdate} from "../renderer/manager/PlayerNameRenderingManager";

class BorderManager {
private tileGrades: Uint8Array;
private borderTiles: Set<number>[];

/**
* Resets the border manager.
* Should only be called when a new game is started.
* @param playerCount The number of players in the game
* @internal
*/
reset(playerCount: number): void {
this.tileGrades = new Uint8Array(gameMap.width * gameMap.height);
this.borderTiles = new Array(playerCount).fill(null).map(() => new Set());
}

/**
* Checks for updated borders when claiming tiles.
* @param tiles The tiles that were claimed.
* @param attacker The player that claimed the tiles.
* @param defender The player that lost the tiles.
*/
transitionTiles(tiles: Set<number>, attacker: number, defender: number): BorderTransitionResult {
const attackerBorder = this.borderTiles[attacker];
const defenderBorder = this.borderTiles[defender] || new Set();
const attackerName = new PlayerNameUpdate(attacker, false);
const defenderName = new PlayerNameUpdate(defender, true);
const result: BorderTransitionResult = {territory: [], attacker: [], defender: []};
for (const tile of tiles) {
let grade = 0;
onNeighbors(tile, (neighbor) => {
const owner = territoryManager.getOwner(neighbor);
if (owner === defender) {
//We don't need to fear handling conquered tiles here, as they should already have the attacker as owner
if (this.tileGrades[neighbor]-- === 4) {
defenderBorder.add(neighbor);
playerNameRenderingManager.removeTile(neighbor, defenderName);
result.defender.push(neighbor);
}
} else if (owner === attacker) {
grade++;
if (!tiles.has(neighbor) && ++this.tileGrades[neighbor] === 4) {
attackerBorder.delete(neighbor);
playerNameRenderingManager.addTile(neighbor, attackerName);
result.territory.push(neighbor);
}
}
});

defenderBorder.delete(tile);
this.tileGrades[tile] = grade;
if (grade < 4) {
attackerBorder.add(tile);
playerNameRenderingManager.removeTile(tile, defenderName);
result.attacker.push(tile);
} else {
playerNameRenderingManager.addTile(tile, attackerName);
result.territory.push(tile);
}
}

attackerName.update();
defenderName.update();

return result;
}

/**
* Gets the border tiles of a player.
* @param player The player to get the border tiles of
* @returns The border tiles of the player
*/
getBorderTiles(player: number): Set<number> {
return this.borderTiles[player];
}
}

export const borderManager = new BorderManager();
export type BorderTransitionResult = {
territory: number[]; // Tiles that changed to attacker territory
attacker: number[]; // Tiles that changed to attacker border
defender: number[]; // Tiles that changed to defender border
};
4 changes: 4 additions & 0 deletions src/game/Game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {GameTickPacket} from "../network/protocol/packet/game/GameTickPacket";
import {hideAllUIElements, showUIElement} from "../ui/UIManager";
import {ClientPlayer} from "./player/ClientPlayer";
import {EventHandlerRegistry} from "../event/EventHandlerRegistry";
import {borderManager} from "./BorderManager";

/**
* Start a new game with the given map.
Expand All @@ -33,9 +34,11 @@ import {EventHandlerRegistry} from "../event/EventHandlerRegistry";
*/
export function startGame(map: GameMap, mode: GameMode, seed: number, players: { name: string }[], clientId: number, isLocal: boolean) {
initGameData(map, mode, isLocal);
gameLoadRegistry.broadcast();
mapNavigationHandler.enable();
mapActionHandler.enable();
territoryManager.reset();
borderManager.reset(500);
boatManager.reset();
playerNameRenderingManager.reset(500);
attackActionHandler.init(500);
Expand Down Expand Up @@ -73,4 +76,5 @@ packetRegistry.handle(GameTickPacket, function () {
}
});

export const gameLoadRegistry = new EventHandlerRegistry<[]>();
export const gameStartRegistry = new EventHandlerRegistry<[]>();
5 changes: 4 additions & 1 deletion src/game/GameTicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {EventHandlerRegistry} from "../event/EventHandlerRegistry";
import {GameTickPacket} from "../network/protocol/packet/game/GameTickPacket";
import {packetRegistry} from "../network/NetworkManager";
import {isLocalGame} from "./GameData";
import {doPacketValidation} from "../network/PacketValidator";

class GameTicker {
private readonly TICK_INTERVAL = 1000 / 20; // 50ms
Expand Down Expand Up @@ -69,7 +70,9 @@ class GameTicker {
return;
}
for (const action of packet.packets) {
packetRegistry.getPacketHandler(action.id).call(action);
if (doPacketValidation(action)) {
packetRegistry.getPacketHandler(action.id).call(action);
}
}
}
this.registry.broadcast();
Expand Down
9 changes: 5 additions & 4 deletions src/game/TerritoryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ class TerritoryManager {
const previousOwner = this.tileOwners[tile];
this.tileOwners[tile] = owner;
if (previousOwner !== this.OWNER_NONE) {
playerManager.getPlayer(previousOwner).removeTile(tile, transaction);
playerManager.getPlayer(previousOwner).removeTile(tile);
}
playerManager.getPlayer(owner).addTile(tile, transaction);
playerManager.getPlayer(owner).addTile(tile);
transaction.addTile(tile);
}

/**
Expand All @@ -110,8 +111,8 @@ class TerritoryManager {
const owner = this.tileOwners[tile];
if (owner !== this.OWNER_NONE) {
this.tileOwners[tile] = this.OWNER_NONE;
playerManager.getPlayer(owner).removeTile(tile, transaction);
transaction.setTerritory(tile);
playerManager.getPlayer(owner).removeTile(tile);
transaction.addTile(tile);
}
}
}
Expand Down
27 changes: 11 additions & 16 deletions src/game/attack/AttackActionValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {AttackActionPacket} from "../../network/protocol/packet/game/AttackActio
import {attackActionHandler} from "./AttackActionHandler";
import {packetRegistry, submitGameAction} from "../../network/NetworkManager";
import {gameMap, gameMode, isLocalGame} from "../GameData";
import {borderManager} from "../BorderManager";
import {validatePacket} from "../../network/PacketValidator";

/**
* Filters out invalid attacks and submits the attack action.
Expand All @@ -21,7 +23,7 @@ export function preprocessAttack(attacker: number, target: number, power: number
}

export function hasBorderWith(player: Player, target: number): boolean {
for (const tile of player.borderTiles) {
for (const tile of borderManager.getBorderTiles(player.id)) {
const x = tile % gameMap.width;
const y = Math.floor(tile / gameMap.width);
if (x > 0 && territoryManager.isOwner(tile - 1, target)) {
Expand All @@ -40,23 +42,16 @@ export function hasBorderWith(player: Player, target: number): boolean {
return false;
}

validatePacket(AttackActionPacket, packet => {
//TODO: This should be used by all attack related methods
const realTarget = packet.attacker === packet.target ? territoryManager.OWNER_NONE : packet.target;
return playerManager.validatePlayer(packet.attacker) && playerManager.validatePlayer(packet.target)
&& gameMode.canAttack(packet.attacker, realTarget);
});

//TODO: clean this up, starting pixels should be calculated here and not in the handler itself
packetRegistry.handle(AttackActionPacket, function (): void {
const target = this.target === this.attacker ? territoryManager.OWNER_NONE : this.target;
if (!gameMode.canAttack(this.attacker, target)) {
return;
}

//TODO: Move these into a general validation function
const attacker = playerManager.getPlayer(this.attacker);
if (!attacker || !attacker.isAlive()) {
return; // invalid origin player
}
if (target !== territoryManager.OWNER_NONE && !(playerManager.getPlayer(target) && playerManager.getPlayer(target).isAlive())) {
return; // invalid target player
}

actuallyHandleAttack(attacker, target, this.power);
actuallyHandleAttack(playerManager.getPlayer(this.attacker), this.attacker === this.target ? territoryManager.OWNER_NONE : this.target, this.power);
});

/**
Expand Down
3 changes: 2 additions & 1 deletion src/game/attack/AttackExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {random} from "../Random";
import {attackActionHandler} from "./AttackActionHandler";
import {gameMap} from "../GameData";
import {TerritoryTransaction} from "../transaction/TerritoryTransaction";
import {borderManager} from "../BorderManager";

/**
* This is the max amount of ticks it can take to conquer a tile.
Expand Down Expand Up @@ -148,7 +149,7 @@ export class AttackExecutor {

const result = [];
const amountCache = attackActionHandler.amountCache;
for (const tile of borderTiles ?? this.player.borderTiles) {
for (const tile of borderTiles ?? borderManager.getBorderTiles(this.player.id)) {
const x = tile % gameMap.width;
const y = Math.floor(tile / gameMap.width);
if (x > 0 && tileOwners[tile - 1] === target) {
Expand Down
33 changes: 9 additions & 24 deletions src/game/boat/BoatManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {gameTicker} from "../GameTicker";
import {BoatActionPacket} from "../../network/protocol/packet/game/BoatActionPacket";
import {packetRegistry, submitGameAction} from "../../network/NetworkManager";
import {Player} from "../player/Player";
import {onNeighbors} from "../../util/MathUtil";
import {territoryManager} from "../TerritoryManager";
import {bordersTile} from "../../util/MathUtil";
import {gameMap} from "../GameData";
import {validatePacket} from "../../network/PacketValidator";

class BoatManager {
private readonly boats: Boat[] = [];
Expand Down Expand Up @@ -122,27 +122,12 @@ export const boatManager = new BoatManager();

gameTicker.registry.register(boatManager.tick);

packetRegistry.handle(BoatActionPacket, function (this: BoatActionPacket): void {
const player = playerManager.getPlayer(this.player);
if (!player || !player.isAlive()) {
return;
}
if (this.start >= gameMap.width * gameMap.height || this.end >= gameMap.width * gameMap.height) {
return;
}
if (gameMap.getDistance(this.start) !== -1 || gameMap.getDistance(this.end) !== 0) {
return;
}
validatePacket(BoatActionPacket, packet => {
return playerManager.validatePlayer(packet.player)
&& gameMap.getDistance(packet.start) === -1 && gameMap.getDistance(packet.end) === 0
&& bordersTile(packet.start, packet.player);
});

let hasBorder = false;
onNeighbors(this.start, neighbor => {
if (territoryManager.isOwner(neighbor, player.id)) {
hasBorder = true;
}
});
if (!hasBorder) {
return;
}

boatManager.addBoat(player, this.start, this.end, this.power);
packetRegistry.handle(BoatActionPacket, function (this: BoatActionPacket): void {
boatManager.addBoat(playerManager.getPlayer(this.player), this.start, this.end, this.power);
});
3 changes: 2 additions & 1 deletion src/game/bot/BotPlayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {onNeighbors} from "../../util/MathUtil";
import {rayTraceWater} from "../../util/VoxelRayTrace";
import {gameMap, gameMode} from "../GameData";
import {boatManager} from "../boat/BoatManager";
import {borderManager} from "../BorderManager";

export class BotPlayer extends Player {
protected readonly triggers: BotTrigger[];
Expand All @@ -32,7 +33,7 @@ export class BotPlayer extends Player {
//TODO: Attack percentage should be configurable
actuallyHandleAttack(this, target, 100);
} else if (this.waterTiles > 0) {
const borderTiles = Array.from(this.borderTiles); //TODO: Check the performance hit this causes
const borderTiles = Array.from(borderManager.getBorderTiles(this.id)); //TODO: Check the performance hit this causes
const startTile = borderTiles[random.nextInt(borderTiles.length)];
const possibleStarts: number[] = [];
onNeighbors(startTile, tile => {
Expand Down
3 changes: 2 additions & 1 deletion src/game/bot/BotStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {territoryManager} from "../TerritoryManager";
import {gameMode} from "../GameData";
import {BotPlayer} from "./BotPlayer";
import {playerManager} from "../player/PlayerManager";
import {borderManager} from "../BorderManager";

export class BotStrategy {
constructor(
Expand All @@ -19,7 +20,7 @@ export class BotStrategy {
getTarget(player: BotPlayer): number | null {
//TODO: Neighbor logic should probably be done using tile updates instead of every tick
let targets: number[] = [];
for (const border of player.borderTiles) {
for (const border of borderManager.getBorderTiles(player.id)) {
onNeighbors(border, neighbor => {
const owner = territoryManager.getOwner(neighbor);
if (owner !== player.id && !targets.includes(owner)) {
Expand Down
9 changes: 4 additions & 5 deletions src/game/player/ClientPlayer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Player} from "./Player";
import {TerritoryTransaction} from "../transaction/TerritoryTransaction";
import {onNeighbors} from "../../util/MathUtil";
import {HSLColor} from "../../util/HSLColor";
import {areaCalculator} from "../../map/area/AreaCalculator";
Expand All @@ -14,8 +13,8 @@ export class ClientPlayer extends Player {
areaIndex = new Uint16Array(areaCalculator.preprocessMap());
}

addTile(tile: number, transaction: TerritoryTransaction) {
super.addTile(tile, transaction);
addTile(tile: number) {
super.addTile(tile);

onNeighbors(tile, neighbor => {
if (territoryManager.isWater(neighbor)) {
Expand All @@ -24,8 +23,8 @@ export class ClientPlayer extends Player {
});
}

removeTile(tile: number, transaction: TerritoryTransaction) {
super.removeTile(tile, transaction);
removeTile(tile: number) {
super.removeTile(tile);

onNeighbors(tile, neighbor => {
if (territoryManager.isWater(neighbor)) {
Expand Down
Loading

0 comments on commit ee84774

Please sign in to comment.