can change name after joining game

This commit is contained in:
evanpelle
2024-08-26 15:03:50 -07:00
parent da6f0a89e7
commit 480cfba8e0
13 changed files with 130 additions and 19 deletions
+2 -2
View File
@@ -47,8 +47,8 @@
* improve menu (keep highlighted when click, allow deselect lobby) DONE 8/25/2024
* give time to (re) spawn at start of game DONE 8/25/2024
* show bar for long to respawn DONE 8/26/2024
* store & delay tile updates for lag compensation
* BUG: error if don't spawn and then click after spawn mode
* store & delay tile updates for lag compensation DONE 8/26/2024
* BUG: error if don't spawn and then click after spawn mode DONE 8/26/2024
* BUG: change player name after join lobby
* REFACTOR: use new priority queue
* BUG: players attack each other same time creates islands
+26
View File
@@ -36,6 +36,12 @@ class Client {
setFavicon()
this.terrainMap = loadTerrainMap()
this.startLobbyPolling()
setupUsernameCallback((username) => {
console.log('Username updated:', username);
if (this.game != null) {
this.game.playerName = username
}
});
}
private startLobbyPolling(): void {
@@ -118,6 +124,11 @@ class Client {
g.stop();
});
}
}
function getUsername(): string {
@@ -129,6 +140,21 @@ function getUsername(): string {
return 'Anon'; // Return 'Anon' if the input element is not found
}
function setupUsernameCallback(callback: (username: string) => void): void {
const usernameInput = document.getElementById('username') as HTMLInputElement | null;
if (usernameInput) {
usernameInput.addEventListener('input', () => {
const username = getUsername();
callback(username);
});
} else {
console.error('Username input element not found');
}
}
// Initialize the client when the DOM is loaded
document.addEventListener('DOMContentLoaded', () => {
new Client().initialize();
+13 -3
View File
@@ -1,4 +1,4 @@
import {Executor} from "../core/execution/Executor";
import {Executor} from "../core/execution/ExecutionManager";
import {Cell, MutableGame, PlayerEvent, PlayerID, MutablePlayer, TileEvent, Player, Game, BoatEvent, Tile} from "../core/Game";
import {createGame} from "../core/GameImpl";
import {EventBus} from "../core/EventBus";
@@ -46,7 +46,7 @@ export class ClientGame {
private isProcessingTurn = false
constructor(
private playerName: string,
public playerName: string,
private id: ClientID,
private gameID: GameID,
private eventBus: EventBus,
@@ -86,6 +86,13 @@ export class ClientGame {
if (!this.isActive) {
this.start()
}
this.sendIntent(
{
type: "updateName",
name: this.playerName,
clientID: this.id
}
)
}
if (message.type == "turn") {
this.addTurn(message.turn)
@@ -163,7 +170,7 @@ export class ClientGame {
private playerEvent(event: PlayerEvent) {
console.log('received new player event!')
if (event.player.info().clientID == this.id) {
if (event.player.clientID() == this.id) {
console.log('setting name')
this.myPlayer = event.player
}
@@ -190,6 +197,9 @@ export class ClientGame {
if (this.gs.inSpawnPhase()) {
return
}
if (this.myPlayer == null) {
return
}
const owner = tile.owner()
const targetID = owner.isPlayer() ? owner.id() : null;
+1 -1
View File
@@ -32,7 +32,7 @@ export function placeName(game: Game, player: Player): [position: Cell, fontSize
Math.floor(largestRectangle.y + largestRectangle.height / 2 + boundingBox.min.y),
)
const fontSize = calculateFontSize(largestRectangle, player.info().name);
const fontSize = calculateFontSize(largestRectangle, player.name());
return [center, fontSize]
}
+2 -2
View File
@@ -84,7 +84,7 @@ export class NameRenderer {
isVisible(render: RenderInfo, min: Cell, max: Cell): boolean {
const ratio = (max.x - min.x) / Math.max(20, (render.boundingBox.max.x - render.boundingBox.min.x))
if (render.player.info().isBot) {
if (render.player.isBot()) {
if (ratio > 25) {
return false
}
@@ -121,7 +121,7 @@ export class NameRenderer {
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(render.player.info().name, nameCenterX, nameCenterY - render.fontSize / 2);
context.fillText(render.player.name(), nameCenterX, nameCenterY - render.fontSize / 2);
context.font = `bold ${render.fontSize}px ${this.theme.font()}`;
let troopsStr: string = ""
let troops = render.player.troops() / 10
+4 -1
View File
@@ -82,8 +82,10 @@ export interface TerraNullius {
}
export interface Player {
info(): PlayerInfo
name(): string
clientID(): ClientID
id(): PlayerID
isBot(): boolean
troops(): number
boats(): Boat[]
ownsTile(cell: Cell): boolean
@@ -99,6 +101,7 @@ export interface Player {
}
export interface MutablePlayer extends Player {
setName(name: string): void
setTroops(troops: number): void
addTroops(troops: number): void
removeTroops(troops: number): void
+25 -2
View File
@@ -1,6 +1,7 @@
import {Config} from "./configuration/Config";
import {EventBus} from "./EventBus";
import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, MutableBoat, BoatEvent} from "./Game";
import {ClientID} from "./Schemas";
import {Terrain, TerrainMap, TerrainType} from "./TerrainMapLoader";
import {simpleHash} from "./Util";
@@ -123,7 +124,30 @@ export class PlayerImpl implements MutablePlayer {
public _boats: BoatImpl[] = []
public _tiles: Map<CellString, Tile> = new Map<CellString, Tile>()
constructor(private gs: GameImpl, public readonly playerInfo: PlayerInfo, private _troops) {
private _name: string
constructor(private gs: GameImpl, private readonly playerInfo: PlayerInfo, private _troops) {
this._name = playerInfo.name
}
name(): string {
return this._name
}
clientID(): ClientID {
return this.playerInfo.clientID
}
id(): PlayerID {
return this.playerInfo.id
}
isBot(): boolean {
return this.playerInfo.isBot
}
setName(name: string) {
}
addBoat(troops: number, tile: Tile, target: Player | TerraNullius): BoatImpl {
@@ -190,7 +214,6 @@ export class PlayerImpl implements MutablePlayer {
this.gs.relinquish(tile)
}
info(): PlayerInfo {return this.playerInfo}
id(): PlayerID {return this.playerInfo.id}
troops(): number {return this._troops}
isAlive(): boolean {return this._tiles.size > 0}
gameState(): MutableGame {return this.gs}
+10 -3
View File
@@ -3,11 +3,13 @@ import {z} from 'zod';
export type GameID = string
export type ClientID = string
export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent
export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent | UpdateNameIntent
export type AttackIntent = z.infer<typeof AttackIntentSchema>
export type SpawnIntent = z.infer<typeof SpawnIntentSchema>
export type BoatAttackIntent = z.infer<typeof BoatAttackIntentSchema>
export type UpdateNameIntent = z.infer<typeof UpdateNameIntentSchema>
export type Turn = z.infer<typeof TurnSchema>
@@ -31,7 +33,7 @@ export interface Lobby {
// Zod schemas
const BaseIntentSchema = z.object({
type: z.enum(['attack', 'spawn', 'boat']),
type: z.enum(['attack', 'spawn', 'boat', 'name']),
clientID: z.string(),
});
@@ -62,7 +64,12 @@ export const BoatAttackIntentSchema = BaseIntentSchema.extend({
y: z.number(),
})
const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema]);
export const UpdateNameIntentSchema = BaseIntentSchema.extend({
type: z.literal('updateName'),
name: z.string(),
})
const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema, UpdateNameIntentSchema]);
const TurnSchema = z.object({
turnNumber: z.number(),
+1 -1
View File
@@ -56,7 +56,7 @@ export class DefaultPlayerConfig implements PlayerConfig {
}
attackAmount(attacker: Player, defender: Player | TerraNullius) {
if (attacker.info().isBot) {
if (attacker.isBot()) {
return attacker.troops() / 20
} else {
return attacker.troops() / 5
+2 -2
View File
@@ -7,10 +7,10 @@ export const devConfig = new class extends DefaultConfig {
return 40
}
gameCreationRate(): number {
return 3 * 1000
return 10 * 1000
}
lobbyLifetime(): number {
return 3 * 1000
return 10 * 1000
}
turnIntervalMs(): number {
return 100
@@ -6,6 +6,7 @@ import {SpawnExecution} from "./SpawnExecution";
import {BotSpawner} from "./BotSpawner";
import {BoatAttackExecution} from "./BoatAttackExecution";
import {PseudoRandom} from "../PseudoRandom";
import {UpdateNameExecution} from "./UpdateNameExecution";
export class Executor {
@@ -40,6 +41,11 @@ export class Executor {
new Cell(intent.x, intent.y),
intent.troops
)
} else if (intent.type == "updateName") {
return new UpdateNameExecution(
intent.name,
intent.clientID
)
} else {
throw new Error(`intent type ${intent} not found`)
}
+2 -2
View File
@@ -28,7 +28,7 @@ export class SpawnExecution implements Execution {
return
}
const existing = this.mg.players().find(p => p.info().clientID != null && p.info().clientID == this.playerInfo.clientID)
const existing = this.mg.players().find(p => p.clientID() != null && p.clientID() == this.playerInfo.clientID)
if (existing) {
existing.tiles().forEach(t => existing.relinquish(t))
getSpawnCells(this.mg, this.cell).forEach(c => {
@@ -42,7 +42,7 @@ export class SpawnExecution implements Execution {
player.conquer(this.mg.tile(c))
})
this.mg.addExecution(new PlayerExecution(player.id()))
if (player.info().isBot) {
if (player.isBot()) {
this.mg.addExecution(new BotExecution(player))
}
this.active = false
+36
View File
@@ -0,0 +1,36 @@
import {Config, PlayerConfig} from "../configuration/Config"
import {Execution, MutableGame, MutablePlayer, PlayerID} from "../Game"
import {ClientID} from "../Schemas"
export class UpdateNameExecution implements Execution {
private active = true
private mg: MutableGame
constructor(private newName: string, private clientID: ClientID) {
}
init(mg: MutableGame, ticks: number) {
this.mg = mg
}
tick(ticks: number) {
const player = this.mg.players().find(p => p.clientID() == this.clientID)
if (player == null) {
return
}
player.setName(this.newName)
this.active = false
}
owner(): MutablePlayer {
return null
}
isActive(): boolean {
return this.active
}
activeDuringSpawnPhase(): boolean {
return true
}
}