From e2ded7501717f37168c80a39f21fe44c84fe7709 Mon Sep 17 00:00:00 2001
From: Dennis Postma <dennis@directonline.io>
Date: Mon, 10 Feb 2025 17:56:38 +0100
Subject: [PATCH] WIP event delays

---
 src/application/base/baseEvent.ts    | 14 +++++++++
 src/events/map/characterMove.ts      | 45 ++++++++++------------------
 src/managers/dateManager.ts          |  2 +-
 src/services/characterMoveService.ts | 19 ++----------
 4 files changed, 33 insertions(+), 47 deletions(-)

diff --git a/src/application/base/baseEvent.ts b/src/application/base/baseEvent.ts
index 892aaae..e54a1f9 100644
--- a/src/application/base/baseEvent.ts
+++ b/src/application/base/baseEvent.ts
@@ -8,12 +8,26 @@ import CharacterRepository from '#repositories/characterRepository'
 
 export abstract class BaseEvent {
   protected readonly logger = Logger.type(LoggerType.GAME)
+  private lastActionTimes: Map<string, number> = new Map()
 
   constructor(
     readonly io: Server,
     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> {
     const characterRepository = new CharacterRepository()
     return characterRepository.getById(this.socket.characterId!)
diff --git a/src/events/map/characterMove.ts b/src/events/map/characterMove.ts
index 42f7c51..efd82cc 100644
--- a/src/events/map/characterMove.ts
+++ b/src/events/map/characterMove.ts
@@ -9,10 +9,7 @@ import TeleportService from '#services/characterTeleportService'
 
 export default class CharacterMove extends BaseEvent {
   private readonly characterService = CharacterService
-  private readonly MOVEMENT_CANCEL_DELAY = 250
-  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
+  private readonly MOVEMENT_THROTTLE = 230 // Minimum time between movement requests
 
   public listen(): void {
     this.socket.on('map:character:move', this.handleEvent.bind(this))
@@ -25,30 +22,22 @@ export default class CharacterMove extends BaseEvent {
       return
     }
 
-    // Implement request throttling
-    const now = Date.now()
-    const lastMove = this.lastMovementTime.get(this.socket.characterId!) || 0
-    if (now - lastMove < this.MOVEMENT_THROTTLE) return
-
-    this.lastMovementTime.set(this.socket.characterId!, now)
-
-    // Clear any existing movement timeout
-    const existingTimeout = this.movementTimeouts.get(this.socket.characterId!)
-    if (existingTimeout) {
-      clearTimeout(existingTimeout)
-      this.movementTimeouts.delete(this.socket.characterId!)
+    if (this.isThrottled(`movement_${this.socket.characterId}`, this.MOVEMENT_THROTTLE)) {
+      // Only cancel current movement if the new target is different
+      if (mapCharacter.isMoving && 
+          (Math.floor(positionX) !== Math.floor(mapCharacter.character.positionX) || 
+           Math.floor(positionY) !== Math.floor(mapCharacter.character.positionY))) {
+        mapCharacter.isMoving = false
+        mapCharacter.currentPath = null
+        // this.finalizeMovement(mapCharacter)
+      }
+      return
     }
 
     // If already moving, cancel current movement
-    if (mapCharacter.isMoving) {
+    if (mapCharacter.isMoving && mapCharacter.currentPath && mapCharacter.currentPath.length > 2) {
       mapCharacter.isMoving = false
       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
@@ -56,12 +45,6 @@ export default class CharacterMove extends BaseEvent {
     const currentY = mapCharacter.character.positionY
     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)
     if (!path?.length) {
       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
         }
 
+        if (i !== 0) {
+          await this.characterService.applyMovementDelay()
+        }
+
         // Validate each step
         if (Math.abs(end.positionX - start.positionX) > 1 || Math.abs(end.positionY - start.positionY) > 1) {
           this.logger.error('Invalid path step detected')
diff --git a/src/managers/dateManager.ts b/src/managers/dateManager.ts
index a4c229b..9892f53 100644
--- a/src/managers/dateManager.ts
+++ b/src/managers/dateManager.ts
@@ -8,7 +8,7 @@ import WorldRepository from '#repositories/worldRepository'
 class DateManager {
   private static readonly CONFIG = {
     GAME_SPEED: 8, // 24 game hours / 3 real hours
-    UPDATE_INTERVAL: 1000, // 1 minute
+    UPDATE_INTERVAL: 1000 // 1 minute
   } as const
 
   private readonly logger = Logger.type(LoggerType.APP)
diff --git a/src/services/characterMoveService.ts b/src/services/characterMoveService.ts
index a444bdc..49dbc8c 100644
--- a/src/services/characterMoveService.ts
+++ b/src/services/characterMoveService.ts
@@ -7,8 +7,7 @@ type Position = { positionX: number; positionY: number }
 export type Node = Position & { parent?: Node; g: number; h: number; f: number }
 
 class CharacterMoveService extends BaseService {
-  private readonly MOVEMENT_DELAY_MS = 200
-  private readonly MAX_PATH_LENGTH = 20 // Limit maximum path length
+  private readonly MOVEMENT_DELAY_MS = 90
 
   private readonly DIRECTIONS = [
     { x: 0, y: -1 }, // Up
@@ -46,21 +45,7 @@ class CharacterMoveService extends BaseService {
       return null
     }
 
-    // Add maximum distance check
-    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
+    return this.findPath(start, end, grid)
   }
 
   public calculateRotation(X1: number, Y1: number, X2: number, Y2: number): number {