aboutsummaryrefslogtreecommitdiff
path: root/internal/game/core_command_queue.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 05:31:09 -0400
committerhistoria <[not public]>2026-09-18 05:31:09 -0400
commit153acb7f644d755c45533b7c5311e3ffa47e8973 (patch)
tree7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/game/core_command_queue.go
parent87d08f0151e643951769e323995f1c5ed89ee200 (diff)
downloadthehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/game/core_command_queue.go')
-rw-r--r--internal/game/core_command_queue.go37
1 files changed, 29 insertions, 8 deletions
diff --git a/internal/game/core_command_queue.go b/internal/game/core_command_queue.go
index 7afc75b..28b7a36 100644
--- a/internal/game/core_command_queue.go
+++ b/internal/game/core_command_queue.go
@@ -1,6 +1,7 @@
package game
import (
+ "sort"
"sync"
"time"
@@ -18,8 +19,10 @@ type QueuedCommand struct {
// 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.
+// commands are a single-slot eat/drink queue. Writers are the command
+// handlers (owner goroutine); the consumer is ProcessQueuedCommands on the
+// owner tick. The mutex is kept as a cheap guard for callers that reach a
+// queue from outside that single-owner path.
type CommandQueue struct {
mu sync.Mutex
freeQueue map[string][]QueuedCommand
@@ -65,8 +68,8 @@ func (q *CommandQueue) DrainFree(name string) []QueuedCommand {
return cmds
}
-// DrainActive returns all active commands sorted by timestamp (first-to-queue
-// wins), then clears the map.
+// DrainActive returns all active commands, then clears the map. It does not
+// order them: ProcessQueuedCommands sorts by Timestamp (first-to-queue wins).
func (q *CommandQueue) DrainActive() []QueuedCommand {
q.mu.Lock()
defer q.mu.Unlock()
@@ -103,19 +106,37 @@ func (q *CommandQueue) DeleteActive(name string) {
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.
+// ActivePositions returns the sorted names of every player holding an active
+// command, and the position (1-indexed) of the named player among them, or 0
+// when that player has no active command. 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)
+ sort.Strings(names)
+ for i, n := range names {
+ if n == name {
+ return names, i + 1
+ }
+ }
return names, 0
}
+// ClearPlayer drops every queued command for the player: free, active, and
+// consume. Called when a session detaches (logout, quit, disconnect, or
+// character removal) so leftovers cannot retain the departed session and
+// player, and cannot replay against a later session for the same name.
+func (q *CommandQueue) ClearPlayer(name string) {
+ q.mu.Lock()
+ defer q.mu.Unlock()
+ delete(q.freeQueue, name)
+ delete(q.activeQueue, name)
+ delete(q.consumeQueue, name)
+}
+
// DrainConsume returns and removes the consume command for the player.
func (q *CommandQueue) DrainConsume(name string) (*QueuedCommand, bool) {
q.mu.Lock()