这几年“用 AI 写代码”这句话已经被说烂了。有人拿它生成小工具有人拿它做网站还有人拿它卷算法题。真正少见的是有人把“开放世界、多人联机、浏览器访问”三个词放到同一个项目里并且真的把它跑了起来。aethercraft 本质上就是这样一个实验项目。从项目描述看它的进度标注非常坦率完成度 10%基础架构搭建成功网络联机效果佳直接浏览器就能访问。很多人看到“完成度 10%”可能会直接划走但这里真正值得琢磨的不是百分比本身而是它敢在这个阶段先把网络链路跑通。先把判断放在前面这类项目最重要的价值不在于“AI 写出了多少行代码”而在于它验证了一条低成本、高反馈的 AI 原生开发路线——先用很少的人力和时间搭出可运行骨架再把最难的联机链路打通最后用 AI 一点一点往里面填内容。也就是说它把传统游戏开发中“先做大量底层再看到效果”的节奏改成了“先立骨架、再跑链路、最后填内容”。本文会把这个项目拆开来看一个完成度只有 10% 的项目背后到底有哪些完整的技术模块浏览器端多人同步是怎么实现的如果你也想用 AI 辅助编程复现类似流程应该从哪里切入、怎么验证、怎么避坑。这篇文章不是教你如何复刻 aethercraft因为从公开材料能获取到的项目细节是有限的。我更想做的是把它当作一个典型样本把开放世界项目在原型阶段最关键的架构问题、联机问题和工程问题讲清楚。读完之后你会知道“AI 辅助编程做开放世界原型”这件事哪些环节真的省力哪些环节依然要靠人来判断。1. 一个完成度 10% 的项目为什么值得拆开看如果你只看“完成度 10%”这一行很容易产生一个误解这项目还早得很没什么参考价值。但如果你把另外两句话连在一起看——“基础架构搭建成功网络联机效果佳”——就会意识到一个关键事实项目方把最难的骨头先啃下来了。做一个开放世界类项目难度从来不在“画一个地图”或者“放置几个角色”。真正的难点在于状态的一致性和同步。比如当玩家 A 在地图的某个角落移动了一步玩家 B 的浏览器需要多久看到这次移动看到的位置是否准确如果两个玩家同时操作同一个物体以谁的状态为准这些问题如果不先在架构层面解决后面内容越多返工成本越大。aethercraft 在 10% 完成度的时候就把网络联机链路跑通这说明它的架构设计大概率是“网络优先”的而不是“先做单机再考虑联机”。对于 AI 辅助编程来说这是一个非常聪明的策略因为网络模块恰恰是 AI 生成的代码里最容易被忽略、也最容易出问题的部分。很多开发者让 AI 生成游戏界面和角色移动代码时AI 通常只会写出面向单机的代码不会主动考虑“另一个浏览器上的玩家也要看到同样的世界”。能够先解决联机说明项目在一开始就对 AI 的产出了严格的约束。另一个值得拆开看的点是“直接浏览器就可以访问”。这句话听起来很简单但它背后包含了一个技术选型判断项目选择了零安装的 Web 路线而不是传统的客户端安装包路线。这个选择极大地降低了测试和分发的成本。你不需要让测试人员安装游戏引擎、配置运行环境只需要打开浏览器输入网址就能进入同一个世界。所以一个完成度 10% 的项目如果它已经把架构、网络、渲染入口这三件事跑通了那它在工程层面的完成度其实远超数字表面传递的信息。剩下的大部分工作反而是更耗时、但技术上更单一的内容填充。2. 项目定位开放世界 浏览器 联机三者叠加意味着什么aethercraft 的三个关键词组合在一起并不只是一个宣传文案它们各自对应着不同的技术约束。“开放世界”意味着项目需要承载一个持续存在的虚拟空间。这个空间里的物体、角色、状态不能只存在于某个玩家的浏览器内存里它需要一个可以被多人共享的权威状态层。哪怕现在的开放世界还很小哪怕只有 10% 的内容这个“持续存在”的性质决定了它不能做成纯单机页面。“浏览器”决定了运行环境的边界。浏览器能做什么、不能做什么直接影响技术方案的选型。比如浏览器里的 3D 渲染通常走 WebGL 或 WebGPU资源加载必须考虑网络带宽和按需加载策略内存和 CPU 受到浏览器沙箱限制。好处是你不用写安装包坏处是你必须更早地考虑性能问题。“联机”则进一步把问题从单机渲染推向了网络同步。在一局传统的单机游戏里世界状态是本地维护的角色走到哪里状态就在哪里。但在一款多人联机的开放世界里每个玩家都有自己的“本地视角”同时又要遵守一个“共同世界”的规则。这个共同世界存在于哪里、由谁说了算就是一个核心架构问题。把这三个词放在一起可以得出一个明确的判断aethercraft 本质上是在验证“Web 端多人开放世界是否具备低成本可行性”。传统开放世界游戏之所以开发成本高是因为渲染、物理、AI、网络、资源管理都要达到商业级标准。但作为原型验证浏览器端方案在开发成本上确实有明显优势——不需要游戏引擎的深度 C 开发经验不需要制作庞大的客户端安装包不需要处理各种显卡驱动兼容问题。如果你只是想验证一个玩法和机制Web 路线足够快。从项目目前的状态看它选择了一条务实路径先用浏览器把入口降到最低用 WebSocket 类协议把联机链路跑通然后再逐块丰富世界内容。这个路径的优点是每次改动都能立刻被验证——改动服务端代码所有在线玩家刷新页面就能拿到新版本。缺点是需要克制“想把所有功能一次做完”的冲动因为浏览器端的性能瓶颈和状态管理复杂度会随着内容增加而成倍放大。3. 基础架构拆解一个可多人访问的开放世界骨架包含哪些模块从 aethercraft 现有状态来看即便完成度只有 10%一个可多人访问的开放世界原型也必须具备一套清晰的分层架构。这套架构不会因为内容少而消失它只是用最少的内容把整条链路贯穿起来。下面按模块拆解这个骨架。3.1 核心模块场景容器模块负责承载地图、物体、玩家对象。在浏览器端这一层通常是 Canvas2D、WebGL 或 Three.js 等渲染库的封装。它需要解决“场景里有什么”和“场景怎么画出来”两个问题。输入处理模块负责采集键盘、鼠标、触摸事件把它们转换成玩家的“操作意图”。比如你按了方向键这个模块会把它变成“我想向左移动”的意图而不是直接修改角色坐标。网络通信模块负责浏览器与服务器之间的消息收发。最常见的底层协议是 WebSocket因为它是浏览器原生支持的长连接协议适合高频双向通信。状态权威模块负责维护“世界当前的真实状态”。这通常放在服务端因为只有服务端才能对多个玩家的操作进行仲裁防止某个客户端乱改数据。这个模块可以是一个 Node.js 进程也可以是无状态的微服务对接 Redis 或数据库。广播与同步模块负责把服务端状态变化推送给所有相关玩家。最简单的模型是全量广播复杂一点的模型会按场景、按视野范围做分区广播减少带宽消耗。持久化模块负责存档、场景数据、玩家数据的落盘。在原型阶段可以先用数据库或 JSON 文件保存后期再迁移到更完整的存储方案。生命周期与房间管理模块负责处理玩家加入、离开、断线重连、房间创建与销毁。只要是一个多人项目这个模块就不是可选项。这七个模块并不需要全部做得很重。以 10% 的完成度来看只要“最小链路”跑通也就是玩家打开浏览器 → 连接到服务器 → 加入世界 → 看到其他玩家 → 自己移动 → 其他玩家也看到你动这个骨架就算立住了。aethercraft 能做到联机效果佳说明这条链路在它内部已经验证通过。3.2 数据流链路可以用一个非常朴素的方式来理解整个系统的数据流浏览器端Vue3 页面 / 场景渲染 / 输入监听 → WebSocket 发送操作意图 → 服务端状态权威模块修改世界状态 → 服务端广播新状态 → 浏览器端收到消息 → 渲染更新。这条链路里最容易出问题的环节不是“发送”而是“仲裁”。如果每个浏览器都直接修改自己的坐标再发给别人那就会出现位置不一致玩家 A 看到自己在 (100, 50)玩家 B 却看到他在 (98, 50)。真正的做法是客户端只发送“我要往哪走”的意图服务器决定最终的坐标再把坐标广播给所有人。这就是多人游戏同步里最基本的“服务器权威模型”。从项目的描述来推断aethercraft 的架构应该也是顺着这个思路走的。因为只有在服务器权威模型之下不同浏览器看到的“共同世界”才可能保持一致也才谈得上“联机效果佳”。如果你拿到这个项目源码建议第一时间看服务端是否集中管理了玩家坐标而不是在客户端各自为政。3.3 为什么这个架构能支撑低完成度跑通有一个常见的误区认为架构是大项目才需要的东西小原型随便写写就行。但多人联机项目恰恰相反——网络状态一旦确定后期改动协议的成本极高。aethercraft 在低完成度时先定架构本质上是把后期最贵的返工成本提前规避掉了。同时这套架构对 AI 辅助编程非常友好。因为每个模块边界清晰AI 生成代码时可以被限制在单一模块内不需要理解整个系统的所有细节。比如让 AI 写“场景容器模块的物体排序逻辑”它只需要看到这一层的接口和数据结构不需要知道网络协议怎么走。这正是 AI 辅助编程的正确用法模块越清晰AI 越不容易“自由发挥”。4. AI 辅助编程真正改变的是什么从“写代码”到“拆任务”很多人以为“AI 写代码”就是把需求丢给 AI让它生成一整套开放世界。这显然是不现实的。开放世界不是“一个功能”而是几十个系统组合在一起。AI 可以生成某一小块逻辑但很难一次性生成一个完整且可集成的项目。aethercraft 这类项目能走出来靠的不是“AI 一键生成游戏”而是一套正确的人机分工流程。传统开发的节奏是需求分析 → 架构设计 → 编码 → 测试 → 联调 → 上线的线性流程。进入 AI 辅助编程阶段后节奏变成需求拆解 → 给出上下文 → 生成代码 → 验证 → 再进入下一个任务。其中变化最大的环节是“需求拆解”。一个任务拆得够细AI 才能输出可用的代码拆得太粗AI 就会开始猜产生大量你需要返工的代码。4.1 用 AI 写代码的正确工作流第一步先确定验证标准。比如“两个浏览器能互相看到对方的移动”这比“实现多人系统”更具体、可测量。第二步把功能拆成能在 20 分钟内验证完成的原子任务。第三步把当前架构、涉及文件、数据结构、协议格式发给 AI让它在限定范围内输出代码。第四步集成并测试确认通过后再进入下一个任务。错误做法是直接对 AI 说“做一个开放世界”。 AI 会给你一个看似完整但根本无法扩展到多人的 demo。更好的做法是告诉它“当前系统已经用 WebSocket 广播玩家坐标请增加一个心跳包机制让服务端能够检测玩家掉线”。这样的任务AI 输出的代码八九不离十你只需要检查边界条件。4.2 一份适合发给 AI 的任务提示模板下面是一个可以直接套用的任务描述格式。以“给多人同步原型增加房间功能”为例当前项目Vue3 Node.js WebSocket 多人同步原型 改动目标在服务端增加房间概念支持创建房间、加入房间、离开房间。 约束 - 不改变现有 move / player-joined / player-left 消息格式 - 房间容量默认 8 人 - 房间数据只保存在内存不需要落库 - 输出完整可运行代码并给出验证步骤 验证标准两个浏览器打开同一页面后输入同一个房间号能互相看到对方移动。这个 prompt 里最关键的不是“请写代码”而是“约束”和“验证标准”。这两行决定了 AI 输出是接近可用的工程代码还是脱离上下文的玩具代码。你在让 AI 辅助编程时花费最多精力的应该是写这段拆解而不是纠结怎么改语法错误。从 aethercraft 的完成度来看它大概率已经把这个工作流跑进了日常开发。否则一个如此低完成度的项目很难在有限人力下既保证架构完整又同步推进网络联机、浏览器渲染和场景内容这些不同方向的任务。5. 浏览器端多人同步的入门实现从一次人物移动开始如果你想验证“浏览器端多人开放世界”的可行性不需要一上来就做复杂的地图和渲染。一个最小可运行的示例就是让两个浏览器里的方块能够互相看到彼此的移动。这个示例跑通之后你已经掌握了开放世界联机原型里最核心的状态同步逻辑。下面给出一个基于 Node.js 和原生 JavaScript 的完整示例。这里不引入 Vue3 或 Three.js是为了把协议层逻辑单独剥离出来让你看清同步原理。理解了这段逻辑再回到 aethercraft 那种完整项目你会更容易读懂它的网络模块。5.1 服务端权威状态与广播创建项目目录并安装唯一需要的依赖 wsmkdir sync-demo cd sync-demo npm init -y npm install ws然后创建server.js// 文件路径sync-demo/server.js const { WebSocketServer } require(ws); const wss new WebSocketServer({ port: 8080, host: 0.0.0.0 }); // 玩家状态表key 是玩家 idvalue 是坐标和场景信息 const players new Map(); function broadcast(message) { const data JSON.stringify(message); for (const client of wss.clients) { if (client.readyState client.OPEN) { client.send(data); } } } wss.on(connection, (ws) { const connectionPlayerId { id: }; ws.on(message, (raw) { const msg JSON.parse(raw.toString()); if (msg.type join) { connectionPlayerId.id msg.id; players.set(connectionPlayerId.id, { x: 0, y: 0, scene: tutorial }); broadcast({ type: player-joined, id: connectionPlayerId.id, players: Array.from(players.entries()).map(([id, p]) ({ id, ...p })) }); } if (msg.type move msg.id) { // 容错如果玩家还没 join先补一个初始状态 if (!players.has(msg.id)) { players.set(msg.id, { x: 0, y: 0, scene: tutorial }); } const player players.get(msg.id); player.x msg.x; player.y msg.y; player.scene msg.scene || player.scene; broadcast({ type: player-moved, id: msg.id, x: player.x, y: player.y, scene: player.scene }); } }); ws.on(close, () { if (connectionPlayerId.id players.delete(connectionPlayerId.id)) { broadcast({ type: player-left, id: connectionPlayerId.id }); } }); }); console.log(WebSocket server running on ws://localhost:8080);这段代码体现的是“服务器权威模型”。客户端发来的 move 消息只包含“我想到达的坐标”服务器收到后更新玩家状态然后重新广播给所有在线客户端。任何客户端都不直接修改服务器状态而是通过服务端做中转和仲裁。5.2 浏览器端发送意图与渲染状态创建index.html!-- 文件路径sync-demo/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 title多人移动同步 demo/title style .player { position: absolute; width: 20px; height: 20px; background: #3b82f6; border-radius: 4px; } .player.self { background: #ef4444; } /style /head body div idworld styleposition: relative; width: 800px; height: 600px; border: 1px solid #ccc;/div script src./client.js/script /body /html创建client.js// 文件路径sync-demo/client.js const world document.getElementById(world); const players new Map(); // 每次打开页面生成一个临时玩家 id真实项目中应由服务端分配 const playerId Math.random().toString(36).slice(2, 10); const ws new WebSocket(ws://localhost:8080); function render() { world.innerHTML ; for (const [id, p] of players) { const el document.createElement(div); el.className player (id playerId ? self : ); el.style.left p.x px; el.style.top p.y px; world.appendChild(el); } } ws.onopen () { ws.send(JSON.stringify({ type: join, id: playerId })); }; ws.onmessage (event) { const msg JSON.parse(event.data); if (msg.type player-joined) { for (const p of msg.players) { players.set(p.id, p); } } else if (msg.type player-moved) { players.set(msg.id, { x: msg.x, y: msg.y, scene: msg.scene }); } else if (msg.type player-left) { players.delete(msg.id); } render(); }; function sendMove(x, y) { ws.send(JSON.stringify({ type: move, id: playerId, x, y })); } // 监听方向键把“想到达的位置”发给服务器 document.addEventListener(keydown, (event) { const step 10; const current players.get(playerId) || { x: 0, y: 0 }; let dx 0; let dy 0; switch (event.key) { case ArrowUp: dy -step; break; case ArrowDown: dy step; break; case ArrowLeft: dx -step; break; case ArrowRight: dx step; break; default: return; } sendMove(current.x dx, current.y dy); });这段代码的关键点在于sendMove只发送“新坐标”然后等待服务器广播回来再由onmessage触发render()。如果你的页面看到的移动有延迟这是正常现象因为所有状态都以服务器为准。后续如果要优化手感可以引入“客户端预测”和“插值平滑”但那属于进阶话题原型阶段不需要。5.3 运行与预期效果在项目目录启动服务端node server.js然后用两个浏览器窗口同时打开index.html。你会看到两个不同颜色的方块出现在同一个页面区域中。当你用方向键移动红色方块时蓝色方块能看到它同步移动。现在把其中一个页面关掉服务器会把player-left广播给另一个页面蓝色方块随之消失。如果这块演示跑不通问题大概率不在代码逻辑上而是 WebSocket 连接没有建立成功。你可以先打开浏览器开发者工具看 Network 标签页里的 WebSocket 连接是否存在再看 Console 有没有报错信息。这一步排查完成后再回来看代码。6. 完成度 10% 到 100%还有哪些模块以及如何评估回到 aethercraft。它目前的完成度标注为 10%这意味着“可玩内容”还非常少。但开放世界类项目有一个特点从 10% 到 30%难度主要在“系统搭积木”从 30% 到 70%难度开始转移到“内容质量”从 70% 到 100%难度则集中在“打磨与性能优化”。下面列出从当前阶段到完整目标之间需要关注的模块以及对它们的优先级判断。模块当前状态的合理推断下一步优先级说明场景容器与渲染已有基础场景高先加 2 个不同风格的场景验证渲染兼容性网络同步与房间管理已验证基本链路高增加断线重连、心跳、房间列表、玩家踢出角色控制与物理未完整中先做简单移动 地图碰撞再做更复杂的交互持久化与存档未完成中服务器权威状态落库防止重启丢数据资源加载与性能未完善中关注浏览器端内存占用和网络包体大小反作弊与权限未开始低原型阶段不必过度设计但公开测试前必须做基础防护玩法与内容最核心的长期工作长期开放世界的真正壁垒在内容设计不在网络性能需要说明的是由于公开材料有限上表只是基于项目描述给出的合理推断具体模块的完成情况应以项目仓库和实际代码为准。但有一个判断方向是确定的所有涉及“多人一致性”的模块优先级都应该高于单机玩法模块。因为这正是开放世界项目与普通网页游戏在架构上的根本差异。如果你也在做类似项目一个建议是不要急着堆玩法。先把“两个玩家在同一个场景里能互相看见、能正常进出、断线后状态能恢复”这三件事做扎实。这三件事在内容为 0 的时候就该完成它们决定了后续所有玩法是不是建立在一个稳定底座上。另外如果你的项目未来计划加入区块链或资产存证相关的能力现阶段也不应该让它抢占开发资源。虚拟世界里的确权、资产记录等内容只有在内容和玩家规模达到一定程度之后才有实际意义。在完成度 10% 的阶段所有精力都应该集中在“让这个世界先存在并且让多人稳定访问它”这个目标上。7. 常见问题与排查思路用 AI 辅助编程做多人浏览器项目最容易出现的并不是 AI 代码本身有 bug而是你不知道去哪里看问题。下面整理了几个联机原型阶段最可能遇到的问题以及排查顺序。问题现象可能原因排查方式解决方案打开页面后没有出现任何方块WebSocket 没有连上浏览器控制台看是否有 WebSocket 报错确认 server.js 已启动确认ws://localhost:8080地址正确检查服务端端口是否被占用自己移动了别人没看到变化服务器没有广播 move 消息在服务端broadcast之前打印日志看是否收到 move检查消息是否包含 id确认 move 分支执行了广播两个方块永远重叠在同一位置客户端直接修改了本地坐标没有等服务端广播检查 sendMove 是不是把服务器回包作为唯一渲染来源改为“只发送意图收到广播后才更新位置”局域网内其他设备访问不了服务端只监听了 localhost查看启动日志中的监听地址监听0.0.0.0并确保防火墙放行对应端口连接一段时间后频繁断开没有心跳机制代理把空闲连接断开查看服务端 WebSocket close 事件定时发送心跳包例如每 10 秒一次 ping/pong玩家退出后其他端仍然显示他服务端 close 处理没有删除玩家看服务端 close 分支是否执行在服务端close事件里删除 players 记录并广播 player-left上面这些问题的共同特点是它们都不是 AI 生成代码时的“语法错误”而是分布式系统的“状态不一致问题”。所以排查时不要只盯着某一行代码要先沿着“发送 → 接收 → 仲裁 → 广播 → 渲染”这条链路逐步确认每一跳都在工作。一个实用的小技巧在服务端给每个广播消息加上一个自增序号例如seq: 42。客户端收到消息后可以在控制台打印序号。这样谁丢了消息、谁的消息顺序乱了一眼就能看出来。等将来项目复杂了这个序号还可以扩展成更可靠的“状态版本号”。8. 最佳实践与工程建议如果你已经决定用 AI 辅助编程去做一个浏览器端开放世界原型下面这些建议不是锦上添花而是能直接减少返工成本的工程底线。第一定义清晰的协议版本。WebSocket 消息的字段一旦被多个客户端使用修改时必须向后兼容。建议一开始就给每条消息加上version字段并在服务端做校验。宁可在早期多花一点时间也不要在接入十几个客户端之后改变消息格式。第二把“配置文件”和“代码逻辑”分开。端口、房间容量、广播频率、心跳间隔这些参数不要硬编码在 server.js 里。可以放到环境变量或配置文件里管理。这样在不同环境下切换时不需要改动代码逻辑也方便你做灰度验证。# 通用参考示例具体参数以项目为准 server.port8080 server.host0.0.0.0 game.maxPlayers64 sync.tickRate20 sync.broadcastIntervalMs50第三对 AI 生成代码做安全审查。在多人联机场景里输入是不可信的。任何来自客户端的消息都要做字段类型、长度、取值范围校验。例如坐标字段必须是数字不能超过地图边界玩家 id 必须是字符串不能包含恶意字符。如果未来开放公网访问还应该考虑加入简单的接入 token 和使用 wss 协议。第四注意状态的可恢复性。原型阶段可能不需要完整数据库但至少要保证“服务端重启后玩家能重新加入而不是整个系统崩掉”。最简单的方式是把玩家状态和场景状态定期序列化到本地文件或数据库。先想好存储边界再考虑优化性能。第五克制“炫技”式重构。AI 辅助编程会不断暗示你可以“重写得更好”但在一个完成度还很低的项目里频繁重写架构是最大的浪费。一个模块能工作就不要因为它“不够优雅”而重写。正确的节奏是当前模块验证通过后立即进入下一个模块等积累到足够多的问题再统一优化。第六使用版本控制并记录决策。哪怕你是单人开发也应该为每一次“改变协议”或“调整架构”建立提交记录。AI 辅助编程的代码生成速度快但版本回滚能力并不比传统开发强。有清晰的提交历史你才能安全地尝试不同方案。第七把验证步骤写进每次任务的完成标准里。比如“两个浏览器登录后能互相看到移动”这可能就是一次任务的验收标准。任务完成的标准不是“代码能编译”而是“链路上的每个节点都有对应日志并且行为符合预期”。9. 总结与后续学习方向回到最开始的问题一个完成度只有 10% 的开放世界项目凭什么值得关注因为它证明了架构先行这条路在 AI 辅助编程时代变得更可行。先把最难的地方打通再用 AI 一点一点填内容这正是从零开始做虚拟世界或开放世界项目的人可以借鉴的路线。aethercraft 目前的状态本质上是把“开放世界 浏览器 联机”这条组合链路里最难验证的部分提前验证完了。如果你也想做类似的实验建议不要一开始就复刻一个完整项目。你先尝试跑通最核心的同步链路一个玩家移动另一个浏览器能看到。这个最小闭环一旦通过你对 Web 联机项目的理解会比单纯读十篇架构文章更深入。之后再逐步加入场景、角色属性、事件系统、持久化每加一个模块都沿用“先拆任务、再让 AI 生成、最后验证”的节奏。后续可以深入的方向包括客户端预测与插值、状态快照与回放、房间分区广播、场景流式加载、以及浏览器端的性能分析。这些内容每一个都足够写几篇文章但它们都建立在一个前提之上——你已经有一个可以多人访问的开放世界骨架。有了这个骨架你会比绝大多数还在“规划一个开放世界”的人走得更远。如果你正好也在做类似项目建议先把这份拆解收藏起来。对照 aethercraft 的模块列表检查一下自己的项目在“网络链路”和“状态权威”上是否已经跑通。这两件事没想清楚之前暂时先不用急着让 AI 生成更多玩法代码。