Monitoring client connections (#941)

## Description:
Disconnected client detection :
If a client haven't send a ping to the server since more than 30 seconds
They will then be marked disconnected with a dedicated icon.
No action are yet taken, this allows for extensive in-game test before
adding the *consequences* of the player leaving the game.
I also added extensive unit tests, lessening the risk of regression for
the future.


![image](https://github.com/user-attachments/assets/884e5e99-15e8-4544-bd52-7524542cc82a)



## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I process any text displayed to the user through translateText()
and I've added it to the en.json file
- [x] I have added relevant tests to the test directory
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
theodoreleon.aetarax

---------

Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
Théodore Léon
2025-06-06 13:30:23 -07:00
committed by GitHub
co-authored by evanpelle
parent 02f7caef88
commit a4fffce7f3
12 changed files with 287 additions and 2 deletions
+3
View File
@@ -433,6 +433,9 @@ export interface Player {
largestClusterBoundingBox: { min: Cell; max: Cell } | null;
lastTileChange(): Tick;
isDisconnected(): boolean;
markDisconnected(isDisconnected: boolean): void;
hasSpawned(): boolean;
setHasSpawned(hasSpawned: boolean): void;
+1
View File
@@ -103,6 +103,7 @@ export interface PlayerUpdate {
smallID: number;
playerType: PlayerType;
isAlive: boolean;
isDisconnected: boolean;
tilesOwned: number;
gold: Gold;
population: number;
+3
View File
@@ -289,6 +289,9 @@ export class PlayerView {
hasSpawned(): boolean {
return this.data.hasSpawned;
}
isDisconnected(): boolean {
return this.data.isDisconnected;
}
}
export class GameView implements GameMap {
+10
View File
@@ -98,6 +98,7 @@ export class PlayerImpl implements Player {
public _outgoingLandAttacks: Attack[] = [];
private _hasSpawned = false;
private _isDisconnected = false;
constructor(
private mg: GameImpl,
@@ -135,6 +136,7 @@ export class PlayerImpl implements Player {
smallID: this.smallID(),
playerType: this.type(),
isAlive: this.isAlive(),
isDisconnected: this.isDisconnected(),
tilesOwned: this.numTilesOwned(),
gold: this._gold,
population: this.population(),
@@ -919,6 +921,14 @@ export class PlayerImpl implements Player {
return this._lastTileChange;
}
isDisconnected(): boolean {
return this._isDisconnected;
}
markDisconnected(isDisconnected: boolean): void {
this._isDisconnected = isDisconnected;
}
hash(): number {
return (
simpleHash(this.id()) * (this.population() + this.numTilesOwned()) +