本站为非官方 Pylon 文档站,大量内容均为 AI 生成·前往官方文档站● ● ●This is an unofficial Pylon documentation site, most content is AI-generated·Visit Official Docs● ● ●中文开发者交流·加入 QQ 群● ● ●本站为非官方 Pylon 文档站,大量内容均为 AI 生成·前往官方文档站● ● ●This is an unofficial Pylon documentation site, most content is AI-generated·Visit Official Docs● ● ●中文开发者交流·加入 QQ 群● ● ●
Pylon 中文文档
开发文档内部

物流系统(Cargo)

了解 Rebar 的物流系统及其工作原理

最后更新时间

作者:Idra

如果对这里的内容有任何不理解的地方,请直接询问 Idra——这部分实现确实比较复杂。

工作原理

Cargo 的工作方式是把 RebarCargoBlock 通过 CargoDuct 连接起来。和流体系统不同的是,物流管道不支持 junction(分叉),每段管道只连接两个端点。

RebarCargoBlock

RebarCargoBlock 代表一个物流端点。当方块实现了这个接口后,就可以在各个面上添加物流组(参见 RebarLogisticBlock),并指定面类型(INPUT 或 OUTPUT)。一旦某个面关联了物流组和类型,物流管道就可以连接到该面。

物品路由

物流管道的一个核心难题是:连接链上的任何部分都可能随时被卸载,而连接链几乎可以无限长。这让路由变得非常棘手。我们当然可以在内存里记录每个端点到节点的连接关系,但网络一有变动就得全量更新。

我(Idra)能想到最简单的方案是用缓存。每次需要查询某个输出连到哪个输入时,先查缓存:命中了就直接返回路由结果;没命中就从输出端出发,沿管道一路追踪到输入端,同时记录沿途所有管道。之后只要路由上任何一个方块被修改(破坏或卸载),整条路由就会失效,需要重新计算。

也就是说,如果物流管道网络的中间部分被卸载了,对应路由会失效,物品不再沿该网络传输。

Cargo ticking

每个 RebarCargoBlock 都会定期 tick。单个 cargo 方块的 tick 流程如下:

  1. 遍历该方块上的每个输出面(及其对应的物流组)
  2. 用上文提到的缓存查找对应的输入方块+面(如果存在)及物流组
  3. 遍历源物流组中的每个源槽位
  4. 遍历目标物流组中的每个目标槽位
  5. 检查是否可以把物品从源槽位移到目标槽位(比如源里有西瓜,目标为空或也有西瓜且数量不足 64)
  6. 为您的电脑即将爆炸而担忧
  7. 成功转移了物品,或者遍历完了所有源/目标槽位,就结束本次 tick

CargoDuct

物流管道负责记录自己连接了哪些面,并管理相关的物品显示。这基本上是它的全部功能。每当有新的物流方块或管道相邻时,它会检查能否与之连接。如果可以,就加入已连接面的列表,刷新自己的物品显示,同时通知对方也刷新显示。

物品显示的刷新逻辑主要是检查所有已连接的面,确保每个面都有对应的显示实体(同时清理不再需要的显示)。

此外,当物流管道或方块建立/断开连接时,会触发相应事件。

Cargo 块 hack

Cargo 方块不会自己追踪已连接的面。"你他*的在说什么?"你可能会问。"Cargo 方块不也能直接相连吗,中间不需要管道?!"没错,但这其实是个 hack。本质上,物品路由逻辑已经支持直接相邻的 cargo 方块了,唯一要做的就是让显示正确更新。为此我们写了 updateDirectlyConnectedFaces 函数,它的作用就是检查所有相邻的 cargo 方块,确保显示都正确。两个 cargo 方块之间的连接显示只由其中一方拥有(不像物流管道那样每段都有显示)。这确实是个很大的 hack。

Z-fighting

必须小心处理 Z-fighting 问题。连接处管道的显示实体使用了不同的宽度,否则会重叠导致 Z-fighting。具体做法是使用 3 种不同的宽度,在管道拐角处连接两根管道时,确保新生成的显示宽度与该角上已有显示的宽度不同。希望这个解释足够清楚。

目录