WIP event delays

This commit is contained in:
Dennis Postma 2025-02-10 17:56:38 +01:00
parent 0cead14e71
commit e2ded75017
4 changed files with 33 additions and 47 deletions

View File

@ -8,12 +8,26 @@ import CharacterRepository from '#repositories/characterRepository'
export abstract class BaseEvent { export abstract class BaseEvent {
protected readonly logger = Logger.type(LoggerType.GAME) protected readonly logger = Logger.type(LoggerType.GAME)
private lastActionTimes: Map<string, number> = new Map()
constructor( constructor(
readonly io: Server, readonly io: Server,
readonly socket: TSocket readonly socket: TSocket
) {} ) {}
protected isThrottled(actionId: string, throttleTime: number): boolean {
const now = Date.now()
const lastActionTime = this.lastActionTimes.get(actionId) || 0
if (now - lastActionTime < throttleTime) {
return true
}
this.lastActionTimes.set(actionId, now)
return false
}
protected async getCharacter(): Promise<Character | null> { protected async getCharacter(): Promise<Character | null> {
const characterRepository = new CharacterRepository() const characterRepository = new CharacterRepository()
return characterRepository.getById(this.socket.characterId!) return characterRepository.getById(this.socket.characterId!)

View File

@ -9,10 +9,7 @@ import TeleportService from '#services/characterTeleportService'
export default class CharacterMove extends BaseEvent { export default class CharacterMove extends BaseEvent {
private readonly characterService = CharacterService private readonly characterService = CharacterService
private readonly MOVEMENT_CANCEL_DELAY = 250 private readonly MOVEMENT_THROTTLE = 230 // Minimum time between movement requests
private readonly MOVEMENT_THROTTLE = 80 // Minimum time between movement requests
private movementTimeouts: Map<string, NodeJS.Timeout> = new Map()
private lastMovementTime: Map<string, number> = new Map() // Track last movement time for each character
public listen(): void { public listen(): void {
this.socket.on('map:character:move', this.handleEvent.bind(this)) this.socket.on('map:character:move', this.handleEvent.bind(this))
@ -25,30 +22,22 @@ export default class CharacterMove extends BaseEvent {
return return
} }
// Implement request throttling if (this.isThrottled(`movement_${this.socket.characterId}`, this.MOVEMENT_THROTTLE)) {
const now = Date.now() // Only cancel current movement if the new target is different
const lastMove = this.lastMovementTime.get(this.socket.characterId!) || 0 if (mapCharacter.isMoving &&
if (now - lastMove < this.MOVEMENT_THROTTLE) return (Math.floor(positionX) !== Math.floor(mapCharacter.character.positionX) ||
Math.floor(positionY) !== Math.floor(mapCharacter.character.positionY))) {
this.lastMovementTime.set(this.socket.characterId!, now) mapCharacter.isMoving = false
mapCharacter.currentPath = null
// Clear any existing movement timeout // this.finalizeMovement(mapCharacter)
const existingTimeout = this.movementTimeouts.get(this.socket.characterId!) }
if (existingTimeout) { return
clearTimeout(existingTimeout)
this.movementTimeouts.delete(this.socket.characterId!)
} }
// If already moving, cancel current movement // If already moving, cancel current movement
if (mapCharacter.isMoving) { if (mapCharacter.isMoving && mapCharacter.currentPath && mapCharacter.currentPath.length > 2) {
mapCharacter.isMoving = false mapCharacter.isMoving = false
mapCharacter.currentPath = null mapCharacter.currentPath = null
// Add small delay before starting new movement
await new Promise((resolve) => {
const timeout = setTimeout(resolve, this.MOVEMENT_CANCEL_DELAY)
this.movementTimeouts.set(this.socket.characterId!, timeout)
})
} }
// Validate target position is within reasonable range // Validate target position is within reasonable range
@ -56,12 +45,6 @@ export default class CharacterMove extends BaseEvent {
const currentY = mapCharacter.character.positionY const currentY = mapCharacter.character.positionY
const distance = Math.sqrt(Math.pow(positionX - currentX, 2) + Math.pow(positionY - currentY, 2)) const distance = Math.sqrt(Math.pow(positionX - currentX, 2) + Math.pow(positionY - currentY, 2))
if (distance > 20) {
// Maximum allowed distance
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'Target position too far')
return
}
const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY) const path = await this.characterService.calculatePath(mapCharacter.character, positionX, positionY)
if (!path?.length) { if (!path?.length) {
this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'No valid path found') this.io.in(mapCharacter.character.map.id).emit('map:character:moveError', 'No valid path found')
@ -90,6 +73,10 @@ export default class CharacterMove extends BaseEvent {
break break
} }
if (i !== 0) {
await this.characterService.applyMovementDelay()
}
// Validate each step // Validate each step
if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) { if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) {
this.logger.error('Invalid path step detected') this.logger.error('Invalid path step detected')

View File

@ -8,7 +8,7 @@ import WorldRepository from '#repositories/worldRepository'
class DateManager { class DateManager {
private static readonly CONFIG = { private static readonly CONFIG = {
GAME_SPEED: 8, // 24 game hours / 3 real hours GAME_SPEED: 8, // 24 game hours / 3 real hours
UPDATE_INTERVAL: 1000, // 1 minute UPDATE_INTERVAL: 1000 // 1 minute
} as const } as const
private readonly logger = Logger.type(LoggerType.APP) private readonly logger = Logger.type(LoggerType.APP)

View File

@ -7,8 +7,7 @@ type Position = { positionX: number; positionY: number }
export type Node = Position & { parent?: Node; g: number; h: number; f: number } export type Node = Position & { parent?: Node; g: number; h: number; f: number }
class CharacterMoveService extends BaseService { class CharacterMoveService extends BaseService {
private readonly MOVEMENT_DELAY_MS = 200 private readonly MOVEMENT_DELAY_MS = 90
private readonly MAX_PATH_LENGTH = 20 // Limit maximum path length
private readonly DIRECTIONS = [ private readonly DIRECTIONS = [
{ x: 0, y: -1 }, // Up { x: 0, y: -1 }, // Up
@ -46,21 +45,7 @@ class CharacterMoveService extends BaseService {
return null return null
} }
// Add maximum distance check return this.findPath(start, end, grid)
const directDistance = Math.sqrt(Math.pow(targetX - character.positionX, 2) + Math.pow(targetY - character.positionY, 2))
if (directDistance > this.MAX_PATH_LENGTH) {
return null
}
const path = this.findPath(start, end, grid)
// Validate path length
if (path.length > this.MAX_PATH_LENGTH) {
return path.slice(0, this.MAX_PATH_LENGTH)
}
return path
} }
public calculateRotation(X1: number, Y1: number, X2: number, Y2: number): number { public calculateRotation(X1: number, Y1: number, X2: number, Y2: number): number {