更新功能、刷新不立即解散房间
This commit is contained in:
@@ -319,3 +319,30 @@ func (h *Hub) removeUserRoom(uid int) {
|
||||
delete(h.userRoom, uid)
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
// onStarveOfflineTimeout 饥荒等待中断线保护超时:30秒内未重连才清座;若房间无真人则解散
|
||||
func (h *Hub) onStarveOfflineTimeout(r *Room, userID, seatIndex int) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
seat := r.seatByUser(userID)
|
||||
if seat == nil || seat.Index != seatIndex || seat.Online || seat.client != nil || r.closed {
|
||||
return
|
||||
}
|
||||
r.clearSeatLocked(seat)
|
||||
r.broadcastStateLocked()
|
||||
delete(h.userRoom, userID)
|
||||
|
||||
empty := true
|
||||
for _, s := range r.seats {
|
||||
if s.UserID > 0 {
|
||||
empty = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if empty {
|
||||
delete(h.rooms, r.Code)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,16 +31,17 @@ const (
|
||||
|
||||
// Seat 房间内的一个座位
|
||||
type Seat struct {
|
||||
Index int // 座位号(斗地主0-2,象棋0红1黑)
|
||||
UserID int // 真人用户ID(AI 座位为 0)
|
||||
Name string // 显示昵称
|
||||
Avatar string // 头像
|
||||
Skin string // 皮肤编码(饥荒组队联机:更衣室选择的皮肤)
|
||||
IsAI bool // 是否 AI 座位
|
||||
Ready bool // 是否已准备(AI 恒为 true)
|
||||
Online bool // 是否在线(断线重连用)
|
||||
Auto bool // 真人主动托管中(每回合由规则 AI 代打,结算后自动取消)
|
||||
client *Client // 关联的连接(AI 为 nil)
|
||||
Index int // 座位号(斗地主0-2,象棋0红1黑)
|
||||
UserID int // 真人用户ID(AI 座位为 0)
|
||||
Name string // 显示昵称
|
||||
Avatar string // 头像
|
||||
Skin string // 皮肤编码(饥荒组队联机:更衣室选择的皮肤)
|
||||
IsAI bool // 是否 AI 座位
|
||||
Ready bool // 是否已准备(AI 恒为 true)
|
||||
Online bool // 是否在线(断线重连用)
|
||||
Auto bool // 真人主动托管中(每回合由规则 AI 代打,结算后自动取消)
|
||||
client *Client // 关联的连接(AI 为 nil)
|
||||
offlineTimer *time.Timer // 饥荒等待中断线保护:30秒后仍未重连才清座
|
||||
}
|
||||
|
||||
// chessState 象棋对局状态
|
||||
@@ -93,6 +94,7 @@ type Room struct {
|
||||
|
||||
// 同一好友再次邀请的最短间隔
|
||||
const inviteCooldown = 60 * time.Second
|
||||
const starveOfflineGrace = 30 * time.Second // 饥荒等待中断线保护:30秒内重连不散房
|
||||
|
||||
// newRoom 构造房间:按游戏类型确定座位数,按配置构造 AI 实例
|
||||
func newRoom(code, game, mode, aiProvider, aiDifficulty string) *Room {
|
||||
@@ -202,6 +204,10 @@ func (r *Room) reattach(c *Client) {
|
||||
defer r.mu.Unlock()
|
||||
for _, s := range r.seats {
|
||||
if s.UserID == c.userID {
|
||||
if s.offlineTimer != nil {
|
||||
s.offlineTimer.Stop()
|
||||
s.offlineTimer = nil
|
||||
}
|
||||
s.client = c
|
||||
s.Online = true
|
||||
r.touch()
|
||||
@@ -223,6 +229,18 @@ func (r *Room) onOffline(c *Client) (leftRoom bool) {
|
||||
seat.client = nil
|
||||
seat.Online = false
|
||||
if r.status == "waiting" {
|
||||
if r.Game == "starve" {
|
||||
// 饥荒等待中断线保护:保留座位 30 秒,刷新/断线重连成功后取消清理
|
||||
if seat.offlineTimer != nil {
|
||||
seat.offlineTimer.Stop()
|
||||
}
|
||||
offlineUID, offlineSeat := seat.UserID, seat.Index
|
||||
seat.offlineTimer = time.AfterFunc(starveOfflineGrace, func() {
|
||||
c.hub.onStarveOfflineTimeout(r, offlineUID, offlineSeat)
|
||||
})
|
||||
r.broadcastStateLocked()
|
||||
return false
|
||||
}
|
||||
r.clearSeatLocked(seat)
|
||||
r.broadcastStateLocked()
|
||||
return true
|
||||
@@ -301,6 +319,10 @@ func (r *Room) leave(c *Client) {
|
||||
// clearSeatLocked 清空座位;房主离开时移交房主(需持锁)
|
||||
func (r *Room) clearSeatLocked(seat *Seat) {
|
||||
uid := seat.UserID
|
||||
if seat.offlineTimer != nil {
|
||||
seat.offlineTimer.Stop()
|
||||
seat.offlineTimer = nil
|
||||
}
|
||||
*seat = Seat{Index: seat.Index}
|
||||
if uid == r.hostUserID {
|
||||
r.hostUserID = 0
|
||||
|
||||
Reference in New Issue
Block a user