export const construct = (redisCnx) => { const tickMs = redisCnx.gpsSrv?.getGpsSettings().collisionTickMs ?? 100 // Interval always runs; tickArena no-ops until LIVE (see gpsServer.tickArena) setInterval(() => { redisCnx.gpsSrv?.tickArena() }, tickMs) } export const methods = { handleLifecycleEvent(msg) { const srv = this.gpsSrv if(!srv) return if(msg.eventType === 'onYourMarks') { srv.onYourMarks(msg.payload ?? {}).catch(err => { console.error(`[${this.redisId}] onYourMarks failed:`, err) srv.publishReadyToStart({ success: false, err: err.message ?? 'onYourMarks failed' }) }) return } if(msg.eventType === 'bigBang') { srv.onBigBang(msg.payload ?? {}) return } }, handleAgentEvent(msg) { const agentId = msg.sender if(!agentId || typeof(agentId) !== 'string') { console.warn(`[${this.redisId}] Agent event without sender`) return } if(msg.eventType === 'change') { const newVector = msg.payload?.newVector if(!newVector || typeof(newVector.x) !== 'number' || typeof(newVector.y) !== 'number' || typeof(newVector.z) !== 'number') { console.warn(`[${this.redisId}] Invalid newVector from ${agentId}`) return } const newPosition = msg.payload?.newPosition ?? null this.gpsSrv.onVectorChange(agentId, newVector, newPosition) return } if(msg.eventType === 'remove') { this.gpsSrv.onAgentRemove(agentId) return } }, dispatchArenaMessage(msg, chan) { const gps = this.config.gps if(!gps || !this.gpsSrv) return(false) if(this.matchesChan(chan, gps.lifecycle?.arenaChannel ?? 'arena:lifecycle')) { this.handleLifecycleEvent(msg) return(true) } if(this.matchesChan(chan, gps.agentVectorChangeChannel)) { this.handleAgentEvent(msg) return(true) } return(false) }, }