让浏览器模拟器的存储在不同浏览器中可靠工作
浏览器模拟器不只是把原生代码编译为 WebAssembly。一个可用的运行时还需要安全保存游戏数据、适应不同浏览器能力,并且在模拟器启动时保持页面响应。这篇说明介绍 Emu666 围绕源私有文件系统(OPFS)、Emscripten WasmFS 和 Web Worker 建立的工程边界。
这是技术说明,不是性能基准或兼容性认证。
关键边界:哪个线程拥有存储操作?
OPFS 可以通过 JavaScript 访问,也可以通过 WasmFS 后端访问;两条路径不能互相替代。特别是在部分 Safari 版本中,从浏览器主线程同步代理 WasmFS 的 OPFS 路径操作可能造成死锁。
因此实现将两类责任分开:
- 模拟器启动前,JavaScript 使用浏览器原生 OPFS API 写入必须预先存在的数据。
- 启动后,模拟器在 pthread worker 中挂载由 OPFS 支持的文件系统,而不是在主线程挂载。
这一边界既避免初始化期间的写入被后续挂载覆盖,也避免页面响应依赖同步文件系统代理。
启动流程
浏览器页面
│
├─ JavaScript 通过原生 OPFS API 写入必要数据
│
├─ Emscripten 创建 WebAssembly 运行时
│
└─ pthread worker 挂载 WasmFS OPFS 后端
│
└─ 模拟器读取已挂载的存储并启动
细节十分重要:主线程不会把属于 OPFS 运行时的路径当作通用的 module.FS 路径处理。小型临时数据可以放在内存文件系统;持久化数据则在 worker 所拥有的运行时准备好前走原生 OPFS 路径。
用能力探测代替浏览器名称规则
浏览器名称并不能说明全部情况。兼容性报告会检测运行时实际需要的能力,包括 WebAssembly、共享内存、线程、BigInt 集成、OPFS、Worker、WebGL、音频能力和 Gamepad API。
用户代理仅用于显示易读的浏览器与系统名称;是否可用由能力探测决定。你可以在 兼容性报告 中运行同一组检测。
跨浏览器安全写入
部分浏览器为 OPFS 文件提供 createWritable(),其他浏览器则需要基于 Worker 的 SyncAccessHandle 路径。存储层将这种差异收口到同一接口中,并在关闭文件前等待每次已排队的写入完成,避免在较严格实现中丢失尾部数据。
对大文件,运行时采用流式文件操作,而不是把完整文件载入 WebAssembly heap。这样既让内存占用更可控,也更适合长时间会话。
本说明不作出的承诺
- 不声称所有浏览器或设备都受到支持。
- 不发布通用性能对比。
- 不提供游戏文件、固件、下载来源或规避版权的指引。
- 不替代浏览器厂商文档,也不替代针对具体应用的可复现实验。
目标更明确:记录一个实用的线程与存储边界,使浏览器端 WebAssembly 运行时更容易理解、测试和持续改进。
复现相关检测
使用 浏览器兼容性报告 查看当前浏览器的能力结果。公开讨论实现细节时,应始终配套最小、与游戏无关的复现,以及明确的浏览器/版本矩阵。