更新功能

This commit is contained in:
李琦
2026-08-14 17:50:37 +08:00
parent 2f5ce02608
commit 19df9f2bfd
9 changed files with 566 additions and 9 deletions

View File

@@ -25,8 +25,30 @@ type wsMessage struct {
}
// push 向该客户端推送一条消息(满队列时丢弃,防止慢客户端拖垮全局)
// 注意:data 若是 json.RawMessage([]byte),必须走 wsMessage 结构体嵌入,
// 否则 encoding/json 会把 []byte 编成 base64 字符串,饥荒世界快照会损坏。
func (c *Client) push(msgType string, data any) {
payload, _ := json.Marshal(map[string]any{"type": msgType, "data": data})
var dataRaw json.RawMessage
switch v := data.(type) {
case nil:
dataRaw = json.RawMessage("null")
case json.RawMessage:
if len(v) == 0 {
dataRaw = json.RawMessage("null")
} else {
dataRaw = v
}
default:
b, err := json.Marshal(v)
if err != nil {
return
}
dataRaw = b
}
payload, err := json.Marshal(wsMessage{Type: msgType, Data: dataRaw})
if err != nil {
return
}
select {
case c.send <- payload:
default:
@@ -44,7 +66,7 @@ func (c *Client) readPump() {
c.hub.onDisconnect(c)
c.conn.Close()
}()
c.conn.SetReadLimit(8 * 1024)
c.conn.SetReadLimit(4 * 1024 * 1024) // 4MB:饥荒全量世界快照(tiles+ents)远超默认 8KB
// 心跳:60秒收不到任何数据判定断线(前端每30秒发 ping)
c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
c.conn.SetPongHandler(func(string) error {

View File

@@ -463,7 +463,7 @@ func (r *Room) handleMessage(c *Client, msgType string, raw json.RawMessage) {
seat.Skin = req.Skin
r.broadcastStateLocked()
case "starve_input":
// 饥荒队友输入:不解析内容,附上座位号仅转发给房主连接(主机权威模拟)
// 饥荒队友输入:解析后附上权威座位号转发给房主(主机权威模拟)
if r.Game != "starve" || r.status != "playing" {
return
}
@@ -471,14 +471,27 @@ func (r *Room) handleMessage(c *Client, msgType string, raw json.RawMessage) {
if host == nil || host.client == nil || seat.UserID == r.hostUserID {
return
}
host.client.push("starve_input", map[string]any{"seat": seat.Index, "data": raw})
var payload map[string]any
if err := json.Unmarshal(raw, &payload); err != nil || payload == nil {
return
}
payload["seat"] = seat.Index // 以服务端座位为准,防伪造
host.client.push("starve_input", payload)
case "starve_state":
// 饥荒主机快照:仅房主可发,原样广播给其余座位(间隔 <80ms 丢弃防刷)
// 饥荒主机快照:仅房主可发,原样广播给其余座位
// 全量世界(full:1)不限流;增量快照间隔 <80ms 丢弃防刷
if r.Game != "starve" || r.status != "playing" || seat.UserID != r.hostUserID {
return
}
isFull := false
var peek struct {
Full int `json:"full"`
}
if json.Unmarshal(raw, &peek) == nil && peek.Full != 0 {
isFull = true
}
now := time.Now().UnixMilli()
if now-r.starveMs < 80 {
if !isFull && now-r.starveMs < 80 {
return
}
r.starveMs = now
@@ -495,6 +508,62 @@ func (r *Room) handleMessage(c *Client, msgType string, raw json.RawMessage) {
r.broadcastChatLocked(-1, "系统", "", "全员阵亡,冒险结束", false)
r.endStarveLocked("全员阵亡,冒险结束")
r.broadcastStateLocked()
case "invite":
// 邀请好友进房:仅房主可发,目标必须是自己的已通过好友且不在房内
// 好友在线则实时推弹窗邀请;离线落一条私聊兜底(上线可见)
var req struct {
UserID int `json:"user_id"`
}
json.Unmarshal(raw, &req)
if r.status != "waiting" {
c.pushError("对局进行中,无法邀请")
return
}
if seat.UserID != r.hostUserID {
c.pushError("只有房主可以邀请好友")
return
}
if req.UserID <= 0 || req.UserID == c.userID {
c.pushError("参数有误")
return
}
// 必须是已通过的好友关系
var cnt int64
database.DB.Model(&model.Friend{}).
Where("(user_id = ? AND friend_id = ? OR user_id = ? AND friend_id = ?) AND status = ?",
c.userID, req.UserID, req.UserID, c.userID, model.FriendStatusAccepted).
Count(&cnt)
if cnt == 0 {
c.pushError("只能邀请好友")
return
}
// 不能已在房内
for _, s := range r.seats {
if s.UserID == req.UserID {
c.pushError("对方已在房间内")
return
}
}
gameName := r.Game
var g model.Game
if err := database.DB.Where("code = ?", r.Game).First(&g).Error; err == nil && g.Name != "" {
gameName = g.Name
}
inviteData := map[string]any{
"code": r.Code, "game": r.Game, "game_name": gameName,
"host_id": c.userID, "host_name": seat.Name, "host_avatar": seat.Avatar,
}
// 在线实时弹窗;离线降级为私聊消息兜底(下次上线可见)
if !PushToUser(req.UserID, "room_invite", inviteData) {
var peer model.User
if err := database.DB.First(&peer, req.UserID).Error; err == nil {
database.DB.Create(&model.ChatMessage{
FromID: c.userID, ToID: req.UserID,
Content: fmt.Sprintf("邀请你加入房间【%s】,邀请码 %s", gameName, r.Code),
})
}
}
c.push("invite_sent", map[string]any{"user_id": req.UserID})
case "chat":
var req struct {
Text string `json:"text"`