物品介绍

工厂管理器(Factory Manager)

工厂管理器是工厂的核心,控制整个工厂。放入磁盘后,可以编辑并运行其中的程序。

同一线缆网络中可以有多个管理器,且工厂管理器也可以充当工厂线缆。

超级工厂管理编程教程及实例-第1张图片管理器 GUI

隧道式工厂管理器(Tunnelled Factory Manager)

隧道式工厂管理器隧保留了工厂管理器的所有功能和界面,不同之处为它还可以把相邻面的输入和输出指令“隧穿”到相对面,并且这种“隧穿”可以堆叠,比如在下图中的漏斗里面放入物品,物品会直接穿过隧道式工厂管理器进入下方的木桶内,并且不会进入左边和右边的木桶。

基于这个特性可以用它来直线传输多种类型的资源,也可以将它作为管道的交叉点来避免飞线,从而压缩空间。

可以传输的资源类型为:物品、流体、能量和通用机械的化学品(气体),并且不能传输红石信号。

超级工厂管理编程教程及实例-第2张图片隧道式工厂管理器传输演示

工厂线缆(Inventory Cable)

工厂线缆用于连接管理器和操作对象。操作方向与连接方向无关,由程序决定。

≥4.20.0:加入了精致工厂线缆(Fancy Inventory Cable),具有非完整的方块模型,效果与工厂线缆一致且可相互连接。

  1. 超级工厂管理编程教程及实例-第3张图片用线缆连接箱子

工厂管理器程序磁盘(Factory Manager Program Disk)

工厂管理器程序磁盘可存储程序和标签,放在管理器里以运行,也可以手持右击编辑程序。    万恶之源

标签枪(Label Gun)

标签枪为方块添加标签,标签用于程序选择对象,一个标签可以有多个方块,一个方块可以有多个标签。 

推荐先写程序,然后 Shift + 右击 从管理器拉取标签,省去手动输入标签。

操作作用
右击空气
打开 GUI
右击方块为方块添加/移除此标签
Shift + 右击方块清除;移除方块所有标签
Ctrl + 右击方块

连续;为所有连续的相同方块添加/移除标签,

以每个有效方块为中心的 3×3×3 范围内的相

同方块且连接到任意线缆网络则判定有效

Alt + 右击方块(≥4.20.0)选取此方块的一个标签

Shift + 滚动滚轮

按键绑定 上一个标签/下一个标签(≥4.20.0)

切换标签,按字母顺序循环
右击管理器推送,标签枪>>磁盘
Shift + 右击管理器拉取,标签枪<<磁盘
Alt(≥4.20.0)切换标签渲染:全部/此标签/此方块


超级工厂管理编程教程及实例-第4张图片标签枪 GUI + 多标签示例

网络工具(Network Tool)

网络工具手持时,穿透显示管理器、线缆和标签。

主手右击方块:查看该位置的方块、线缆网络和输出信息,输出方向为点击的面。

超级工厂管理编程教程及实例-第5张图片查看信息

≥4.20.0

加入线缆伪装功能,副手网络工具,主手方块:

操作范围
右击单个方块
Ctrl + 右击连续的相同方块
Alt + 右击线缆网络内所有相同的方块
Ctrl + Alt + 右击线缆网络内所有方块

蓄水槽(Water Tank)

蓄水槽至少有两个面接触水源时,可以作为无限水源输出,多个水槽相连时可提高整体的输出上限。

<4.20.0:不论是否具有无限水源,每个提供 1000mB/t 的输出量。

≥4.20.0:相连接的水槽需具有无限水源,提供 1000 * (2 ^ (有效水槽数 - 1)) mB/t 的输出量。

超级工厂管理编程教程及实例-第6张图片下方为具有无限水源的蓄水槽


编程

将磁盘放入管理器,用线缆连接各方块,使用标签枪标记方块后存入磁盘,对磁盘进行编程。

语句可以由任意空格、换行分隔。

-- 为单行注释符,在注释符后的内容不会被执行。

点击左下角的 # 按钮可切换是否显示行号和代码补全(≥4.21.0)。

由于原版的限制,程序的长度限定在约 32300 个字符以内。   真的可以达到吗?

符号说明
[ ]可选项
< >必填项
*多值项,可用逗号分隔多个值
|或,必须为其中一项
string字符串,可以用"双引号"括起来
int整数型
long长整型
label标签,用标签枪指定

快捷键

快捷键作用
Ctrl + E在管理器界面打开编程界面
Ctrl + Shift + E在管理器界面查看示例程序
Ctrl + /用 -- 注释或取消注释当前行
Tab、Shift + Tab缩进或取消缩进
\(≥4.21.0)代码补全
Shift + Enter完成编辑
Ctrl + Back、Ctrl + Del删除直到单词边界(空格)
Home、End光标移至行首或行末
Ctrl + Home、Ctrl + End光标移至开头或结尾
Ctrl + Space

查看 input、output、resourceid、label、if、

boolexpr

Ctrl + i

开关容器检查器,可显示槽位和导出检查器

name(命名程序/磁盘)

name <string>    一切的开始。。

为程序/磁盘命名,磁盘的名称将变为该字符串。

只能为首行代码。

name "Hello World"

every(触发器)

every [interval: int] [global | g] [(plus | +) <offset: int>] (ticks | tick | seconds | second) do [block] end

时间触发器,每 interval 游戏刻(ticks)或秒(seconds),即 计时 % 间隔 == 0 时,执行代码块 block。

除 name 和 every 外的其它语句仅能且只有这些语句能存在代码块中。

interval 和 offset 为非负整数,interval 未定义时为 1。

tick 和 second 分别与 ticks、seconds 相同。(4.20.0:second 有 bug,效果与 ticks 一致)

默认最小间隔为 1 秒(20游戏刻),若代码块仅进行能量传输时最小间隔为 1 游戏刻,可在配置文件 config/sfm-server.toml(≥4.20.0,同时可通过管理器界面左下角的 View server config 打开) 或 config/sfm-common.toml 更改 timerTriggerMinimumIntervalInTicks 值。


≥4.20.0

计时分为管理器内部时钟(默认)或世界时间。

放置管理器时,内部时钟为 0,每游戏刻更新方块实体时此值 +1。

在世界中重新载入管理器时,内部时钟随机初始化。

使用 global 或其简写 g 时,使用世界时间。

使用 plus 或其缩写 + 时,计时偏移 offset 刻。

--每秒运行一次
every seconds do end

--每 20 游戏刻运行一次
every 20 ticks do end
every 20+10 ticks do end --偏移 10 游戏刻

--每 20 游戏刻(世界时间)运行一次
every 20g ticks do end
every 20 global plus 1 ticks do end --偏移 1 游戏刻
every 20g+1 ticks do end

--每游戏刻运行一次,仅进行能量传输
every tick do
    input fe:: from a top side
    output fe:: to b top side
end

every redstone pulse do [block] end

红石触发器,每次红石脉冲(上升沿)执行代码块。

激活间隔不受最小传输间隔影响,可以每游戏刻都激活,也可以在同一游戏刻内多次激活。

every redstone pulse do end

input、output(输入输出)

基础语法

input from <label>*

将标签处添加为输入对象,不进行输出时什么也不会发生。

output to <label>*

从输入对象输出物品到标签处,不受部分容器本身传输速率限制。

标签为多值项,可以指定多个标签并用逗号分隔,如 a,b。

注意:input 和 output 不是一定成对出现的,且输入不会因 output 语句而清除,需要使用 forget 遗忘输入。

可以交换顺序,from ... input ...to ... output ... 也是有效的语法。

--a = 箱子, b = 箱子
--将 a 箱子所有物品传输到 b 箱子
every 20 ticks do
    input from a
    output to b
end

every seconds do
    from c,d input
    to e output
end

完整语法

(input output) [[<number: long> [each]] [retain <keep: long> [each][resourceid] [(with without<withClause>]]* [except <resourceid>*] (from to) [empty (slots | slot) in] [each] <label>* [round robin by (label block)] [<side>* side] [(slots | slot) <range>*]

resourceid

(input | output) [... [resourceid]]* (from | to) <label>* [<side>* side]

通过指定 resourceid,可以传输指定的物品或流体、能量、化学品等,resourceid 的大写字母字符在解析时自动转化为小写。

除物品外的输入输出通常需指定面(side)。    这个一定要注意!有些方块会有指定的资源输入输出面,如果发现没法抽取就要检查了

红石信号为只读,且优先使用缓存(上次无缓存时读取到的红石强度),故无法即时响应,用法见 if 的示例。

resourceid 的格式为 [sfm]:[resourceType]:[namespace]:[name],默认的 resourceid 为 sfm:item,将匹配所有物品。

  • sfm:可省略。

  • resourceType:资源类型。

    资源类型说明别名
    item物品
    fluid流体
    forge_energy
    能量ferfenergypower
    mekanism_energy(MC1.19.2)
    能量,单次传输
    上限为 MAX_LONG

    chemical(该名称需 ≥MC1.21.1,
    其它版本可使用别名)
    通用机械的化学品gas
    (气体)
    infusion
    (灌注)
    slurry
    (浆液)
    pigment
    (颜料)
    redstone(≥4.27.0)红石信号
  • namespace:命名空间,通常为模组 id,如原版的命名空间为 minecraft,通用机械的命名空间为 mekanism,省略时匹配所有命名空间。

  • name:名称,省略时匹配所有名称。

示例资源类型命名空间名称
超级工厂管理编程教程及实例-第7张图片itemminecraftdiamond
超级工厂管理编程教程及实例-第8张图片fluidminecraftwater
能量forge_energyfeenergy
通用机械能量mekanism_energymekanism
energy
超级工厂管理编程教程及实例-第9张图片chemical
mekanismnuclear_waste
红石信号redstoneminecraftredstone

≥4.20.0:可以用 or 连接 resourceid,如 stone or dirt。

注意 [resourceid] 本身不是多值项,[... [resourceid]]* 才是。

input 5 stone,dirt from a --输入 5 个石头,全部泥土
input 5 stone or dirt from a --输入 5 个石头或泥土
input 5 "stone|dirt" from a --输入 5 个石头或泥土

resourceid 支持正则表达式,"resourceid" 可以是字符串,未用双引号时 fe:: 等价于 fe:*:* 等价于 "fe:.*:.*"。

元字符说明示例结果
.匹配任意字符"...._ingot"

匹配 iron_ingot 等,

不匹配 copper_ingot 等

*匹配前面的子表达式任意次*_ingot

解析时变为 .*_ingot,

匹配 iron_ingot 等

+匹配前面的子表达式一次或多次"stone.+"

匹配 stone_axe 等,

不匹配 stone

?匹配前面的子表达式零次或一次"bricks?"匹配 brick、bricks
{n}匹配前面的子表达式n次"oak_wo{2}d"
仅匹配 oak_wood
{n,}匹配前面的子表达式至少n次"oak_wo{2,}d"

匹配 oak_wood、

oak_woood 等

{n,m}匹配前面的子表达式至少n次,至多m次"a{2,3}"匹配 aa、aaa
(pattern)匹配pattern并获取引用"(char)?coal"匹配 charcoal、coal
(?=pattern)非获取匹配,正向肯定预查"(?=.*gold)(?=.*block).+"

匹配 gold_block、raw_gold_block,

也匹配 block_gold 等

(?!pattern)非获取匹配,正向否定预查"iron_(?!block).*"

不匹配 iron_block,且匹配位置从

iron_ 处继续

$匹配结尾"(?!quartz$).+"不匹配 quartz,但匹配 quartz_block 等
x|y

匹配x或y

"(iron|gold)_ingot"
匹配 iron_ingot、gold_ingot
[xyz]匹配包含的任意一个字符"[fl]eather"匹配 feather、leather
[^xyz]匹配未包含的任意字符"[^(stripped_)].*_log"

匹配任何非去皮原木 oak_log 等,

不匹配 stripped_cherry_log 等

[a-z]匹配范围内的任意字符"[a-l]+_wool"

匹配 black_wool,

不匹配 red_wool 等

\d匹配数字字符,等价于[0-9]"rocket_t\d"匹配 rocket_t1 等
\D匹配非数字字符,等价与[^0-9]"rocket_t\D+"不匹配 rocket_t1 等
\xn

匹配十六进制ASCII编码为n的字符,

n为两位数

"\x64irt"匹配 dirt
\un

匹配十六进制Unicode编码为n的字符,

n为四位数

"oak\u005flog"匹配 oak_log
\num获取匹配引用,num为从1开始的正整数"(.)\1.*"匹配 llama_spawn_egg


超级工厂管理编程教程及实例-第10张图片光标移至 resourceid 按下 Ctrl + Space

若要获取容器里已有的 resourceid,可以用网络工具、Ctrl + i、光标移至标签处按 Ctrl + Space。

超级工厂管理编程教程及实例-第11张图片光标移至 label 按下 Ctrl + Space

every 20 ticks do
    --从a输入钻石、各种锭
    input diamond,*_ingot from a
    --将铁锭输出给b
    output iron_ingot to b
    --将除铁锭外物品输出给c
    output "[^(iron_ingot)].*" to c

    --从"裂变反应堆"输入核废料
    input gas:mekanism:nuclear_waste from "裂变反应堆" each side
    --输出所有化学品到"同位素离心机"
    output gas:: to "同位素离心机" each side
end

every tick do
    --传能量
    input fe:: from "能量" top side
    output fe:: to "机器" top side
end

number each? retain?

(input output) [[<number: long> [each]] [retain <keep: long> [each][resourceid]]* (from to<label>*

number 指定输入输出的数量,若为 each,对 resouceid 每项都生效。

retain 关键字用于保留指定的数量,输入时保留 keep 个,输出时直至目标有 keep 个,若为 each,对 resourceid 每项都生效。

使用 each 时,resourceid 匹配的项不能超过 512 个。

every 20 ticks do
    input
        1, --输入1个物品
        1 *_ingot, --输入1个锭
        1 each *_ingot, --输入每种锭各1个
        retain 5, --保留5个物品,其余输入
        retain 5 *_ingot, --保留5个锭,其余锭输入
        retain 5 each *_ingot, --保留每种锭各5个,其余锭输入
        3 retain 6 *_ingot, --保留6个锭,输入3个锭
        3 retain 6 each *_ingot, --保留每种锭各6个,输入3个锭
        3 each retain 6 *_ingot, --保留6个锭,输入每种锭各3个
        3 each retain 6 each *_ingot, --保留每种锭各6个,输入每种锭各3个
        1000 fluid:: --输入1000mB流体
    from a top side

    output
        7, --输出7个物品
        7 each *_ingot, --输出每种锭各7个
        retain 12, --输出到b中拥有12个物品停止
        5 each retain 9 each *_ingot --输出每种锭各5个,直至b中每种锭各有9个
    to b
end

with、without

(input | output) [[resourceid] [(with | without<withClause>]]* (from | to) <label>*

4.19.0 加入,≥4.20.0 without 生效。

从 resourceid 中加入或排除(with/without)匹配标签的项。

<withClause>说明
( <withClause> )括号
not <withClause>逻辑非
<withClause> and <withClause>
逻辑与
<withClause> or <withClause>
逻辑或
(tag [#] | #) <tagMatcher>标签值

tagMatcher 的格式为 [namespace]:<name>,省略命名空间时匹配所有命名空间。

用星号*匹配任意字符任意次,用两个星号**匹配任意深度。

every 20 ticks do
    --输入所有矿石
    input with #ores from a
    --输入所有深板岩变种的矿石
    input with tag #forge:ores_in_ground/deepslate from a
    --输入排除所有木板
    input without tag #minecraft:planks from a
    --输出所有原版的矿石
    output minecraft: with #ores to b
    
    --1 test:a
    --2 test:a/b
    --3 test:a/b/c
    --4 test:a/b/c/d
    input with #a from a --匹配1
    input with #test:a from a --匹配1
    input with #test:* from a --匹配1
    input with #test:* / * from a --匹配2
    input with #test:* / ** from a --匹配2、3、4
    input with #test:** from a --匹配1、2、3、4
end

except

(input output) [[resourceid]]* [except <resourceid>*] (from to<label>*

except 用于排除指定的 resourceid,同样支持正则表达式。

every 20 ticks do
    --输入锭但排除铜锭
    input *_ingot except copper_ingot from a
    --输出所有物品但排除原版物品
    output except minecraft: to b
    --输出所有具有storage_blocks标签的物品,但排除铁块
    output with #storage_blocks except iron_block to c
end

each、round robin

(input output) [[<number: long>] [retain <keep: long>]]* (from to[each] <label>* [round robin by (label | block)]

默认情况下,多个标签多个方块均视为一个整体。

若使用 each,限制数量对每个标签下的每个方块都独立计算(方块可能包含多个标签而被执行多次)。


round robin 用于单个对象循环,每次执行程序切换到下一个对象,在同个触发器和同个资源类型中,多个包含 round robin 的语句索引的对象相同。

  • label:按标签循环,如果某个方块包含了多个标签,在一轮循环中会出现多次。

  • block:按方块循环,在一轮循环中每个方块只会出现一次。

同时使用 each 和 round robin 时,行为同 round robin。

every redstone pulse do
    --从a标签的每个方块各输入5个物品
    input 5 from each a
    --输出给每个具有b的标签的方块,直至有2个物品
    output retain 2 to each b
    --循环选择c标签或d标签
    input 4 from c,d round robin by label
    --从所有具有e或f标签的方块循环选择,每个方块在每轮循环中只会被选取一次
    output to e,f round robin by block
end

side

(input output) [... [resourceid]]* (from to<label>* [<side>* side]

side 用于指定输入输出面,未指定时为 null,在传输流体、能量、化学品时需要指定面。

指定多个面或 each 时,对每个面都进行一次操作。

<side>说明
each所有面
top顶面
bottom底面
north北面

east

东面
south南面
west西面
left左面

4.27.0 加入

相对于方块的朝向(facing)

否则为 null

right右面
front前面
back后面
null不指定无法对限制方向的方块操作
every 20 ticks do
    --从a的顶面输入能量
    input fe:: from a top side
    --输出能量到b的东面或前面
    output fe:: to b east,front side
    --从c的所有面输入化学品(气体)
    input gas:: from c each side
    --输出化学品到每个具有d标签的方块的所有面,直至拥有200mB
    output retain 200 gas:: to each d each side
end

slots

(input | output) (from | to) <label>* [(slots | slot) <range>*]

指定输入输出的物品的槽位,按 Ctrl + i 切换槽位显示,或在槽位放置物品后使用网络工具、Export Inspector、Ctrl + Space 获取该物品的槽位。    Ctrl + i 获取的槽位是GUI槽位,不一定是自动化槽位。

range 可以为数值或范围,类型为 long,如 1,3-5。

超级工厂管理编程教程及实例-第12张图片Ctrl + i

every 20 ticks do
    input from a slots 0
    output to b slots 24-26
end

empty slots in

output to [empty (slots | slot) in] <label>*

4.25.0 加入,只输出到空槽位。

every 20 ticks do
    input from a
    output to empty slots in b
end

forget(遗忘输入)

forget [<label>*]

遗忘输入,若指定了标签,则只遗忘指定的标签。

input 的输入在进行 output 后仍然可用于下个 output 语句,这时就可以用 forget 遗忘之前的输入。

every 20 ticks do
    input from a
    output to "熔炉"
    --若不进行forget,a的物品会直接进入b
    forget
    input from "熔炉" slots 2
    output to b
end

if(条件控制)

if <boolexpr> then [block]    

[else if <boolexpr> then [block]]*

[else [block]]

end

if 语句用于条件控制,当 <boolexpr> 的值为 True 时,执行相应代码块然后结束,没有其它代码块执行时执行 else 代码块。

由于语句不受换行和缩进影响,当在 else 分支的代码块中紧跟着出现 if 语句时,实际上这是 else if 分支。   if真是太劲爆了!

<boolexpr>说明
true值为True
false值为False
( <boolexpr> )括号
not <boolexpr>逻辑非
<boolexpr> and <boolexpr>逻辑与
<boolexpr> or <boolexpr>逻辑或

[setOp] <label>* [...] has <comparisonOp>

<number: long> [resourceid] [...]

判断资源的数量

redstone [<comparisonOp> <number: long>]判断管理器的红石信号

[setOp] <label>* [round robin by (label | block)] [<side>* side] [(slots | slot<range>*has <comparisonOp> <number: long> [resourceid] [(with | without<withClause>] [except <resourceid>*]

其中 with、without、except 仅在 ≥4.20.0 版本可用,低版本 except 可用正则表达式实现。

[setOp]说明
everyeach所有个体满足
some一个或以上个体满足
one仅一个个体满足
lone零个或一个个体满足
overall(默认)整体满足
<comparisonOp>说明
>gt大于
<lt小于
=eq等于
<=le小于等于
>=ge大于等于

redstone [<comparisonOp> <number: long>]

仅使用 redstone 关键字时表示管理器自身是否有红石信号。     恭喜你学完了,但是代价是什么呢。。(掉头发

every 20 ticks do
    input from a

    --a是否大于5个物品
    if a has > 5 then
        output 1 to b
    --b是否有除了金锭和铜锭外的物品10个或以上,且有2个或以上的纸
    else if b has >= 10 "(?!(gold|copper)).*_ingot" and b has >= 2 paper then
        output retain 4 to c
    else
        output to d
    end

    --e的每个方块是否都有>=200mB的化学品
    if each e has >= 200 gas:: then
        input gas:: from e top side
        output 200 gas:: to each f
    end
    
    --管理器是否有红石信号(>0)
    if redstone then
        input fe:: from g top side
    end
    --管理器的红石信号是否小于10
    if redstone < 10 then
        output fe:: to h each side
    end
    
    --i处红石信号是否大于8
    if i top side has >= 8 redstone:: then
        output fe:: to i front side
    end
end

实例

有机灌注器阵列

热力系列+神秘农业的精华产线为例,需要输入能量、水、种子、肥料,产出种子和精华。

超级工厂管理编程教程及实例-第13张图片将所有机器的顶部设置为输入输出面,使用 Ctrl + 右击 快速标记所有有机灌注器合成仓发配种子到 "input" 里,物品接口提供肥料和接收产物。energy 是能量立方或其它供能元件。water 是流体储罐并配合管道和水槽缓存水。

超级工厂管理编程教程及实例-第14张图片

every tick do
    input fe:: from energy top side --传能量
    output fe:: to "有机灌注器" top side
end

every 20 ticks do
    input fluid:: from water top side --传水
    output fluid:: to "有机灌注器" top side
    
    input thermal:phytogro from "物品接口" --传肥料
    output to "有机灌注器" top side
    forget

    input from "input"
    output retain 2 *_seeds to each "有机灌注器" top side --传种子且每个机器最多有2个
    forget

    input from "有机灌注器" top side --传产物
    output to "物品接口"
end

Mek 聚变反应堆自适应速率

聚变反应堆可以直接通入氘氚燃料,燃料速率为 1-1000mB/t 。用终极化学品储罐储存燃料,设计目标为储罐拥有 8,192,000mB 燃料时速率达到 1000mB/t,传输速率与燃料储量成正比,每个游戏刻都需要燃料以维持运行,使用 every redstone pulse 触发器和 1tick 脉冲。

超级工厂管理编程教程及实例-第15张图片1 tick 脉冲发生器超级工厂管理编程教程及实例-第16张图片超级工厂管理编程教程及实例-第17张图片粘性活塞超级工厂管理编程教程及实例-第18张图片粘性活塞,侦测器方向与下层相反
超级工厂管理编程教程及实例-第19张图片超级工厂管理编程教程及实例-第20张图片超级工厂管理编程教程及实例-第21张图片
every tick do
    input fe:: from energy top side
    output fe:: to need_energy each side
end

every 20 ticks do
    input fluid:: from "电动泵" top side
    output fluid:: to "电解分离器" top side
end

every redstone pulse do
    input gas:: from "燃料" top side
    if "燃料" has >= 4096000 gas:: then
        output 500 gas:: to "聚变反应堆端口" top side
        output 4096000 gas:: to temp top side
    end
    if "燃料" has >= 2048000 gas:: then
        output 250 gas:: to "聚变反应堆端口" top side
        output 2048000 gas:: to temp top side
    end
    if "燃料" has >= 1024000 gas:: then
        output 125 gas:: to "聚变反应堆端口" top side
        output 1024000 gas:: to temp top side
    end
    if "燃料" has >= 512000 gas:: then
        output 62 gas:: to "聚变反应堆端口" top side
        output 512000 gas:: to temp top side
    end
    if "燃料" has >= 256000 gas:: then
        output 32 gas:: to "聚变反应堆端口" top side
        output 256000 gas:: to temp top side
    end
    if "燃料" has >= 128000 gas:: then
        output 16 gas:: to "聚变反应堆端口" top side
        output 128000 gas:: to temp top side
    end
    if "燃料" has >= 64000 gas:: then
        output 8 gas:: to "聚变反应堆端口" top side
        output 64000 gas:: to temp top side
    end
    if "燃料" has >= 32000 gas:: then
        output 4 gas:: to "聚变反应堆端口" top side
        output 32000 gas:: to temp top side
    end
    if "燃料" has >= 16000 gas:: then
        output 2 gas:: to "聚变反应堆端口" top side
        output 16000 gas:: to temp top side
    end
    if "燃料" has >= 8000 gas:: then
        output 1 gas:: to "聚变反应堆端口" top side
    end
    if "聚变反应堆端口" has = 0 gas:mekanismgenerators:fusion_fuel then
        output 1 gas:: to "聚变反应堆端口" top side
    end
    forget
    input gas:: from temp top side
    output gas:: to "燃料" top side
end

sfm 代码时序与效率

关于 sfm 代码执行的时序

当使用 sfm 执行多对输入输出语句,如下列代码:

input from "原料" bottom side
output to "粉碎机" top side

input from "粉碎机" bottom side
output to "输出" bottom side

会发现非常多的原料未经处理直接进入输出栏,这是因为第一次的输入对象未被遗忘,变成了:

input from "原料" bottom side
output to "粉碎机" top side

input from "原料","粉碎机" bottom side
output to "输出" bottom side

解决方式有三种,核心都在于将原本无间断执行的两个操作(放入原料输出产物)变为有间断的两个操作(放入原料,而后输出产物)。

第一种方式是使用 forget 代码,将代码改进为:

every 20 ticks do
    input from "原料" bottom side
    output to "粉碎机" top side
    forget

    input from "粉碎机" bottom side
    output to "输出" bottom side
end

这样的代码运行时会在中间进行截断,能够避免出现代码执行时序出错。

第二种方式为触发器分段,将代码分为两段:

every 20 ticks do
    input from "原料" bottom side
    output to "粉碎机" top side
end

every 20 ticks do
    input from "粉碎机" bottom side
    output to "输出" bottom side
end

在每个触发器执行完成后,会自动遗忘所有输入对象,这样的代码将两个操作完全独立运行,不会出现代码执行时序出错。

第三种方式为物品精确描述,避免代码过于通用,将代码改进为:

every 20 ticks do
    input with #minecraft:sand from "原料" bottom side
    output with #minecraft:sand to "粉碎机" top side
    input with #exdeorum:dust from "粉碎机" bottom side
    output with #exdeorum:dust to "输出" bottom side
end

这样的代码完全精确的指明了哪些物品能输入到哪一个机器中。但对于大部分机器来说,能处理的物品非常多,且是多个模组多个类型的物品,使用这样的代码只能处理少部分物品。

关于 sfm 代码执行的效率

当使用 sfm 代码执行一系列的同机器操作时,如果未能在机器配置上进行合理分配,会导致实际效率极大降低。如下列代码:

every tick do
    input fe:: from "能源" bottom side
    output fe:: to "结晶" top side
end

every 20 ticks do
    input from "结晶" bottom side
    output to "输出" top side
end

every 80 ticks do
    input gas:: from "输入" bottom side
    output gas:: to "结晶" top side
end

当使用下列数量配置和处理列表时效率大幅降低。

超级工厂管理编程教程及实例-第22张图片

可以看出,当前的机器阵列设置了需要处理 4 种类型的物品。但在 sfm 中只配置了 1 个输出端。最终导致的结果就是处理效率大幅降低,大部分时候只有不到 40% 的机器在正常工作,其余的 60% 无法处理物品。

every 20 ticks do
    input from "结晶" bottom side
    output to "输出" top side
end

分析上面代码的处理优先级如下:

在处理 1 种物品时,其处理优先级为:产物 A>输出口。
在处理 2 种及以上物品时,其处理优先级为:物品 A>输出口,物品 B 即其他物品进入等待直到输出口可用。

在 sfm 的代码执行过程中,遵循可执行优先>并行优先的原则。例如在本 sfm 机器阵列中,存在 1 个"输出",1020 个"机器",1 个"能源",1 个"输入"。此时的执行顺序和优先级如下:

sfm 先检测输入中的物品,然后默认依次将"输入"中的物品输出到"机器"中(注意是依次而非并行)

这造成的结果是当输入内的物品无法同时填满 1020 个机器时,就会存在部分机器无法获得输入中物品。从而导致部分机器未工作,效率降低。解决方式为使用 each、retain 等语句控制对机器的输入数量。
当机器运行完毕后,由于同刻输出物品太多,堵满了"输出"的物品返回栏,导致部分机器输出槽满无法继续工作。从而导致部分机器无法正常运行,效率降低。解决方式为增加"输出"数量或分批多次收取产物。

测试结果如下:

机器输出同时处理处理结果
102412 种物品

258 个机器处理物品 A 后临时卡死
762 个机器处理物品 B 后正常输出

物品B处理完毕后

258 个机器处理物品 A 后正常输出

102464 种物品

258 个机器处理物品 A 后正常输出

200 个机器处理物品 B 后正常输出

300 个机器处理物品 C 后正常输出
262 个机器处理物品 D 后正常输出

总结:当使用 sfm 堆量合成物品时,根据整列处理物品时输出物品总类型决定输出的数量。如使用 Mek 的电解分离器时,电解分离水会产生氢气和氧气,电解分离盐水会产生氯气和氢气,总计 3 种气体,为了确保机器的效率,最少需要 3 个输出口,而且最好在 sfm 代码中分别输出不同种类的气体到专用的输出。因为 Mek 的电解分离器在电解水和盐水时产生氢气的效率远高于其他两种气体,一般情况下,不直接将气体产物输出到 ME 接口,而是输出到 Mek 化学品储罐作为缓存,再将化学品储罐中的气体输出到 AE 网络中。同时,用于输出氢气的化学品储罐数量要大于其他两种气体储罐的总和。

关于 sfm 代码与机器的适配

因为 sfm 代码执行逻辑为:依次选中机器>按顺序执行 sfm 代码,所以实际编写 sfm 代码时,对代码与执行机器的适配性提出了很高的要求。比如以下代码:

every tick do
input fe:: from "能源" bottom side
output fe:: to "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 ae2:printed_logic_processor to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 ae2:printed_calculation_processor to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 ae2:printed_engineering_processor to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 megacells:printed_accumulation_processor to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 appflux:printed_energy_processor to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 extendedae:concurrent_processor_print to each "装配" top side
end
every 20 ticks do
input from "输入" bottom side
output 64 minecraft:redstone,64 ae2:printed_silicon,64 advanced_ae:printed_quantum_processor to each "装配" top side
end
every 20 ticks do
input from "装配" bottom side
output to "输出" bottom side
end

从代码中可以看出,这是使用 sfm 批量合成 AE 及其附属模组中的总计 7 种处理器(如逻辑、工程、累积处理器)。该类型的合成机器有非常多,如压印器AE2)、高级压印器高级AE)、水晶装配器高级AE)、装配机现代工业化)。对上述所有的机器合成效率进行分析:

在使用 sfm 堆量提高各类型的处理器合成速度时,在完成输入输出方向配置后,堆量 512 个机器。使用上面的代码后执行结果如下:

机器名称合成速度可添加升级合成类型合成时输入限制输入槽属性卡死卡死情况卡死频率
压印器基础速度加速升级*4每次 1 种合成时拒绝其他输入有类型限制的输入槽*3

高级压印器基础速度加速升级*4每次 4 种合成时允许其他输入有类型限制的输入槽*12

同时合成超过 1 种处理器时

1024 合成卡死 10-50
水晶装配器4 倍速度加速升级*4每次 1  种合成时允许其他输入无类型限制的输入槽*9

大量合成时(即使只合成 1 种处理器)

1024 合成卡死 125-250
装配机基础速度

加速升级*64
机身升级*1
超频升级*1

每次 1 种合成时允许其他输入

无类型限制的输入槽*9
有类型限制的输入槽*1

大量合成时(即使只合成 1 种处理器)

1024 合成卡死 25-50

下面是各类型机器卡合成的原因分析:

机器名称高级压印器(高级AE)水晶装配器(高级AE)装配机(现代工业化)
卡死分析

有类型限制的输入槽*12实质上是 4 个独立的压印器输入槽,
同一份红石粉/硅板,优先输入到合成槽 1>2>3>4。
当合成槽 1 被占用后,合成槽 2 中的部分原料(并非全部原料)会自动迁移到合成槽 1 中,

导致合成槽 1,合成槽 2 中分别存在部分合成材料,但无法合成产物。

一个机器内只输入了 1-2 种合成原料占满了输入槽,
第三种原料无法输入,导致无法合成。

一个机器内只输入了 1 种合成原料占满了输入槽,
第二种原料无法输入,导致无法合成,
因为红石粉被替换为红石流体,不占用物品输入槽,而是有专用的流体输入槽,
这使得装配机卡合成的概率小于水晶装配器。

初始状态压印器 1:64 红石粉+64 硅板+64 逻辑板。压印器 2:64 红石粉+64 硅板+64 工程板64*3 红石粉+64*3 硅板+64*3 逻辑板64*4 硅板+64*4 逻辑板+对应数量红石流体
中间状态压印器 1:64 红石粉+64 硅板+32 逻辑板。压印器 2:32 红石粉+32 硅板+64 工程板64*3 红石粉+64*5 硅板+64*1 逻辑板64*5 硅板+64*4 逻辑板+对应数量红石流体
卡死状态压印器 1:32 红石粉+32 硅板+0 逻辑板。压印器 2:0 红石粉+0 硅板+32 工程板64*4 红石粉+64*5 硅板+0 逻辑板64*9 硅板+0 逻辑板+对应数量红石流体

总结:当使用 sfm 堆量合成物品时,优先使用基础的,限制多的,速度慢一点的机器,尤其是 sfm 机器阵列需要同时处理多材料输入类型的合成时。