世界与 Tick
一个永久世界
- 所有玩家共享同一个永久运行的二维方格世界。
- 没有赛季,没有对局重置,也没有 NPC、野怪或服务器舰队。
- 每个账号同一时间最多拥有一个存活的 Core。
- Unit 和每一代 Core 使用不可枚举的 UUID。对象活着的时候 ID 一直不变,死亡之后 这个 ID 永不复用。
- 每个 Core 都带公开的
owner_username,客户端显示为@username。内部账号 ID、邮箱和 Unit 所属玩家不会进入状态。 - v0.1 没有公开排行榜。
如果一个账号正好在世界结算期间激活,它不会被塞进一份没做完的快照里。服务端会给它
记一个持久的 activation_tick,玩家在那个 Tick 走确定性重生流程进入世界,和其他人
一样。
确定性区块生成
地形以 32×32 区块为单位,由一个永久保密的 world seed 加上版本化的 HMAC-SHA256 协议 生成,客户端永远拿不到 seed。
这给了你几条可以放心依赖的保证:
- 世界、生成器版本、平衡参数和坐标都相同,地形就一定相同。
- 相邻区块之间的边界通道是确定性的。
- 任何可通行区域都连到区块主干上;但允许出现一格宽的瓶颈,而且这种地形往往很关键。
[0, 0]以及它通往主干的那条路永久是EMPTY,所以 Champion Beacon 不会被围死。- 生成器协议对不上,服务直接拒绝启动。也就是说改动生成语义就得换一个世界数据库。
障碍和主干通道属于永久地形。资源点则是另一层可消耗地图数据,每个区块都有固定配额, 补充位置同样按确定性规则生成。配额和放置规则见 地图与视野。
Tick 生命周期
每个逻辑 Tick 都是固定长度的命令阶段,加上一段长短不固定的结算阶段。
窗口先开,之后服务端才逐个发布玩家状态。所以你拿到手的,是那 15 秒里还剩下的
那部分;收到 state 并不会给你单独起一个 15 秒计时。协议也故意不公开
opened_at 和 deadline_at。
两条消息别搞混:tick 只是宣布有这么一个逻辑 Tick,此时命令还没开放;state 才是
唯一的行动信号。
结算顺序
下面这个顺序属于协议的一部分,不是实现细节:
- 锁定最终有效的 Agent 与 Manual 计划。
- 结算所有
SELF_DESTRUCT,立即移除这些 Unit,并把 Worker Cargo 掉在最后所在格。 - 结算 Unit 移动,以及走到第 4 个 Tick 的 Core 迁移。
- 检查新提交的 Core
START_MOVE。 - 结算 Champion Beacon 的拾取与放下。
- 结算 Worker 的采集与交付。
- 冻结不可变战斗快照,累计所有合法攻击。
- 同时应用伤害、移除死亡对象,并转移符合条件的被摧毁 Core 库存。
- 结算所有战斗后仍存活 Core 的
SELF_DESTRUCT;销毁库存和全军,并让 Worker Cargo 与 Beacon 掉在各自实际位置。 - 按 Unit UUID 顺序结算仍存活 Unit 的
HEAL。 - 记录每名玩家此时的存活人口,再结算其余存活 Core 的
HEAL、REPAIR_SHIELD或 动态定价的SPAWN动作。 - 立即尝试让新摧毁的 Core 重生,只有找不到合法出生点时才顺延到下一 Tick 重试。
- 每结算满 4 个 Tick,按区块只补回已经消耗的资源槽位,使可用资源总数回到该区块的 固定配额。
- 原子提交世界、资源层、事件、统计、journal 和新时钟。
- 宣布下一个 Tick,并准备新的私有状态。
服务端不会为了追上墙上时间而跳 Tick,停服期间世界就是暂停。两个 Tick 也绝不会同时 结算。
资源补充属于本 Tick 的结算末尾。在补充 Tick 里被采掉的点会先消失,随后补充步骤再填满 区块缺少的槽位。没被采的点不会移动,没用掉的配额也不会累计到上限以上。
原子性与重放
一个 Tick 的结果在同一个 PostgreSQL 事务里提交,所以客户端不存在看到「结算了一半的 世界」的时机。规则引擎同样不允许用 map 遍历顺序、墙上时间、进程随机数或者无序的查询 结果去决定胜负。
世界状态和锁定计划都相同,规则算出来的结果就逐字节相同。
崩溃恢复
| 故障点 | 恢复方式 |
|---|---|
| 状态准备失败 | 不开启命令窗口。 |
| 状态发布失败 | 中止 gate;恢复后重新宣布同一 Tick 并重开完整 15 秒。 |
| OPEN 阶段崩溃 | 保留已持久化计划;重启后发送同一 tick、完整 state 和最新回执,并重开完整窗口。 |
| 锁定后崩溃 | 不重新开放窗口,确定性重放已锁定计划。 |
| 服务离线 | 世界和所有逻辑计时暂停。 |
现有世界切换
可消耗资源版本会替换现有世界的旧资源布局,但不会重置世界时钟、玩家、Core、Unit、 库存、重生状态或 Champion Beacon 状态。资源位置作为地图层迁移到新的区块配额与补充 协议。
规则 v0.5 保留 Worker 死亡后的 Cargo 资源堆,并增加随人口变化的 Core 容量。规则
v0.6 将容量改为 max(10, population × 5)。规则 v0.7 让 Ranger 射击穿过 Unit 和
Core,只有障碍物阻挡。规则 v0.8 加入射程 1-3 的 45° 斜线射击,并且只检查射线实际
经过的中间格障碍物。规则 v0.9 把战斗中被摧毁 Core 的容量内库存交给对它伤害最高、
且 Core 在同 Tick 战斗后仍存活的玩家。规则 v0.10 把 Core 的资源动作移到战斗后,
并加入 Unit 与 Core 的 HP 恢复。规则 v0.11 曾把维护费欠款从 Core 伤害改为超额 Unit
伤害。规则 v0.12 加入战斗后无条件、无冷却的 Core 自毁。规则 v0.13 加入无需目标的
Ranger 按格射击,以及按最低 HP、UUID 确定目标的规则。规则 v0.14 删除维护费,改用
精确的随人口变化的 Unit 价格。现有 v0.1 到 v0.13 世界可以在
OPEN 或 COMMITTED 边界升级,
不会重置游戏状态。当前规则同时移除了复活冷却:Core 被摧毁后会在同一个 Tick 的后续
阶段立即尝试重生。如果旧服务停在 LOCKED 或 RESOLVING,必须先用旧规则完成该 Tick,
才能升级。