ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

OmniGame 技术白皮书:从零依赖到 WebRTC P2P,重新定义网页小游戏的工程上限

OmniGame 技术白皮书:从零依赖到 WebRTC P2P,重新定义网页小游戏的工程上限 OmniGame 技术白皮书从零依赖到 WebRTC P2P重新定义网页小游戏的工程上限摸鱼无界 · 对决瞬发当 100 款游戏装进浏览器当 WebRTC 把局域网延迟压到 5ms 以内当 Shadow DOM 让每一款游戏互不干扰——这就是 OmniGame。一、为什么我们要做 OmniGame1.1 传统网页小游戏的三大痛点在浏览器游戏这件事上行业已经走了二十年但绝大多数产品依然停留在打开一个网页、加载一堆广告、玩三十秒就弹窗的初级阶段。我们在调研了近百款网页小游戏平台后总结出三个根深蒂固的痛点第一加载慢、体积重、广告多。一个所谓的秒开小游戏首屏往往要加载 2-5MB 的 JS 包、几十个广告 SDK、一堆第三方追踪脚本。用户点进去广告还没播完耐心已经耗光了。更不用说那些挂着免费名头、实际每 30 秒弹一次充值窗口的产品——这根本不是游戏是广告载体。第二单机为主、联机缺失。大多数网页小游戏只能单人玩想和朋友联机要么下载客户端要么加微信群传文件。WebRTC 技术出来都快十年了但真正把 P2P 联机做好的网页游戏平台屈指可数。局域网对战这种最自然的场景——两个人坐在同一间办公室、连同一个 Wi-Fi——反而被所有平台忽略了。第三污染环境、容易暴露。上班族玩网页游戏最大的风险不是被老板看到而是游戏的 CSS 污染了整个网页、游戏的弹窗跳不出来、游戏的 JS 报错把整个页面搞崩。更糟的是很多游戏平台为了追流量会在页面里插一堆悬浮广告、自动弹窗、甚至后台下载——这在办公环境里简直是灾难。1.2 OmniGame 的技术哲学基于这三个痛点我们确定了 OmniGame 的三条技术铁律100% 离线优先零外部 CDN 依赖、零网络追踪、零广告 SDK。所有游戏资源打包进浏览器扩展断网也能玩。P2P 联机原生支持WebRTC DataChannel 直连不经过我们的服务器延迟最低可以压到 1ms 以内。物理隔离沙盒每一款游戏都跑在独立的 Shadow DOM 里CSS 不污染、JS 不泄露、崩溃不影响主页面。这三条原则听起来简单但要全部落地需要解决一大堆工程问题。接下来我们逐层拆解。二、整体技术架构2.1 架构分层OmniGame 的整体架构可以分为四层┌─────────────────────────────────────────────────┐ │ 应用层 │ 官网 / 扩展商店页 / Web 在线大厅 │ ├─────────────────────────────────────────────────┤ │ 引擎层 │ 游戏运行时 / 联机引擎 / 沙盒管理器 │ ├─────────────────────────────────────────────────┤ │ 数据层 │ 本地持久化 / Elo 天梯 / 云存档同步 │ ├─────────────────────────────────────────────────┤ │ 基础设施│ WebRTC / Shadow DOM / Web Audio / P2P │ └─────────────────────────────────────────────────┘应用层负责用户入口。目前我们有三个入口官方网站Next.js 16 构建、Chrome 扩展商店版Manifest V3、以及即将上线的 Web 在线大厅免装插件直接在浏览器开玩。引擎层是核心。里面有三个子系统游戏运行时负责把每一款游戏加载到独立的 Shadow DOM 沙盒里联机引擎封装了 WebRTC DataChannel 的所有底层细节对外暴露简单的创建房间 / 加入房间API沙盒管理器管理所有游戏实例的生命周期包括创建、销毁、聚焦、隐藏数据层负责持久化。本地用chrome.storage.local加localStorage双轨存储保证在扩展和普通网页两种环境下都能正常读写。天梯分数、游戏进度、设置项全部本地保存等云存档功能上线后再增量同步到服务器。基础设施层就是浏览器原生能力。我们尽量不引入第三方库——WebRTC 用浏览器原生的RTCPeerConnection音频用 Web Audio API 直接合成DOM 隔离用 Shadow DOM——这样整个产品的依赖体积可以压到极小。2.2 技术选型背后的思考很多人会问为什么用 Next.js 16为什么用 Tailwind CSS 4为什么用 Framer Motion答案很简单我们要的是快和小。Next.js 16 (Turbopack)相比 WebpackTurbopack 的冷启动速度快 10 倍以上热更新快 100 倍。对于我们这种需要频繁迭代 100 款游戏元数据的项目开发体验的提升是决定性的。Tailwind CSS 4原子化 CSS 的好处是生产环境最终只打包用到的 class我们官网最终的 CSS 体积不到 20KB。比起传统的 CSS-in-JS 方案性能好一个数量级。Framer Motion动画性能最好的 React 动画库没有之一。我们官网的所有过渡效果——悬浮球拖拽、弹窗动效、页面切换——都是用 Framer Motion 做的60 帧不掉帧。三、核心技术一WebRTC P2P 联机引擎3.1 为什么选 WebRTC而不是 WebSocket很多人第一反应是联机游戏用 WebSocket 不就行了干嘛要上 WebRTC答案是延迟和成本。WebSocket 的架构是这样的玩家 A ←→ 中转服务器 ←→ 玩家 B所有数据都要经过我们的服务器中转。如果两个人在同一个办公室、连同一个 Wi-Fi数据包也要绕一圈公网再回来——延迟至少 30-50ms还浪费服务器带宽。WebRTC 的架构是这样的玩家 A ←→ P2P 直连 ←→ 玩家 B一旦连接建立数据直接在两台设备之间传输不经过任何服务器。局域网内延迟可以压到 1-5ms跨网直连通过 STUN 打洞通常也在 50ms 以内。而且因为不经过我们的服务器带宽成本是零——用户越多我们越省钱。当然WebRTC 不是没有缺点。它的 API 非常底层ICE 候选收集、STUN/TURN 服务器配置、SDP 协商、DataChannel 配置……一堆细节要处理。我们做的事情就是把这些底层细节全部封装起来给上层游戏开发者暴露一个极其简单的 API// 创建房间constroomawaitOmniNet.createRoom({gameId:gravity-4,maxPlayers:2,});// 加入房间constroomawaitOmniNet.joinRoom(123456);// 发送消息room.send({type:move,x:3,y:5});// 监听消息room.on(message,(msg){console.log(收到:,msg);});3.2 局域网雷达最被低估的功能OmniGame 有一个杀手级功能局域网雷达。你在办公室打开 OmniGame同一个 Wi-Fi 下的所有朋友会自动出现在你的雷达列表里——不需要加好友、不需要输 IP、不需要扫码。点一下就能对战。这个功能听起来简单实现起来要解决三个问题第一怎么发现同 Wi-Fi 下的其他设备答案是mDNS UDP 广播。每台设备启动后会在 239.255.255.250:1900 端口广播自己的存在格式是 SSDP 协议。同一局域网内的其他设备收到广播后就知道附近有一个 OmniGame 实例。Chrome 扩展环境里我们直接用chrome.mdnsAPI需要 permissions 声明普通网页环境里我们用 WebRTC 的 ICE 候选收集机制——因为 STUN 请求会在局域网内广播我们可以从 ICE 候选地址里提取出局域网内的其他设备 IP。第二发现之后怎么建立连接发现只是第一步真正建立连接还是要走 WebRTC 流程。但因为我们已经知道对方的局域网 IP 了ICE 打洞的成功率几乎是 100%——不需要经过公网 STUN 服务器直接走局域网内的 host candidate连接速度从几秒降到几百毫秒。第三怎么保护用户隐私很多人担心我打开 OmniGame隔壁同事的电脑就能看到我答案是默认隐身。雷达发现功能默认关闭用户需要手动开启可被发现模式。而且每一次被发现都会在 UI 上提示——用户完全知道自己正在被谁看到。我们不会偷偷收集任何局域网内的设备信息。3.3 6 位房间密钥跨网直连不在同一个局域网怎么办比如你在家、朋友在公司怎么联机我们设计了6 位数字房间密钥。房主创建房间后会得到一个 6 位数字比如 123456把这个数字发给朋友朋友输入就能加入。背后的原理是房主创建房间后我们的信令服务器非常轻量只做消息转发不传输游戏数据会生成一个 6 位房间号房主和信令服务器建立 WebSocket 连接等待加入朋友输入 6 位房间号信令服务器把两个人的 SDP Offer/Answer 互相转发SDP 交换完成后WebRTC 打洞建立 P2P 直连之后游戏数据不再经过信令服务器整个过程对用户完全透明。你只需要输入 6 位数字剩下的连接、打洞、加密全部自动完成。值得一提的是因为游戏数据走的是 WebRTC 加密通道DTLS-SRTP我们的信令服务器根本看不到任何游戏数据——它只做最基本的信令转发。从隐私角度来说这是最安全的架构。四、核心技术二Shadow DOM 沙盒系统4.1 为什么游戏需要沙盒想象一下这个场景你在 OmniGame 里打开了 2048玩了一会想换成俄罗斯方块。如果两个游戏的 CSS 都写了body { background: red; }那会发生什么——两个游戏的样式互相污染页面变得乱七八糟。更严重的是 JS 层面的冲突。如果游戏 A 写了window.gameState {...}游戏 B 也写了window.gameState {...}那 B 直接把 A 的状态覆盖了。传统的解决方法是用 iframe。但 iframe 的问题太多了性能开销大每开一个 iframe 就相当于新开一个浏览器进程通信麻烦postMessage 各种序列化反序列化样式隔离是隔离了但 DOM 操作起来很别扭我们用的是Shadow DOM。4.2 Shadow DOM 是什么Shadow DOM 是浏览器原生的组件隔离机制。它允许你在一个普通的 DOM 元素下挂载一个隐藏的 DOM 子树——这个子树对外界是完全隔离的外面的 CSS 选择器选不到 Shadow DOM 里面的元素Shadow DOM 里面写的 CSS 也不会泄漏到外面外面的 JS 可以操作 Shadow DOM但需要通过shadowRoot接口最关键的是Shadow DOM 是原生 DOM 的一部分不是 iframe所以没有进程开销。开 100 个 Shadow DOM 游戏实例性能和开一个差不多。4.3 我们的沙盒管理器我们实现了一个SandboxManager负责所有游戏实例的生命周期管理classSandboxManager{privatesandboxesnewMapstring,ShadowRoot();// 创建一个新的游戏沙盒create(gameId:string,container:HTMLElement):ShadowRoot{consthostdocument.createElement(div);host.style.positionabsolute;host.style.width100%;host.style.height100%;constshadowhost.attachShadow({mode:closed});container.appendChild(host);this.sandboxes.set(gameId,shadow);returnshadow;}// 加载游戏 HTML 到沙盒里loadGame(gameId:string,html:string){constshadowthis.sandboxes.get(gameId);shadow.innerHTMLhtml;}// 销毁沙盒destroy(gameId:string){constshadowthis.sandboxes.get(gameId);shadow.host.remove();this.sandboxes.delete(gameId);}}这里有一个关键设计选择mode: closed。为什么用 closed 而不是 open因为我们要保证游戏之间的完全隔离——游戏 A 的 JS 不应该能拿到游戏 B 的 shadowRoot更不应该能操作 B 的 DOM。closed 模式下只有创建这个 shadowRoot 的人能拿到引用外面完全访问不到。4.4 沙盒带来的额外好处除了样式和脚本隔离Shadow DOM 还给我们带来了几个意想不到的好处第一浏览器扩展天然兼容。Chrome 扩展的 Content Script 环境里页面的 CSS 和扩展的 CSS 是互相隔离的。但如果我们用 Shadow DOM 做游戏沙盒扩展注入的游戏和原生网页完全不冲突——甚至你可以在任何网站上打开 OmniGame 的悬浮球游戏永远在你的 Shadow DOM 里不会污染网页。第二崩溃隔离。如果某一款游戏的 JS 出了 bug最多就是这个游戏的 Shadow DOM 里白屏不会影响 OmniGame 的主界面更不会影响其他游戏。用户只要关掉这个游戏的沙盒实例一切照常。第三样式完全可控。因为 Shadow DOM 里的 CSS 是完全独立的我们可以给每一款游戏注入统一的基础样式——重置 CSS、设置字体、配置主题色——而不用担心影响外面。五、核心技术三100% 离线优先架构5.1 为什么要做离线优先很多人不理解现在谁还会断网为什么要花力气做离线优先因为我们的目标用户是上班族。上班族的网络环境是什么样的——公司 Wi-Fi 不稳定、开会要断网、出差在飞机上、甚至老板走过来的时候你要秒切页面。如果游戏必须联网才能玩那这个产品的使用场景就砍掉了 80%。更重要的是离线优先意味着更快。一个需要联网的游戏首屏要等服务器响应、要下载资源、要建立 WebSocket 连接——用户至少要等 2-5 秒。一个离线优先的游戏所有资源都已经打包在本地了点击就开0 秒等待。5.2 资源打包策略OmniGame 的所有游戏资源——HTML、JS、CSS、图片——全部打包在浏览器扩展里。用户安装扩展的那一刻100 款游戏就已经全部下载到本地了。这听起来体积会很大其实不会。我们做了严格的体积控制每一款游戏的平均大小不到 50KB100 款游戏加起来不到 5MB加上引擎层、UI 层、联机模块整个扩展的最终体积不到 8MB对比一下一个微信小程序包都要 2MB一个普通的网页游戏平台首页就 5MB。我们这已经是极致优化了。我们的优化手段包括游戏 HTML 内联每一款游戏的 HTML/JS/CSS 全部内联成一个字符串不发任何额外的 HTTP 请求图片全部用内联 SVG没有 PNG/JPG所有图标、游戏素材都是 SVG 矢量图体积小、不失真Tree Shaking构建的时候自动删掉游戏里没用到的代码——很多经典游戏比如贪吃蛇的原始代码有 10KB我们最终打包下来不到 3KB5.3 本地持久化双轨制游戏进度存在哪我们做了双轨方案Chrome 扩展环境用chrome.storage.local容量大5MB 起步、同步可靠普通网页环境用localStorage容量小5MB、但所有浏览器都支持上层 API 完全一致底层自动根据环境切换。这样同一份游戏代码在扩展和网页里都能正常保存进度。天梯分数、最高分、游戏设置这些关键数据我们还做了增量备份——每次保存的时候同时写两份到不同的存储里。哪怕其中一个存储出问题还有另一份备份。六、自研游戏代表作深度解析6.1 重力方阵Gravitas-4重新发明连珠棋重力方阵是我们的旗舰自研作品。表面上它是一个四子棋但实际上我们加了一个核心机制棋盘可以旋转。普通四子棋的规则是棋子从上方落下沉到最底下先连成四个的赢。重力方阵在这个基础上加了一层每回合你除了落子还可以选择把整个棋盘顺时针旋转 90°。旋转的那一刻所有棋子会因为重力重新坍塌——可能原本不相连的棋子旋转后突然就凑成了四连绝杀。也可能你精心布置的防线被人一转直接破掉。这个机制的技术难点在哪第一坍塌物理模拟。旋转棋盘之后所有棋子要逐列计算新的位置——每一列的棋子都要掉到最底下中间不能有空隙。这个计算看起来简单但要做到 60 帧流畅旋转效果需要在 16ms 内完成整个棋盘的重排、补间动画、碰撞检测。我们的做法是旋转的 300ms 动画期间不真的移动棋子——只是用 CSS transform 把整个棋盘旋转 90°。动画结束的那一刻再瞬间把棋子重置到新的重力位置。用户视觉上感觉是旋转之后棋子哗啦一下掉到底但实际上整个过程是 GPU 加速的完全没有卡顿。第二联机状态同步。旋转棋盘是一个全局状态变更两个人看到的棋盘必须完全一致。我们的做法是旋转操作由房主发起房主计算好新的棋盘状态然后把整个新状态广播给所有玩家。这样不管网络延迟多少所有人看到的棋盘永远是一致的。6.2 方块死斗双人消行对轰方块死斗是俄罗斯方块的双人对战版。你消行就给对方加垃圾行对方消行垃圾行就砸到你这边。谁先堆到顶谁输。这个游戏的技术难点是实时性。俄罗斯方块这种游戏晚 100ms 就可能死人。我们用 WebRTC DataChannel 的 unreliable 模式不可靠传输、不保证顺序、但延迟最低把每一次的落子、旋转、消行事件实时同步给对方。因为局域网内延迟只有 2-5ms两个人的操作几乎是同时的——就像在同一个机器上玩一样。6.3 暗箱轮盘心理博弈的 AI 对手暗箱轮盘是一个恶魔轮盘风格的心理战游戏。左轮手枪里装了若干发子弹两个人轮流开枪——要么自己中弹要么把枪转给对方。你要根据前面的轮次推算出剩下的子弹数量做出最优决策。这个游戏最有意思的地方是 AI 对手。我们实现了一个简化版的蒙特卡洛树搜索 AI每一个决策点AI 都会模拟接下来的所有可能路径计算出胜率最高的选择。在困难模式下AI 的胜率大概在 82% 左右——普通人想赢它非常难。七、摸鱼场景的工程设计7.1 悬浮球网页上的常驻入口OmniGame 的一个核心交互是网页悬浮球。你在任何网站上右下角都会有一个小小的游戏按钮点一下就能呼出 OmniGame 小窗直接玩游戏。这个功能听起来简单实现起来要解决一堆问题第一不能污染网页。悬浮球的 DOM 必须挂在 Shadow DOM 里不能影响网页本身的 DOM 结构。而且悬浮球的样式要适应各种网页——深色网页上用浅色悬浮球浅色网页上用深色悬浮球自动反色。第二不能被网页屏蔽。很多网站会有自己的悬浮按钮、弹窗、广告。我们的悬浮球要能和这些元素共存不能被网页的 JS 删掉也不能被网页的 CSS 盖住。我们的做法是悬浮球的 z-index 设到 2147483647浏览器允许的最大值并且每 5 秒检查一次自己有没有被意外移除——如果被删了立刻重新挂载。第三拖拽要流畅。悬浮球可以拖到屏幕的任何位置。我们用 Framer Motion 的drag实现支持惯性拖拽、边缘吸附——拖到屏幕边缘的时候悬浮球会自动贴到边上不会挡着网页内容。7.2 老板键毫秒级伪装老板走过来的时候按一个键OmniGame 瞬间消失——屏幕变成一张看起来非常专业的分布式系统堆栈排查终端满屏的日志滚动看起来就像你在认真排查生产环境问题。再按一下游戏瞬间恢复刚才的进度一分不少。这个功能的技术核心是状态保存与恢复。按老板键的那一刻我们把所有游戏实例的状态序列化保存到内存里然后把悬浮球和游戏窗口全部隐藏弹出伪装终端。按第二次的时候再把状态恢复回来。整个过程在 10ms 以内完成——比你眨眼睛还快。伪装终端也不是随便做的——我们真的写了一套模拟的生产日志生成器输出的日志格式和真实的 Kubernetes 集群日志一模一样Pod 名称、时间戳、错误级别、堆栈信息……老板站你旁边看三秒绝对看不出这是假的。八、Web Audio 原生音效系统8.1 为什么不用音频文件一般的网页游戏音效都是预先录好的 MP3/WAV 文件打包进项目里。这样做的问题是体积大一个简单的点击音效都要几十 KB加载慢要发 HTTP 请求下载还要解码风格不统一不同音效可能录的时候参数不一样混在一起听着很怪我们用的是Web Audio API 实时合成。所有音效都是代码生成的——悬浮的滴声、点击的咔哒声、胜利的叮咚声、老板键的唰声——全部是用振荡器 增益节点实时合成的。好处太多了零体积整个音效系统加起来不到 2KB 代码零加载打开就有声音不用等可调参数想要更尖一点的声音改一下振荡器频率就行。想要更闷一点加个低通滤波器完全无版权风险所有声音都是我们自己合成的不存在任何版权问题8.2 音效合成的几个例子最简单的悬浮音效Hover Blip一个正弦波振荡器频率从 800Hz 快速滑到 1200Hz持续 50ms音量从 0 渐变到 0.1 再降到 0——就是那种很轻很清脆的滴一声。胜利音效Bonus Chime三个音符依次播放C5 → E5 → G5每个音符持续 200ms叠加一点混响——听着就有奖励的感觉。老板键音效Shutdown Sweep一个白噪声经过低通滤波器频率从 2000Hz 快速降到 100Hz持续 150ms——就是那种唰一下切断的感觉非常有紧迫感。九、部署与运维架构9.1 官网部署栈OmniGame 官网本身是一个 Next.js 静态生成站点。我们的部署架构是用户 ←→ Cloudflare CDN ←→ Nginx 反向代理 ←→ Next.js (pm2 进程)CloudflareCDN 加速 DDoS 防护 SSL 终结。所有静态资源JS/CSS/图片全部缓存在 Cloudflare 的全球节点用户访问的时候就近取延迟最低可以压到 20ms 以内。Nginx反向代理 负载均衡。负责把 80/443 端口的请求转发到 Next.js 的 8098 端口。PM2进程管理。保证 Next.js 进程崩溃了能自动重启服务器重启了能自动启动。整个栈的成本非常低一台最低配的云服务器1核2G就能跑加上 Cloudflare 免费版每个月服务器成本不到 50 块钱。9.2 信令服务器轻到极致WebRTC 联机需要一个信令服务器做 SDP 交换。我们的信令服务器有多轻用 Node.js 写的总共不到 200 行代码只做一件事把 WebSocket 收到的消息转发给同一个房间的其他玩家不存储任何用户数据、不记录任何游戏内容、不做任何鉴权一台 1 核 512MB 的小机器就能同时支撑几万个房间因为游戏数据完全走 P2P信令服务器只是个传话筒——哪怕信令服务器挂了已经建立的 P2P 连接完全不受影响游戏继续玩。9.3 监控与可观测性我们没有上复杂的监控系统——对这个量级的产品来说太重了。我们用的是最简单的方案PM2 自带的日志所有进程 stdout/stderr 都自动落盘Nginx access log所有请求的状态码、响应时间、User-Agent 自动记录Cloudflare Analytics流量、攻击、缓存命中率全部在 Cloudflare 面板里看出问题的时候先看 Cloudflare 面板——如果是全局错误那就是源服务器挂了如果只有部分用户出问题那就是他们本地网络的问题。90% 的问题都能在 1 分钟内定位。十、路线图从扩展到开放平台OmniGame 现在只是第一步。我们的完整路线图分四个阶段Phase 1浏览器扩展基石已完成100 款离线游戏局域网雷达 6 位房间直连悬浮球 老板键Elo 天梯本地持久化这是目前的版本。所有核心功能已经跑通用户可以离线玩、可以局域网联机、可以摸鱼。Phase 2Web 在线大厅进行中免装插件打开网页就能玩全网房间匹配不只是局域网全互联网的用户都能匹配到一起在线排行榜所有人的天梯分数实时排名这一步要解决的问题是WebRTC 在普通网页环境下打洞成功率不如扩展环境。很多运营商的 NAT 类型比较严格P2P 打洞失败的话就要走 TURN 中继服务器。我们正在部署全球节点的 TURN 服务器保证打洞失败的用户也能正常联机——只是延迟会高一点经过中继但至少能玩。Phase 3云存档与跨设备同步游戏进度、天梯分数跨设备同步手机、电脑、平板随时接着玩赛季制天梯每个月一个赛季段位重置排行榜清零到这一步OmniGame 就从一个摸鱼工具变成了一个真正的游戏平台。你在公司电脑上玩到一半回家打开手机进度一模一样。Phase 4创作者工坊与开放 SDK开放游戏开发 SDK任何人都可以给 OmniGame 开发新游戏创作者可以上传自己的游戏其他用户可以下载玩最终的目标是OmniGame 不只是我们做的 100 款游戏而是一个开放的生态——成千上万的开发者在上面做游戏用户永远有新东西玩。十一、技术选型的得与失做了这么久我们也踩了不少坑。诚实地总结一下技术选型的得失11.1 选对了的Shadow DOM 沙盒这是我们最正确的一个决定。它带来的隔离性、性能、兼容性比 iframe 方案好太多了。没有它我们根本不可能在一个页面里跑 100 款游戏而不崩。WebRTC P2P 架构不仅省钱而且体验好。局域网 5ms 延迟是任何 WebSocket 方案都做不到的。用户第一次在办公室用雷达秒连对战的时候那种怎么可能这么快的惊讶就是我们产品的核心竞争力。零第三方依赖整个产品除了 React 和 Next.js几乎没有引入其他第三方库。音效是自己合成的沙盒是自己写的联机是自己封装的。好处是体积小、bug 少、版本升级不用追着第三方库跑。11.2 踩过的坑Chrome 扩展 Manifest V3 的限制V3 对 Content Script 的权限收得很紧很多 V2 时代能用的 API 现在要额外申请权限。比如 mDNS 发现功能我们申请了三个月才过审。未来如果 V4 再收紧可能要做更多的兼容工作。WebRTC 调试的痛苦WebRTC 的问题是有的用户能连上有的用户连不上——而且你根本复现不了。我们花了大量时间写日志、抓包、分析各种 NAT 类型才把打洞成功率从 60% 提升到 95%。剩下的 5% 是极端网络环境只能靠 TURN 中继兜底。离线优先的同步问题所有数据先写本地等联网了再同步——听起来简单但冲突怎么处理比如你离线的时候玩了两局赢了 50 分同时你的朋友在线上也用你的账号打了一局输了 30 分——这时候云端和本地的数据就冲突了。我们现在用的是本地优先策略本地的分数永远比云端新同步的时候直接覆盖云端。简单粗暴但对这个量级的用户来说够用了。十二、结语技术是为体验服务的做 OmniGame 的过程中我们越来越坚信一个道理技术不是用来炫技的是用来解决问题的。WebRTC 不是因为它酷才用的——是因为它能把联机延迟压到 5ms让用户在办公室里秒连同事对战这个体验是 WebSocket 永远做不到的。Shadow DOM 不是因为它新才用的——是因为 100 款游戏装在一个页面里没有沙盒根本跑不起来样式和脚本全乱套。离线优先不是因为它高级才做的——是因为上班族就是会在地铁上、在飞机上、在断网的会议室里想玩游戏你必须让他打开就有。我们做的所有技术选择最终都指向同一个目标让用户玩得爽。不是我们用了什么技术栈不是我们的架构有多先进而是——用户点开 OmniGame 的那一刻游戏立刻就开了同事坐在隔壁点一下雷达里的名字就开连老板走过来按一下键就消失。这才是技术真正的价值。OmniGame 还很早离我们想象中的样子还差很远。但我们很高兴——我们已经把最硬的骨头啃下来了联机、沙盒、离线、摸鱼场景。接下来就是把更多游戏做出来、把联机体验做得更顺、把开放平台搭起来。欢迎你和我们一起把这个产品做下去。OmniGame摸鱼无界 · 对决瞬发100 款离线游戏 · WebRTC P2P 联机 · 零广告零追踪
返回列表