package main import ( "sync" "time" ) type Room struct { Players map[string]*Player Input chan InputMessage mu sync.Mutex } func NewRoom() *Room { return &Room{ Players: make(map[string]*Player), Input: make(chan InputMessage, 128), } } func (r *Room) update() { // 1️⃣ обрабатываем input for { select { case input := <-r.Input: r.mu.Lock() p := r.Players[input.PlayerID] if p != nil { p.DX = input.DX p.DY = input.DY } r.mu.Unlock() default: goto DONE } } DONE: // 2️⃣ двигаем игроков r.mu.Lock() for _, p := range r.Players { p.X += p.DX * 4 p.Y += p.DY * 4 } r.mu.Unlock() } func (r *Room) broadcast() { r.mu.Lock() state := make(map[string]map[string]any, len(r.Players)) for id, p := range r.Players { state[id] = map[string]any{ "x": p.X, "y": p.Y, "name": p.Username, } } r.mu.Unlock() msg := StateMessage{ Type: "state", Payload: map[string]any{ "players": state, }, } // отправляем БЕЗ mutex — важно for _, p := range r.Players { _ = p.Conn.WriteJSON(msg) } } func (r *Room) Run() { ticker := time.NewTicker(time.Second / 30) defer ticker.Stop() for range ticker.C { r.update() r.broadcast() } }