在线联机原型全集:第 20 章 社交大厅(Platform Lobby|Multi-Room Roaming & Unified Identity)

第 20 章:社交大厅(Platform Lobby|Multi-Room Roaming & Unified Identity),介绍了一个简单的社交大厅游戏原型,包括平台大厅、多房间漫游、统一身份系统、房间目录、即时通信、跨房间通信、内容安全等功能。

社交大厅(Platform Lobby|Multi-Room Roaming & Unified Identity)

  • 类别:平台层(账号 + 会话 + 大厅 + 房间目录 + 即时通信 + 漫游)

  • 目标

    1. 构建统一身份系统(OIDC/OAuth2 + JWT/RT + Device Binding);
    2. 打通多房间漫游(从大厅→若干游戏房→返回大厅)的无缝进出与断线恢复;
    3. 提供房间目录与搜索全局在线/好友/队伍/公会跨房间即时通信内容安全
    4. 支撑“一个入口,多原型切换”的产品化能力(第 14–19 章原型可从此入口跳转)。
  • 原型代号proto-020-lobby-portal

  • 依赖模块proto-002-rps(时序/事务基元)、proto-007-snake-battle(网关/房间骨架)、proto-016/17/19(断线恢复/观战/AOI 思路)

  • 推荐栈:Server(Go/Java 优先,网关 + 身份 + 大厅 + 漫游协调器),Client(TS/Unity/Unreal)

  • 协议栈:HTTP(注册/登录/目录/鉴权) + WebSocket(在线/IM/事件) + OIDC(外部登录)


1. 总体视图与模块划分

graph TD
  U["Client(Web/PC/Mobile)"] --> GW["Gateway(WS/UDP/HTTP)"]
  GW --> IDP["Identity Provider(OIDC/OAuth2)"]
  GW --> LOB["Social Lobby(Presence/IM/Directory)"]
  LOB --> DIR["Room Directory(Match/Query)"]
  LOB --> PARTY["Party/Team"]
  LOB --> MOD["Moderation/Safety"]
  LOB --> ROAM["Roaming(Coordinator/Handoff)"]
  ROAM --> RM1["Game Room A"]
  ROAM --> RM2["Game Room B"]
  IDP --> KMS["Key Mgmt(JWT/Device Keys)"]
  LOB --> STORE["Profile/Relation/Inventory"]
  LOB --> OBS["Metrics/Logs/Audit"]

2. 统一身份系统(Unified Identity / SSO)

2.1 账号模型

  • Useruser_id(全局唯一,雪花/ULID)、tenant_id(可选多租户)、usernameemail/phonestatus
  • Identity:外部绑定(provider=wechat/google/steam/...sub),多身份→一账号
  • Profile:展示名、头像、地区、语言、年龄段标记(COPPA/未成年人保护)
  • Securitymfapassword_hashdevice_keys[]risk_score

2.2 鉴权与令牌

  • OIDC/OAuth2/authorize → /token;支持密码/设备码/社交登录

  • 令牌对

    • Access Token(JWT):短期(5–15 分钟),作用域(scope: lobby.read chat.send room.join
    • Refresh Token(RT):长寿命(7–30 天),可绑定设备与会话
  • JWT Claim 建议sub, aud, iss, iat, exp, jti, scope, device_id, tenant_id, session_id, roles

  • 密钥:KMS 轮换(Kid + JWKs),支持逐版本签名强制下线(RT 黑名单 + JWT 版本号)

2.3 设备与会话

  • Device Binding:设备指纹/ID(可匿名→登录后绑定)
  • 多端策略:同一账号可多设备在线;policy: allow_one_room_per_game
  • 会话表sessions(session_id, user_id, device_id, ip, ua, created_at, last_seen, jwt_ver)

2.4 权限/角色

  • 系统角色user, mod, gm, admin
  • 房间内角色owner, host, member, spectator, banned
  • 策略引擎:ABAC(属性 + 规则),例如“未成年人禁用语音/夜间限时”。

3. 社交大厅(Presence / IM / Directory)

3.1 在线状态(Presence)

  • 状态offline, online, in_lobby, in_room(game_id, room_id), do_not_disturb
  • 心跳:WS 心跳 15–30s;超时转离线;
  • 订阅:好友/公会/队伍订阅其在线变化,事件 PresenceUpdate

3.2 即时通信(IM)

  • 频道global, lobby, room, party, guild, dm
  • 消息类型:文本/表情/系统卡片(开房/邀请/战绩)
  • 漫游存档:按频道分区,窗口滑动(例如最近 1000 条)
  • 内容安全:脏词过滤、敏感样式检测、图像审核(占位,原型可文本)

3.3 房间目录(Directory)

  • 查询list(game_id, mode, region, elo_band, keyword)
  • 排序:活跃度、延迟、好友在内优先
  • 开房CreateRoom(game_id, capacity, visibility=public|friends|private, password?)
  • 匹配:轻量 MatchMaker(基于段位/地区/队伍人数);可对接 15–19 章的各原型

4. 多房间漫游(Roaming / Handoff)

4.1 目标

  • 从大厅无缝切换到任一游戏房,再返回;
  • 更换房间/游戏时会话不断开,只更新目标房间授权
  • 断线恢复:重连后自动回到上次所在房间/大厅。

4.2 漫游令牌(Room Ticket)

  • Room Ticket(短签):由 Roaming 协调器签发,ttl=60–120s,一次性消费
  • 内容user_id, room_id, game_id, issued_at, exp, role, session_id, anti_replay_nonce, sig(kid)
  • 使用:客户端持大厅 Access Token 向 Roaming 请求 Ticket → 与目标房间握手 → 房间验证签名并绑定会话

4.3 切换时序

sequenceDiagram
  participant C as Client
  participant L as Lobby (Roaming)
  participant R as Game Room
  C->>L: requestRoomTicket(game_id, room_id, access_jwt)
  L-->>C: room_ticket(sig), room_endpoint
  C->>R: ws.connect(room_endpoint, room_ticket)
  R->>R: verify(ticket.sig & jti not used)
  R-->>C: join-ack(state snapshot / seat / role)
  C-->>L: presence: in_room(game_id, room_id)

4.4 返回大厅 / 跨房切换

  • 退出房间 → 房间发 leave 到 Lobby → Presence 切回 in_lobby
  • 跨房:直接申请新 Ticket,与新房握手后旧房自动释放座位(或开启“并房观战”权限)

4.5 断线恢复

  • 客户端携 session_id + last_room 重连 Lobby → Lobby 查询 room_state

    • 若房间仍存活且座位保留 → 再签发 Ticket 复位
    • 若房间关闭 → 回大厅并推送“上局战绩/回放链接”

5. 统一资料(Profile)、好友/队伍/公会

5.1 资料与隐私

  • 公开资料:昵称/头像/地区/签名/最近游玩
  • 隐私控制:可见范围(公开/好友/仅自己)、开关“显示在线/显示所在房间”
  • 屏蔽/拉黑:在 IM 与匹配中生效

5.2 好友与关系

  • 关系图friend, block, follow
  • 邀请/同玩:发起 PartyInvite,接受后进入队伍频道
  • 跨游戏邀请:发“跳转+Ticket”的系统卡片

5.3 队伍(Party)

  • 结构party_id, leader, members[], game_lock?
  • 就绪流ReadyCheck,超时替换/开黑保护
  • 一键进房:队长为全队申请房间与 Tickets,成员逐个握手

5.4 公会(Guild)

  • 层级owner, officer, member
  • 频道:公会聊天室与公告;活动(固定开房模板)

6. 内容安全与治理(Moderation)

  • 实时过滤:脏词/辱骂/广告;命中→降权/撤回/禁言
  • 举报闭环Report(user_id, evidence) → 审核队列 → 结果通知
  • 安全评分safety_score 跨房共享,影响匹配分组与聊天阈值
  • 未成年人保护:夜间禁言/禁玩时段、语音关闭、家长控制 PIN
  • 合规审计:日志落地(消息、踢封、举报处理结果),留存周期可配

7. 数据与存储模型(要点)

7.1 关系/KV 表

  • users(id, tenant_id, username, email, phone, status, created_at)
  • identities(user_id, provider, sub, meta)
  • sessions(id, user_id, device_id, ip, ua, jwt_ver, last_seen)
  • profiles(user_id, nickname, avatar, locale, privacy, safety_score)
  • relations(user_id, peer_id, kind, created_at)
  • parties(id, leader, members_blob, game_lock)
  • guilds(id, owner, meta) / guild_members(gid, uid, role)
  • rooms(id, game_id, state, endpoint, cap, visibility, tags, created_at)
  • presence(user_id, state, game_id, room_id, updated_at)
  • chat_messages(channel_id, seq, sender, kind, content, ts)(按频道分表/流式存储)
  • moderation_actions(id, user_id, action, reason, ts)
  • tickets(jti, user_id, room_id, exp, used)(一次性校验与黑名单)

7.2 事件溯源(供回放/审计)

  • Lobby EventUserLogin, UserLogout, CreateRoom, JoinRoom, LeaveRoom, PartyInvite, PartyAccept, FriendAdd, Mute, Ban
  • Chat EventMessage, Recall, Report, Action
  • 可重放:用于重建大厅状态(在线数/队伍/房间列表)

8. API & 协议(示例)

8.1 登录/刷新(HTTP)

POST /oauth/token
grant_type=authorization_code|password|refresh_token
→ { access_token, refresh_token, expires_in, id_token?, scope }

8.2 大厅 WS(事件流)

// 订阅
{ "t":"sub", "ch":["presence","friends","party","rooms","chat:lobby"] }

// 房间查询
{ "t":"rooms.query", "game":"proto-019", "filters":{"region":"eu","mode":"squad"} }

// 邀请
{ "t":"party.invite", "to": 12345, "game":"proto-016" }

// 聊天
{ "t":"chat.send", "ch":"chat:party:789", "msg":"ready?" }

8.3 漫游 Ticket(HTTP)

POST /roam/ticket
Authorization: Bearer <access_jwt>
Body: { "room_id":"r-abc", "game_id":"proto-016" }
→ { "ticket":"base64(sig)...", "endpoint":"wss://g16.example/ws" }

9. 断线恢复与多端切换

  • 大厅恢复:凭 session_id 恢复 Presence 与订阅频道;离线消息补齐(光标 last_seq
  • 房间恢复:参照各游戏原型的关键帧 + 事件补齐;Lobby 负责 Ticket 再签发
  • 多端切换:在新设备登录后,允许“迁移会话”(旧端降级为只读大厅或被踢下线,策略可配)

10. 可观测性与指标

  • 身份:登录成功率、RT 刷新失败率、JWT 版本失配率
  • 大厅:在线人数、并发 WS 连接数、消息吞吐、延迟分位
  • 漫游:Ticket 签发 QPS、校验失败率、跨房切换耗时、断线恢复时间
  • 社交:日活私聊/群聊条数、撤回/举报率、误杀率
  • 治理:封禁/禁言/解封时长分布、复核通过率
  • SLA:网关 p95、Lobby p95、目录查询 p95、单节点最大并发连接

11. 安全与风控

场景风险对策
令牌窃取伪造或重放JWT Kid+过期+受众校验;RT 绑定设备/IP 风险;jti 黑名单;TLS 固钉
Ticket 滥用跨房复用Ticket 一次性 + 短期;房间验证 used;Room ↔ Lobby 双向回执
冒名顶替改昵称/头像欺骗昵称唯一/审核;房内展示 #user_id 简短标签
垃圾消息机器人刷屏频道速率限制/滑动窗口;挑战(验证码/行为学)
年龄合规未成年保护时段/语音限制;敏感词更严格阈值
数据泄漏隐私/聊天外泄服务端裁剪;最小可见原则;敏感字段脱敏存储

12. 测试与验证

  1. SSO 回路:密码/社交/刷新/轮换/强制下线全链路;
  2. 漫游稳定性:1→N 房切换(N=20),平均切换耗时 < 800ms;断线 10s 后恢复 < 3s;
  3. 目录吞吐:1 万在房 + 2 千并发查询,p95 < 120ms;
  4. IM 压测:每秒 5 万条消息,按频道分片,丢包率 0;
  5. 安全回归:令牌重放/混淆输入/超速聊天/大字节消息拦截;
  6. 容灾:Lobby 节点滚动升级/重启不断线;KMS 轮换不中断签名校验。

13. 客户端 UX 关键点

  • 统一入口:左侧游戏平台(原型列表/最近游玩/推荐),右侧聊天/好友/队伍面板
  • 一键开黑:队伍一键开房并跳转(显示“正在为队伍预留座位…”)
  • 漫游反馈:切换房间的过渡卡片(房间名、模式、延迟、票据校验进度)
  • 错误可恢复:Ticket 过期→自动重签;房间满员→排队与预计时间
  • 隐私开关:一键隐身、关闭被邀请、限制陌生人私聊

14. 版本迭代路线

版本目标要点
v0.1身份 + 大厅骨架OIDC/JWT/RT、Presence、Lobby WS、房间目录
v0.2漫游最小闭环Ticket + Handoff + 断线回到房间
v0.3社交增强好友/队伍/邀请卡片、频道化 IM、基础过滤
v0.4治理与合规举报/禁言/审计、未成年策略
v1.0规模与稳定横向扩展、指标看板、压测达标(>50k 并发 WS)

15. MVP 勾选清单

  • OIDC 身份与 JWT/RT 策略(KMS 轮换 + 强制下线)
  • 大厅 WS(Presence/IM/Directory)
  • Roaming Ticket(一次性短签)与房间握手
  • 多房间切换与断线恢复
  • 基础内容安全(脏词/速率)与举报流
  • 指标看板与审计日志

16. 参考实现要点(伪代码)

16.1 签发 Ticket(Go)

type RoomTicket struct {
  UserID string `json:"sub"`
  RoomID string `json:"rid"`
  GameID string `json:"gid"`
  Sid    string `json:"sid"`
  Nonce  string `json:"jti"`
  Iat    int64  `json:"iat"`
  Exp    int64  `json:"exp"`
  Role   string `json:"role"`
}

func IssueTicket(user, room, game, sid string, ttl time.Duration) (string, error) {
  t := RoomTicket{UserID:user, RoomID:room, GameID:game, Sid:sid,
    Iat: now(), Exp: now()+int64(ttl.Seconds()), Nonce: ulid(), Role:"member"}
  return jwt.Sign(t, kms.ActiveKey()) // kid embedded
}

16.2 房间校验(Go)

func (h *Handshake) Verify(ticket string) (*Claims, error) {
  cl := jwt.Verify(ticket, kms.JWKS())
  if used(cl.Jti) || cl.Exp < now() || cl.Aud != expectedAud { return ErrDenied }
  markUsed(cl.Jti)
  bindSession(cl.Sid, cl.Sub, cl.RoomID)
  return cl, nil
}

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页