mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-18 08:15:03 +00:00
fix(server): name the player on every played word, and bound the process
PlayedWord.player_id was declared and read by the client but never set by the server, so a room of three or four never showed who played each word. The chain byline now comes from the room, with a producer-side test. The process also gains the ceilings it was missing: a cap on live rooms and on open sockets, a per-connection frame-rate limit so a payload-less frame is no longer free, and an opt-in trusted-proxy list so the join limiter can tell players apart behind the documented reverse proxy instead of putting them in one bucket. The typed word is sanitized before the engine stores it, since every seat is shown it; a room exiting on its idle clock releases the sessions still bound to it; the dictionary builder escapes its SQLite path like the store does and renames over the old database instead of deleting it first.
This commit is contained in:
1 parent
a5052fca3f
commit
442ced32cc
14 files changed
+494
-54
No files matched your search
@@ -26,8 +26,15 @@ const codeAttempts = 10
|
||||
var (
|
||||
errRoomNotFound = errors.New("wsapi: no such room")
|
||||
errNoRoomCode = errors.New("wsapi: could not allocate a room code")
|
||||
errServerFull = errors.New("wsapi: room limit reached")
|
||||
)
|
||||
|
||||
// defaultMaxRooms bounds live rooms across the whole process when nothing else
|
||||
// is configured. Each room is a goroutine, an engine and a registry entry held
|
||||
// for up to the idle window, so without a ceiling the per-connection limiter
|
||||
// only sets the rate at which a fleet of connections can fill memory.
|
||||
const defaultMaxRooms = 1000
|
||||
|
||||
// hub owns the registries and nothing else.
|
||||
//
|
||||
// It never touches a game: rooms are handed out as pointers whose channels are
|
||||
@@ -41,6 +48,7 @@ type hub struct {
|
||||
turnLimit time.Duration
|
||||
graceFor time.Duration
|
||||
idleFor time.Duration
|
||||
maxRooms int
|
||||
|
||||
mu sync.Mutex
|
||||
rooms map[string]*room
|
||||
@@ -49,13 +57,17 @@ type hub struct {
|
||||
joinLimiter *keyedLimiter
|
||||
}
|
||||
|
||||
func newHub(ctx context.Context, dict Dictionary, turnLimit, graceFor, idleFor time.Duration) *hub {
|
||||
func newHub(ctx context.Context, dict Dictionary, turnLimit, graceFor, idleFor time.Duration, maxRooms int) *hub {
|
||||
if maxRooms <= 0 {
|
||||
maxRooms = defaultMaxRooms
|
||||
}
|
||||
return &hub{
|
||||
ctx: ctx,
|
||||
dict: dict,
|
||||
turnLimit: turnLimit,
|
||||
graceFor: graceFor,
|
||||
idleFor: idleFor,
|
||||
maxRooms: maxRooms,
|
||||
rooms: map[string]*room{},
|
||||
sessions: map[string]*session{},
|
||||
joinLimiter: newKeyedLimiter(joinsPerSecond, joinBurst, limiterIdleFor),
|
||||
@@ -145,7 +157,14 @@ func (h *hub) newRegisteredRoom() (*room, error) {
|
||||
|
||||
r := newRoom(h, code, h.turnLimit, h.graceFor, h.idleFor)
|
||||
|
||||
// The ceiling is checked under the same lock that registers the room, so
|
||||
// two creators racing for the last slot cannot both get it.
|
||||
h.mu.Lock()
|
||||
if len(h.rooms) >= h.maxRooms {
|
||||
h.mu.Unlock()
|
||||
r.cancel()
|
||||
return nil, errServerFull
|
||||
}
|
||||
h.rooms[code] = r
|
||||
h.mu.Unlock()
|
||||
|
||||
@@ -153,6 +172,13 @@ func (h *hub) newRegisteredRoom() (*room, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// roomCount is how many rooms are live right now.
|
||||
func (h *hub) roomCount() int {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
return len(h.rooms)
|
||||
}
|
||||
|
||||
// evict removes a finished room. Called by the room goroutine as it exits, so
|
||||
// a code is reusable the moment its game is done.
|
||||
func (h *hub) evict(code string) {
|
||||
|
||||
Reference in new issue
Block a user