aboutsummaryrefslogtreecommitdiff
path: root/internal/net
diff options
context:
space:
mode:
Diffstat (limited to 'internal/net')
-rw-r--r--internal/net/server.go35
1 files changed, 30 insertions, 5 deletions
diff --git a/internal/net/server.go b/internal/net/server.go
index 42077a1..adc828c 100644
--- a/internal/net/server.go
+++ b/internal/net/server.go
@@ -67,13 +67,20 @@ type Session struct {
PendingSkill string
PendingLastFlag string
PendingBackground bool
- Disconnecting bool
- DisconnectTicks int
PendingUndigDir string
PendingUndigRoom int
promptVisible bool
writeMu sync.Mutex
+ // Disconnect bookkeeping is written by the connection goroutine that
+ // discovered the dead transport (Hub.Remove's deferred path) and read
+ // by the game's per-tick DisconnectTick on the owner goroutine, so both
+ // fields are atomics. Plain fields would be a data race (and, worse, a
+ // lost disconnect: the owner's tick could spin forever on a stale
+ // value).
+ disconnecting atomic.Bool
+ disconnectTicks atomic.Int64
+
// Outbound queueing. Transport sessions (telnet/TLS/websocket) are put
// into queued mode by startWriter before their input goroutine begins:
// Write* then enqueues into outbox, and the dedicated writer goroutine
@@ -198,6 +205,25 @@ func NewHub() *Hub {
}
}
+// StartDisconnect arms the graceful-disconnect countdown. Called by the
+// connection goroutine that discovered the dead transport; DisconnectTick
+// (on the game owner) counts the session down and hard-removes it.
+func (sess *Session) StartDisconnect(ticks int) {
+ sess.disconnectTicks.Store(int64(ticks))
+ sess.disconnecting.Store(true)
+}
+
+// IsDisconnecting reports whether the graceful-disconnect countdown has
+// been armed. Safe to call from any goroutine.
+func (sess *Session) IsDisconnecting() bool { return sess.disconnecting.Load() }
+
+// TickDisconnectCountdown decrements the armed countdown and reports
+// whether it has expired. Called only from the game's DisconnectTick
+// (owner goroutine); safe against a concurrent StartDisconnect.
+func (sess *Session) TickDisconnectCountdown() bool {
+ return sess.disconnectTicks.Add(-1) <= 0
+}
+
func (h *Hub) OnRemove(cb func(*Session)) {
h.mu.Lock()
defer h.mu.Unlock()
@@ -219,9 +245,8 @@ func (h *Hub) Add(s *Session) {
func (h *Hub) Remove(s *Session) {
h.mu.Lock()
- if s.Player != nil && !s.Disconnecting {
- s.Disconnecting = true
- s.DisconnectTicks = 10
+ if s.Player != nil && !s.IsDisconnecting() {
+ s.StartDisconnect(10)
onDisconnect := h.onDisconnect
h.mu.Unlock()
if onDisconnect != nil {