Tiny World 运行时状态(Tiny World Runtime State)是 jasonkneen/tiny-world-builder 项目中的一份实施规范,主题是浏览器本地持久化用户状态的管理方式。文档本身面向开发者在添加或修改持久化状态时使用,并未提供独立的安装命令或产品定价信息。
用途与总体约定 多数浏览器本地用户状态存放在 localStorage 的 tinyworld:* 前缀下,读写约定为:基本类型用字符串化,对象用 JSON.stringify。禁止将凭据、本地世界存档、云端世界存档或按视口计算的像素位置写入随站点发布的默认值文件。影响共享 Three 材质的持久化渲染/材质设置需在启动后期重新应用,例如材质磨损(tinyworld:render:materialWear)需通过 applyPersistedMaterialSettingsOnBoot() 在首次渲染时生效,而不只依赖控件输入处理。
默认值与迁移 构建器方向光默认值在 tinyworld:render:directionalSun 下设为 10.0(1000%);迁移仅将缺失或未修改的旧 1.0 值升级为 10.0,保留用户明确编辑过的非默认值。Island Viewer 有独立的 tinyworld:island-viewer:* 图形键,并单独迁移旧的 1.1 默认值。
云端存档与资产 云端存档与默认值/localStorage 分离:账户弹窗将完整 TinyWorld JSON 提交至 Netlify Functions(/api/builds),由 Netlify Database 支持。认证启动时,tinyworld:worlds.v1 中的本地命名世界会上传至 /api/builds;当前未分槽的 tinyworld:v1 状态先分配本地槽位以便绑定云端行。顶栏“My worlds”和账户弹窗“My Worlds”需读取同一份云端感知列表。世界菜单分享操作将同一完整状态提交至 /api/share,公开分享 URL 通过 ?share=<id> 解析到同源 /api/share?id=<id>。认证后本地自定义资产也会同步:/api/assets 存储自定义体素构建印章与已保存资产模板,再将远程库合并进 localStorage 并回推合并后的本地副本。snapshotCurrentState() 需与 saveState() 保持同步,使账户保存与分享 URL 包含网格尺寸、岛屿、系泊、自定义体素印章、相机、景观设置以及用户在主页板外编辑的单元格。
JSON 导入 顶栏 JSON 导入需接受应用自身的可移植形态:裸世界状态(根级 cells)、云端/账户封装(data 或 state 内含世界)、命名世界/localStorage 列表及导出资产包。导入的世界应插入 tinyworld:worlds.v1,以便登录后账户数据库同步可拾取。可见的导入入口应为原生 <label for="import-file"> 触发配合屏幕外文件输入,而非仅用按钮程序化点击隐藏输入,因为部分浏览器会静默丢弃隐藏输入的文���选择器调用。排队的账户同步在前一 /api/builds 请求进行中时不得丢弃,需围绕 twCloudWorldSyncing 保留待重试标志。
多人房间与功能开关 多人实时房间是临时运行时状态:PartyKit 存在信息(光标、选中单元格、活动工具)不进入已保存世界 JSON;持久编辑以完整 cell.set 快照发送,再通过 setCell() 应用,使渲染和后续账户保存走正常持久化路径。tinyworld:worlds.activeTinyverse.v1 为每设备导航状态,非世界存档、不同步账户,且在两处排除。房间拆除与用户退出需分开:内部 leaveRoom() 仅拆除套接字/HUD/头像/小地图;面向用户的岛屿退回应调用 WS.exitToWorldPicker(),清除上述键并通过 WS.restoreFreeform() 恢复房间前构建器状态、打开选择器浮层。房间游玩模式为临时状态,使用 __tinyworldMode.setPlayTemporary(),不持久化到 tinyworld:build-play-mode.v1,退出后恢复 Build。世界选择星门每个岛屿仅保留一个中心星门,客户端与 /api/worlds 归一化需剥离旧多星门单元格。PartyKit 世界房间可走性须服务端权威且默认实心:仅空地块、水、桥、星门、动植物、低矮地面覆盖可站立,建筑、树、岩石、栅栏、模型印章、体素构建及未知类型阻挡。tinyworld:features:cluso 为遗留标志,无运行时路径读取;tinyworld:features:ai 控制 AI 面板,生产环境通过 html.ai-disabled 隐藏,本地主机 / ?ai=1 / 该标志启用,白名单账户登录时由 applyAccountAiEntitlement 解锁。
默认值流水线(开发到所有用户) Settings → Workspace 中有“Save Defaults”按钮(仅 localhost/127.0.0.1/file: 可见)。点击后浏览器快照每个 tinyworld:* 键(减去排除清单),POST { settings: { key: value, ... } } 到 /api/save-defaults;tools/dev-server.js 将结果写入仓库根目录 tinyworld-defaults.json;publish.sh 将其复制到 dist/ 随站点发布。每次页面加载时首个内联 <script id="tinyworld-defaults-bootstrap"> 同步 XMLHttpRequest 获取该文件,为用户尚未拥有的键播种默认值,已有用户偏好优先、默认值绝不覆盖。该引导脚本必须带属性,避免 tools/check.js 正则误抓。
排除清单需在 tools/dev-server.js 服务端过滤器与内联 setupDevSaveDefaults() 客户端过滤器中同步维护,涵盖世界存档、多世界存档、API 凭据、账户会话凭据、用户提示文本、会话演示状态、手动音乐选择与模式、欢迎语关闭记录、备份、面板/控件像素位置等。新增不应发布为默认值的持久化值需同时加入两份清单。
面板位置与音频 可拖拽面板(小地图、人群面板、智能体面板及未来面板)必须保存为视口百分比而非绝对像素,格式为 { topPct, leftPct },读取时兼容旧绝对 top/left,并在 resize 时重新应用且限制在边距内;小地图现有实现为参考模式,折叠需原地缩小。AI 聊天面板为固定右侧栏而非可拖拽底部提示,仅持久化宽度/折叠状态于 tinyworld:agent:panel-pos。音频分两层:HTMLAudioElement 用于循环音乐与单次音效,Web Audio(PannerNode/StereoPannerNode)用于位置声源;状态键在 AUDIO_LS 下包括音乐/音效/环境/引擎及静音项。音乐曲目由 MUSIC_TRACKS 管理,随机播放仅使用 MUSIC_RANDOM_TRACKS / music-horizon-* 文件,music-rising-1.mp3 可手动选择但不作为默认或随机项;飞机引擎使用 foley-rocket-engines-1..4,水声使用 foley-water-1..4,循环接缝通过叠加两个不同起始偏移的变体隐藏。工具栏内单个 #sound-icon 按钮切换浮层 #sound-panel。
相机与验证 相机持久化于 tinyworld:view.camera,保存 mode、azimuth、polar、viewSize 与 target;updateCamera() 以 250ms 防抖节流保存,启动时读取并夹取范围,该状态随默认值发布。变更持久化后需运行 node tools/check.js 与 node tools/smoke-static.js,并在干净 localStorage 与既有 localStorage 两种浏览器场景验证默认值播种与用户偏好不被覆盖。常见陷阱包括:面板位置存绝对像素、将 API 密钥/提示文本/世界存档写入默认值、新增无属性内联 <script> 破坏测试、修改 dev-server 后未重启 npm run dev、动态文件输入过早清理、相机默认值漂移。导出(#export 处理器)必须与 saveState() 序列化相同载荷,包含岛屿、系泊、单元格、voxelBuildStamps、相机、景观及 planetLandscape,applyState() 导入时恢复;新增任何持久化世界概念需同时加入 saveState 与导出对象并在 applyState 处理。