aboutsummaryrefslogtreecommitdiff
path: root/internal/net/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/net/server.go')
-rw-r--r--internal/net/server.go13
1 files changed, 13 insertions, 0 deletions
diff --git a/internal/net/server.go b/internal/net/server.go
index b0407fc..42077a1 100644
--- a/internal/net/server.go
+++ b/internal/net/server.go
@@ -174,7 +174,15 @@ func (sess *Session) StartWriter() {
// enqueue appends msg to the outbound queue. When the client's queue is full
// the session is disconnected instead of blocking the caller: the world must
// never wait on a single slow client.
+// enqueue appends msg to the outbound queue. When the client's queue is
+// full the session is disconnected instead of blocking the caller: the world
+// must never wait on a single slow client.
func (sess *Session) enqueue(msg string) {
+ // A broadcast can race Close(): the queue may already be (or become)
+ // closed mid-send. Such late writes are dropped, never panicked on —
+ // Close() closes the queue so the writer goroutine drains whatever was
+ // queued before the close and then flushes the transport.
+ defer func() { _ = recover() }()
select {
case sess.outbox <- msg:
default:
@@ -490,6 +498,10 @@ func (s *Server) serveTelnet() {
Conn: newTCPConn(conn),
State: StateAccountName,
}
+ // Queued writes like the websocket transport: a dedicated writer
+ // goroutine performs the I/O with per-message deadlines, so a slow
+ // or stalled telnet client can never block game code.
+ sess.StartWriter()
s.hub.Add(sess)
go s.handleSession(sess, s.handler)
}
@@ -505,6 +517,7 @@ func (s *Server) serveTelnetTLS() {
Conn: newTCPConn(conn),
State: StateAccountName,
}
+ sess.StartWriter()
s.hub.Add(sess)
go s.handleSession(sess, s.handler)
}