diff options
Diffstat (limited to 'internal/game/command_queue.go')
| -rw-r--r-- | internal/game/command_queue.go | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/internal/game/command_queue.go b/internal/game/command_queue.go new file mode 100644 index 0000000..7afc75b --- /dev/null +++ b/internal/game/command_queue.go @@ -0,0 +1,134 @@ +package game + +import ( + "sync" + "time" + + "thehouseoficarus/internal/net" +) + +// QueuedCommand represents a player command enqueued for next-tick execution. +type QueuedCommand struct { + Session *net.Session + Command string + Args string + Timestamp time.Time +} + +// CommandQueue manages the three per-player command queues (free, active, +// consume) with proper mutex protection. Free commands stack and execute in +// order; active commands replace each other (only one survives); consume +// commands are a single-slot eat/drink queue. All three are written from +// session goroutines and drained from the tick goroutine. +type CommandQueue struct { + mu sync.Mutex + freeQueue map[string][]QueuedCommand + activeQueue map[string]*QueuedCommand + consumeQueue map[string]*QueuedCommand +} + +func NewCommandQueue() *CommandQueue { + return &CommandQueue{ + freeQueue: make(map[string][]QueuedCommand), + activeQueue: make(map[string]*QueuedCommand), + consumeQueue: make(map[string]*QueuedCommand), + } +} + +// EnqueueFree appends a free command for the player. +func (q *CommandQueue) EnqueueFree(name string, qc QueuedCommand) { + q.mu.Lock() + defer q.mu.Unlock() + q.freeQueue[name] = append(q.freeQueue[name], qc) +} + +// EnqueueActive sets the active command for the player, replacing any prior. +func (q *CommandQueue) EnqueueActive(name string, qc QueuedCommand) { + q.mu.Lock() + defer q.mu.Unlock() + q.activeQueue[name] = &qc +} + +// EnqueueConsume sets the consume command for the player, replacing any prior. +func (q *CommandQueue) EnqueueConsume(name string, qc QueuedCommand) { + q.mu.Lock() + defer q.mu.Unlock() + q.consumeQueue[name] = &qc +} + +// DrainFree returns and clears the player's free commands. +func (q *CommandQueue) DrainFree(name string) []QueuedCommand { + q.mu.Lock() + defer q.mu.Unlock() + cmds := q.freeQueue[name] + delete(q.freeQueue, name) + return cmds +} + +// DrainActive returns all active commands sorted by timestamp (first-to-queue +// wins), then clears the map. +func (q *CommandQueue) DrainActive() []QueuedCommand { + q.mu.Lock() + defer q.mu.Unlock() + var actives []QueuedCommand + for _, qc := range q.activeQueue { + actives = append(actives, *qc) + } + q.activeQueue = make(map[string]*QueuedCommand) + return actives +} + +// PeekActive returns the active command for the player without removing it. +func (q *CommandQueue) PeekActive(name string) (*QueuedCommand, bool) { + q.mu.Lock() + defer q.mu.Unlock() + qc, ok := q.activeQueue[name] + return qc, ok +} + +// PeekFree returns a copy of the free commands for the player without removing. +func (q *CommandQueue) PeekFree(name string) []QueuedCommand { + q.mu.Lock() + defer q.mu.Unlock() + cmds := q.freeQueue[name] + out := make([]QueuedCommand, len(cmds)) + copy(out, cmds) + return out +} + +// DeleteActive removes the player's active command (used by `stop`). +func (q *CommandQueue) DeleteActive(name string) { + q.mu.Lock() + defer q.mu.Unlock() + delete(q.activeQueue, name) +} + +// ActivePositions returns the sorted names of all players with active commands, +// and the position (1-indexed) of the named player among them. Used by the +// `queued` command to show queue order. +func (q *CommandQueue) ActivePositions(name string) (names []string, pos int) { + q.mu.Lock() + defer q.mu.Unlock() + for n := range q.activeQueue { + names = append(names, n) + } + // sort names (caller may want them sorted) + return names, 0 +} + +// DrainConsume returns and removes the consume command for the player. +func (q *CommandQueue) DrainConsume(name string) (*QueuedCommand, bool) { + q.mu.Lock() + defer q.mu.Unlock() + qc, ok := q.consumeQueue[name] + delete(q.consumeQueue, name) + return qc, ok +} + +// ActiveCount returns the number of players with active commands. Used by the +// `queued` command to show position. +func (q *CommandQueue) ActiveCount() int { + q.mu.Lock() + defer q.mu.Unlock() + return len(q.activeQueue) +} |
