6.5 游戏后端与用户管理


6.5 游戏后端与用户管理

数字/事实:一款中等手游可能有百万级玩家档案、实时排行榜每秒更新、对战房间要毫秒级状态同步。这些需求里,玩家档案、好友关系、排行榜这类"数据为主"的部分 Supabase 能很好覆盖;但帧级对战状态同步得用专用游戏服务。这一节我们讲游戏的"数据层"怎么用 Supabase 搭,以及边界。

玩家档案与社交关系

玩家档案本质是带扩展字段的用户表,好友关系是多对多。这两者都是关系模型强项:

-- 玩家档案,复用 auth.users 的 id create table public.players ( id uuid primary key references auth.users(id), nickname text unique not null, level int default 1, coins int default 0 ); -- 好友关系(双向,用复合主键去重) create table public.friendships ( a uuid references public.players(id), b uuid references public.players(id), primary key (a, b), created_at timestamptz default now() ); alter table public.players enable row level security; alter table public.friendships enable row level security; -- 玩家可读自己档案,好友可读彼此公开信息 create policy "自己档案" on public.players for all using (auth.uid() = id) with check (auth.uid() = id); create policy "好友互看" on public.players for select using ( exists ( select 1 from public.friendships f where (f.a = auth.uid() and f.b = players.id) or (f.b = auth.uid() and f.a = players.id) ) );

排行榜:用索引与物化保持快

排行榜是"按分数排序取前 N",高频读。给分数建索引,查询走有序扫描:

create index idx_players_score on public.players (coins desc); -- 取前 100 名 select nickname, coins from public.players order by coins desc limit 100;

若分数更新极频繁、榜单又大,可用物化视图定时刷新,避免每次实时排序全表。

实时对战的边界

聊天那种"数据变了推一下"的实时,Supabase Realtime 胜任。但动作游戏的"每帧坐标同步"是另一回事——要求毫秒级、高频、状态机复杂,应交给专用游戏服务器(如基于 UDP 的帧同步服务),Supabase 只存最终战绩。

下面 SVG 区分"Supabase 能管的游戏数据"和"要专用服务的部分":

三、实时对战的边界

展开案例:休闲游戏的排行榜与好友

背景:一个消除类休闲游戏要做"好友排行榜"和国家榜,玩家数据要安全、榜单要快。

操作过程:

  1. 玩家首次登录,触发器自动建 players 行(昵称来自第三方登录元数据)。
  2. 每过一关,客户端 update players set coins = coins + 奖励 where id = auth.uid(),RLS 保证只能改自己。
  3. 排行榜页面 select ... order by coins desc limit 100 走索引,毫秒返回。
  4. 好友榜:先查 friendships 拿到好友 id 列表,再 where id in (...) 取分数,前端并排展示。
  5. 对战类小游戏(回合制)用 Realtime 的 broadcast 同步出牌,帧级动作游戏另接专用服务。

结果:数据层零自建服务,榜单流畅;真正需要低延迟的部分才引入专用组件,成本可控。

解读:游戏后端常被误以为"必须全套自研"。其实数据为主的部分(档案、社交、榜单、存档)用 Supabase 又快又安全,只有实时战斗这种硬实时需求才需要专用服务。把两者切开,省钱省心。

变式:若担心分数被客户端篡改(直接 update coins),可把"加分数"做成数据库函数,由服务端/受信函数调用,客户端只能触发"过关"事件,不能自己写分数——把信任边界收进库。

-- 受信函数:只有 security definer + 服务端调用才能改分数 create or replace function public.award_coins(p_player uuid, p_delta int) returns void language plpgsql security definer set search_path = public as $$ begin -- 校验增量合理(比如单关奖励不超过上限,防刷) if p_delta < 0 or p_delta > 1000 then raise exception '非法奖励值'; end if; update public.players set coins = coins + p_delta where id = p_player; end; $$; -- 客户端只能调函数,不能直接 update 列 revoke update on public.players from anon, authenticated;

security definer 让函数以定义者权限跑,配合 revoke update,玩家即使绕过客户端也改不了 coins 列,只能走 award_coins 这个被校验过的入口。里面的 p_delta 上限校验是第二道防线:即便函数被高频调用,单次加分也封顶,刷分收益有限。

下面一张表区分"哪些游戏子系统该放 Supabase、哪些该接专用服务",避免你一股脑全塞进数据库:

游戏子系统 放 Supabase? 理由
玩家档案 / 好友关系 关系模型强项,RLS 控权限
排行榜 / 成就 索引或物化视图够快
关卡存档 低频写、按用户隔离
回合制对战状态 可(broadcast) 秒级同步,Realtime 胜任
帧级动作同步 要 UDP、毫秒级、专用服务器
匹配撮合 需要内存态 + 低延迟调度

⚠️ revoke update 后别忘了保留"读"和"插入存档"的权限给玩家,否则正常功能也被锁死。权限收紧要精确到列和角色,先列清楚"玩家自己能做什么",再 revoke 多余的那一项,别图省事整表锁掉。

💡 关键直觉:游戏的信任边界不该画在"客户端是否诚实",而要画在"数据库入口是否可被绕过"。把数值变更收进受信函数、列权限收回,等于把作弊成本从"改前端"提高到"攻破数据库",绝大多数情况下这就足够拦住刷分。

本节要点回顾

  • 玩家档案、好友、排行榜、存档都适合 Supabase 的关系模型。
  • 排行榜靠分数索引或物化视图保持快。
  • 帧级对战要专用服务,战绩回写 Supabase。

⚠️ 别让客户端直接 update coins,等于把分数修改权交给玩家。涉及数值变更用受信函数(服务端/Edge Function 调)封装,客户端只发"事件"不写结果。

💡 我们建议游戏的"数据平面"全交给 Supabase,"实时战斗平面"交给专用服务,两者通过战绩回写衔接。这样你不用为维护玩家数据库分心,专注玩法。

下一节我们剖几个真实成功案例,看前面所有知识点怎么在完整产品里组装落地。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U