Fluid network
A fluid network carries a liquid or gas through connected pipes from where it is pumped or made to the machines that use it.
A fluid network carries a liquid or gas through connected pipes from where it is pumped or made to the machines that use it. The games here either pass the fluid from pipe to pipe, so it takes time to cross a line and every pipe limits the rate, or merge connected pipes into one unit whose fluid every outlet can draw on at once1,2,3.
- Pipe to pipeBuildCraft (2011). Waterproof pipes keep liquid in buffers at each side and the center, passing at most 20 units a tick (80 in a golden pipe) from buffer to buffer and pipe to pipe.1,4,5,6
- Merged segmentThermal Expansion (2012). Thermal Dynamics merges connected fluiducts into one grid with one tank, and every outlet duct draws on it.7,2,8,9Factorio (2016). Since 2.0, connected pipes and tanks form one segment holding one fluid, and pumps split segments; before, fluid flowed from pipe to pipe.3,10,11,12
#Pipe to pipe and merged
In BuildCraft 2.2.4 each waterproof pipe holds its own liquid, in a buffer at each of its six sides and one at its center1. Liquid that enters by a side moves to the center, the center deals it out to the sides that lead on, and each of those passes it into the next pipe1. Every buffer releases at most 201 units a tick, and the liquid waits in a delay line at every step, so a line fills one pipe at a time and carries no more than its pipes pass1. Factorio’s fluids also moved from pipe to pipe until 2.0, and Wube’s posts say throughput there fell as pipes got longer3.
Thermal Dynamics, the duct mod split from Thermal Expansion, merges connected fluiducts into one grid that holds one fluid7. The grid keeps that fluid in one tank, sized by its duct count, and fluid put into any duct goes straight into the tank2. Every duct beside a machine draws on the same tank each tick, so fluid never travels along the grid8. Factorio 2.0’s segments work the same way by Wube’s description: pipes, underground pipes, and storage tanks form one segment holding one fluid, fluid pushed in is available at once anywhere along it, and a pump splits one segment from the next3.
A merged network still limits its rate, by how full its store is. A Thermal Dynamics outlet duct hands out between half and all of the fluid’s throughput, more as the tank fills8. Factorio 2.0 caps each transfer at 1003 units and scales it by how full both the source and the sink are3. Thermal Dynamics has no counterpart to that fixed cap or to the scaling by the sink13.
#Across games
| Game and version | Where the fluid is | How it moves inside the network | Rate limit |
|---|---|---|---|
| BuildCraft 2.2.4 | seven buffers in each pipe1 | buffer to buffer and pipe to pipe, through a delay line at each step1 | 201 units a tick per buffer, 801 in a golden pipe1 |
| Factorio before 2.0 | in each pipe3 | pipe to pipe3 | throughput fell with pipe length and could depend on the order pipes were built3,12 |
| Thermal Dynamics r2.3.9 | one tank per grid2 | it doesn’t: every outlet draws on the tank8 | each outlet from half to all of the fluid’s throughput, by the tank’s fill8 |
| Factorio 2.0 | one store per segment3 | it doesn’t: fluid pushed in is available anywhere along the segment3 | 1003 units per transfer, scaled by the fill of source and sink3 |
#Quirks
- A BuildCraft 2.2.4 pipe offers its outputs equal shares in an order reshuffled every tick, but the test that marks an output full checks the running total instead of that output’s own amount, so a second offer of the leftover can skip an output that took its whole share and go to one that took none4.
- In Factorio’s pipe-to-pipe system, the fluid going to each side of a junction depended on the order the pipes were built; in the 2.0 segments junctions don’t matter12.
- Factorio’s merged pipelines had to end somewhere. When Wube added a limit to a Factorio pipeline’s extent in 2024, a pipeline that overran it stopped all flow until a pump split it10. In the same 2024 post Wube cut the pump’s rate to a tenth11.
- A Thermal Dynamics duct placed between grids of different fluids closes its sides to every neighbour whose fluid differs from the one it picked, so the grids stay apart7. Breaking a grid apart divides its tank equally among its ducts, which carry their shares into the grids they join next9.
#History
BuildCraft 2.1.0 (2011) added liquid pipes and pumps, by the account of asie, a later maintainer14. Its 2.2.4 tag of October 2011 has five waterproof pipes, each made from a transport pipe and a pipe sealant5.
By Team CoFH’s account, Thermal Expansion began as a BuildCraft addon15. Its 2.1.0 update added Liquiducts, ducts for fluids, by a note captured on 2 January 201316. Thermal Expansion 3’s fluid ducts were the Fluiducts17, and 4.0.0 moved the ducts into Thermal Dynamics18. No source links the ducts’ design to BuildCraft’s pipes, so the two are compared here, not traced one from the other. Thermal Expansion’s CurseForge description says its fluid system is now part of Minecraft Forge, a statement Forge’s documentation makes only for the energy system19.
For Factorio 2.0, Wube’s FFF #416 (2024) says Rseding proposed an algorithm “very close to” one from Thermal Expansion, in which pipes, underground pipes, and storage tanks merge into segments20. The post doesn’t say how much of the mod’s design Factorio kept. The system it goes on to describe merges segments as Thermal Dynamics merges grids, but limits rates differently13.
Conveyor belts and BuildCraft’s transport pipes carry items instead, each item an object that travels along the line21.
:::gap{internal}[Earlier duct code]
Thermal Dynamics r2.3.9 is the only duct code read. Thermal Expansion 2’s Liquiducts, Thermal Expansion 3’s Fluiducts, and the 1.7.10 Thermal Dynamics branch, which has classes named FluidGrid and FluidTankGrid, would date the merged grid. Later BuildCraft versions’ liquid pipes aren’t read either.
:::
Games
| Game | Year | How | Note |
|---|---|---|---|
| BuildCraft | 2011 | Pipe to pipe | waterproof pipes keep liquid in buffers at each side and the center, passing at most 20 units a tick (80 in a golden pipe) from buffer to buffer and pipe to pipe |
| Thermal Expansion | 2012 | Merged segment | Thermal Dynamics merges connected fluiducts into one grid with one tank, and every outlet duct draws on it |
| Factorio | 2016 | Merged segment | since 2.0, connected pipes and tanks form one segment holding one fluid, and pumps split segments; before, fluid flowed from pipe to pipe |
Every claim about this mechanic, by game (3)
References
- ^ abcdefghijkIn BuildCraft 2.2.4 a waterproof pipe stores its own liquid, in seven buffers: one at each of its six sides and one at its center. A buffer accepts liquid only up to a quarter bucket (250 units) plus its flow rate, 270 units in most pipes, and refuses a different liquid while it holds some. Each tick a buffer releases at most its flow rate, 20 units (80 in a golden pipe). Liquid waits in a delay line once after it arrives and again after it is released: written to the slot before the current tick's, it is read five ticks later (one tick later in a golden pipe). Liquid that came in by a side goes on to the center; the center hands it to the sides that lead out; and a side that leads out passes it into the next pipe or, when that pipe takes none, back to its own center, a side refused more than 20 times in a row being treated as an input. What nothing takes stays in the buffer for a later tick. So liquid crosses a line one pipe at a time, never faster than its pipes' flow rate.CodeBuildCraft 2.2.4revised
How we know
22 public static final int BUCKET_VOLUME = 1000;buildcraft/common/net/minecraft/src/buildcraft/api/API.java line 22 at 0a20887 · upstream ↗ 31 public static int LIQUID_IN_PIPE = API.BUCKET_VOLUME / 4; ⋮ 33 public int travelDelay = 6; 34 public int flowRate = 20;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 31–34 at 0a20887 · upstream ↗ 87 if (qty > 0 && this.liquidId != liquidId && this.liquidId != 0) { 88 return 0; ⋮ 91 if (this.liquidId != liquidId) { 92 reset (); ⋮ 95 this.liquidId = liquidId; ⋮ 97 int date = (int) (worldObj.getWorldTime() % travelDelay); 98 int newDate = date > 0 ? date - 1 : travelDelay - 1; ⋮ 100 if (qty + toFill > LIQUID_IN_PIPE + flowRate) { 101 toFill = LIQUID_IN_PIPE + flowRate - qty; ⋮ 104 if (doFill) { 105 qty += toFill; 106 in [newDate] += toFill;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 87–107 at 0a20887 · upstream ↗ 113 int date = (int) (worldObj.getWorldTime() % travelDelay); 114 int newDate = date > 0 ? date - 1 : travelDelay - 1; ⋮ 116 if (ready - toEmpty < 0) { 117 toEmpty = ready; ⋮ 120 ready -= toEmpty; ⋮ 122 out [newDate] += toEmpty;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 113–122 at 0a20887 · upstream ↗ 130 int date = (int) (worldObj.getWorldTime() % travelDelay); ⋮ 132 ready += in [date]; 133 in [date] = 0; ⋮ 135 if (out [date] != 0) { 136 int extracted = 0; ⋮ 138 if (orientation < 6) { 139 if (isInput [orientation]) { 140 extracted = center.fill(out [date], true, liquidId); 141 } if (isOutput[orientation]) { 142 Position p = new Position(xCoord, yCoord, zCoord, 143 Orientations.values()[orientation]); 144 p.moveForwards(1); ⋮
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 130–162 at 0a20887 · upstream ↗ 192 qty -= extracted; 193 ready += out[date] - extracted; 194 out[date] = 0;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 192–194 at 0a20887 · upstream ↗ 388 int [] rndIt = getSplitVector(worldObj); ⋮ 390 for (int r = 0; r < 6; ++r) { 391 int i = rndIt [r]; ⋮ 393 side [i].empty(flowRate); ⋮ 396 center.empty(flowRate);
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 388–396 at 0a20887 · upstream ↗ 411 if (side [i].bouncing) { 412 isInput [i] = true;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 411–412 at 0a20887 · upstream ↗ 20 ((PipeTransportLiquids) transport).flowRate = 80; 21 ((PipeTransportLiquids) transport).travelDelay = 2;
buildcraft/common/net/minecraft/src/buildcraft/transport/pipes/PipeLiquidsGold.java lines 20–21 at 0a20887 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcdIn Thermal Dynamics r2.3.9 a fluid-duct grid stores its fluid in one tank, not in its ducts. The tank holds the grid's duct count times a per-duct volume, which for ordinary fluiducts is 25 times the fluid's throughput, at most 3,000 mB. The throughput is 120,000 divided by the fluid's viscosity (taken as at least 100), clamped to 80-600 mB a tick; steam is set to 600. A fluid of viscosity 1,000 gets 120 mB a tick and 3,000 mB a duct, the tank's defaults. Fluid of the grid's kind put into any open side of any duct goes straight into that tank, up to its free space, with no other limit on the rate.CodeThermal Dynamics r2.3.9 · CoFH Core r4.3.9
How we know
27 public final FluidTankGrid myTank = new FluidTankGrid(1000, this);src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java line 27 at 5ae9f68 · upstream ↗ 81 public void balanceGrid() { ⋮ 84 myTank.setCapacity(size() * myTank.fluidPerDuct);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 80–85 at 5ae9f68 · upstream ↗ 135 public float getThroughPutModifier() { ⋮ 137 return 1.0F; ⋮ 140 public int getMaxFluidPerDuct() { ⋮ 142 return 3000;
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 135–143 at 5ae9f68 · upstream ↗ 10 public int fluidThroughput = 120; 11 public int fluidPerDuct = 3000; ⋮ 16 fluidFlowrate.put("steam", 600);
src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java lines 10–17 at 5ae9f68 · upstream ↗ 58 viscosity = Math.max(fluid.getFluid().getViscosity(), 100); 59 fluidThroughput = MathHelper.clamp(120000 / viscosity, 80, 600); ⋮ 62 fluidThroughput = fluidFlowrate.get(fluid.getFluid().getName()); ⋮ 64 fluidThroughput *= myMaster.getThroughPutModifier(); 65 fluidPerDuct = Math.min(25 * fluidThroughput, myMaster.getMaxFluidPerDuct());
src/main/java/cofh/thermaldynamics/duct/fluid/FluidTankGrid.java lines 53–68 at 5ae9f68 · upstream ↗ 489 public int fill(FluidStack resource, boolean doFill) { ⋮ 491 if (resource != null && isOpen(from) && matchesFilter(from, resource)) { 492 return grid.myTank.fill(resource, doFill);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 489–494 at 5ae9f68 · upstream ↗ 190 if (!fluid.isFluidEqual(resource)) { 191 return 0; ⋮ 193 int filled = capacity - fluid.amount; ⋮ 195 if (resource.amount < filled) { 196 fluid.amount += resource.amount; ⋮ 199 fluid.amount = capacity;
src/main/java/cofh/core/fluid/FluidTankCore.java lines 186–201 at ccf809a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcdefghijklmWube's FFF #416 and #430 (2024) describe 2.0's fluids: connected pipes, underground pipes and storage tanks form one segment holding one fluid, and a pump splits one segment from the next. Fluid put into a segment is available at once anywhere along it: there is no flow from pipe to pipe, so pipe length and junctions no longer cut throughput as they did in 1.x. Each transfer is capped at 100 units (6,000 a second) and scaled by how full the source and the sink are, so a machine pushing in slows as the segment fills. These rules are the posts'; they weren't re-checked against 2.1.23.Dev blogFriday Facts #416 · Friday Facts #430revised
How we know
Pipes, underground pipes, and storage tanks are merged into fluid "segments". … There is no longer a realistic fluid "flow" through pipes; fluid pushed to a segment will be immediately available at any point along a segment. … In the old system, throughput would get worse as pipes got longer. … In the new system, junctions are entirely unimportant; a segment only cares about the machines connected to it.
Fluids 2.0 · archived copy ↗ You can split a pipeline using a pump.
Pipeline extents · archived copy ↗ Output rate used to be unlimited, but it is now inversely proportional to the fullness ratio of the sink. … there is a hardcoded limit of 100 fluid per flow operation (6000/s). … This limit is multiplied with the fullness ratios of the source and sink to produce the actual flow value
Algorithm tweaks · archived copy ↗ The quote matches our archived copy.
Revised after a check by a second AI model, 2026-10-26.
- ^ abIn BuildCraft 2.2.4 a liquid pipe's center buffer divides what it releases among its outputs: the sides that the pipe leaves open (an iron pipe only the one it points to), that face a pipe or liquid container it connects to, and that liquid didn't come in by. Each output is offered an equal share, rounded up, until the amount runs out, in an order reshuffled every tick by 20 random swaps. If the outputs took less than all of it, the rest is offered once more, split among the outputs not marked full; the test that marks one full compares the total taken so far in the pass, not that output's own amount, with a share, so it can mark full an output that took its whole share and leave unmarked one that took none. A side counts as an input from when liquid enters by it until it has been empty for more than 20 ticks, and every side stops counting as one when the pipe has had no output for more than 20 ticks.CodeBuildCraft 2.2.4
How we know
164 int outputNumber = 0; ⋮ 166 for (int i = 0; i < 6; ++i) { 167 if (isOutput [i]) { 168 outputNumber++; ⋮ 172 filled = new boolean [] {false, false, false, false, false, false}; ⋮ 174 // try first, to detect filled outputs 175 extracted = splitLiquid(out [date], outputNumber); ⋮ 177 if (extracted < out [date]) { 178 outputNumber = 0; ⋮ 180 // try a second time, if to split the remaining in non 181 // filled if any 182 for (int i = 0; i < 6; ++i) { ⋮
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 164–189 at 0a20887 · upstream ↗ 209 private int splitLiquid (int quantity, int outputNumber) { 210 int extracted = 0; ⋮ 212 int slotExtract = (int) Math 213 .ceil(((double) quantity / (double) outputNumber)); ⋮ 215 int [] splitVector = getSplitVector(worldObj); ⋮ 217 for (int r = 0; r < 6; ++r) { 218 int toExtract = slotExtract <= quantity ? slotExtract : quantity; ⋮ 220 int i = splitVector [r]; ⋮ 222 if (isOutput [i] && !filled [i]) { 223 extracted += side [i].fill(toExtract, true, liquidId); 224 quantity -= toExtract; ⋮ 226 if (extracted != toExtract) { ⋮
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 209–232 at 0a20887 · upstream ↗ 355 public int fill(Orientations from, int quantity, int id, boolean doFill) { 356 isInput[from.ordinal()] = true;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 355–356 at 0a20887 · upstream ↗ 371 int outputNumber = computeOutputs (); ⋮ 373 if (outputNumber == 0) { 374 lockedTime++; 375 } else { 376 lockedTime = 0; ⋮ 379 if (lockedTime > 20) { 380 for (int i = 0; i < 6; ++i) { 381 isInput[i] = false; ⋮ 384 outputNumber = computeOutputs();
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 371–385 at 0a20887 · upstream ↗ 413 } else if (side [i].qty == 0) { 414 side [i].emptyTime++; ⋮ 417 if (side [i].emptyTime > 20) { 418 isInput [i] = false;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 413–419 at 0a20887 · upstream ↗ 431 isOutput [i] = container.pipe.outputOpen(p.orientation) 432 && canReceiveLiquid(p) && !isInput[i];
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 431–432 at 0a20887 · upstream ↗ 523 for (int i = 0; i < 20; ++i) { 524 int a = worldObj.rand.nextInt(6); 525 int b = worldObj.rand.nextInt(6); ⋮ 527 int tmp = splitVector [a]; 528 splitVector [a] = splitVector [b]; 529 splitVector [b] = tmp;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 523–530 at 0a20887 · upstream ↗ 112 public boolean outputOpen(Orientations to) { 113 return to.ordinal() == worldObj 114 .getBlockMetadata(xCoord, yCoord, zCoord);
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeLogicIron.java lines 112–114 at 0a20887 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abBuildCraft 2.2.4 (October 2011) has three families of pipe. Seven transport pipes carry items (wooden, cobblestone, stone, iron, golden, diamond, obsidian); five waterproof pipes carry liquids and are made from a transport pipe of the same material and a pipe sealant made of green dye; three conductive pipes carry energy and are made from a transport pipe and redstone.CodeBuildCraft 2.2.4
How we know
115 CoreProxy.addName(pipeWaterproof, "Pipe Waterproof"); ⋮ 118 Character.valueOf('W'), new ItemStack(Item.dyePowder, 1, 2)});
buildcraft/common/net/minecraft/src/BuildCraftTransport.java lines 113–118 at 0a20887 · upstream ↗ 124 pipeItemsWood = createPipe (DefaultProps.PIPE_ITEMS_WOOD_ID, PipeItemsWood.class, "Wooden Transport Pipe", Block.planks, Block.glass, Block.planks); ⋮ 130 pipeItemsObsidian = createPipe (DefaultProps.PIPE_ITEMS_OBSIDIAN_ID, PipeItemsObsidian.class, "Obsidian Transport Pipe", Block.obsidian, Block.glass, Block.obsidian); ⋮ 132 pipeLiquidsWood = createPipe (DefaultProps.PIPE_LIQUIDS_WOOD_ID, PipeLiquidsWood.class, "Wooden Waterproof Pipe", pipeWaterproof, pipeItemsWood, null); ⋮ 136 pipeLiquidsGold = createPipe (DefaultProps.PIPE_LIQUIDS_GOLD_ID, PipeLiquidsGold.class, "Golden Waterproof Pipe", pipeWaterproof, pipeItemsGold, null); ⋮ 140 pipePowerWood = createPipe (DefaultProps.PIPE_POWER_WOOD_ID, PipePowerWood.class, "Wooden Conductive Pipe", Item.redstone, pipeItemsWood, null); ⋮ 144 pipePowerGold = createPipe(DefaultProps.PIPE_POWER_GOLD_ID, PipePowerGold.class, "Golden Conductive Pipe", Item.redstone, pipeItemsGold, null);
buildcraft/common/net/minecraft/src/BuildCraftTransport.java lines 124–144 at 0a20887 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^The 2.2.4 pump lowers a tube to the liquid below it and, for 10 energy, takes up one block of it as a bucket's worth (1,000 units), removing the block from the world, then pushes the liquid into an adjacent tank, pipe or machine that holds liquids. A pumped deposit is used up.CodeBuildCraft 2.2.4
How we know
84 if (powerProvider.useEnergy(10, 10, true) == 10) { 85 index = getNextIndexToPump(true); 86 worldObj.setBlockWithNotify(index.i, index.j, index.k, 0); 87 internalLiquid = internalLiquid += API.BUCKET_VOLUME;
buildcraft/common/net/minecraft/src/buildcraft/factory/TilePump.java lines 74–93 at 0a20887 · upstream ↗ 124 if (tile instanceof ILiquidContainer) { 125 internalLiquid -= ((ILiquidContainer) tile).fill(
buildcraft/common/net/minecraft/src/buildcraft/factory/TilePump.java lines 115–132 at 0a20887 · upstream ↗ 22 public static final int BUCKET_VOLUME = 1000;buildcraft/common/net/minecraft/src/buildcraft/api/API.java line 22 at 0a20887 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcIn Thermal Dynamics r2.3.9 (Minecraft 1.12.2), connected fluid ducts form one grid. A grid forms outward from one duct, breadth-first over its connected ducts: a duct with no grid joins, and a duct in another grid merges the two grids, the smaller into the larger, the larger keeping its fluid and taking the other's. A grid holds one fluid: a duct joins, and two grids merge, only when their fluids match or one is empty; otherwise the connection is rejected. When a duct is placed, it compares its neighbours' fluids with one of them, the fluid of the duct the player aimed at or else the first neighbour that has one, and blocks its sides to every neighbouring duct whose fluid differs from that.CodeThermal Dynamics r2.3.9revised
How we know
16 theGrid = theMultiBlock.createGrid(); ⋮ 22 while (!blocksToCheck.isEmpty()) { 23 checkMultiBlock(blocksToCheck.remove());
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java lines 14–26 at 5ae9f68 · upstream ↗ 45 if (aBlock.getGrid() == null && theGrid.canAddBlock(aBlock)) { ⋮ 51 if (theGrid.size() >= aBlock.getGrid().size()) { 52 theGrid.mergeGrids(aBlock.getGrid()); ⋮ 54 aBlock.getGrid().mergeGrids(theGrid); ⋮ 59 currentMultiBlock.onConnectionRejected(i);
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockFormer.java lines 44–61 at 5ae9f68 · upstream ↗ 205 public void mergeGrids(MultiBlockGrid<DuctUnitFluid> theGrid) { ⋮ 211 myTank.fill(gridFluid.getFluid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 204–225 at 5ae9f68 · upstream ↗ 419 if (retrace != null && retrace.sideHit != null) { 420 EnumFacing sideHit = retrace.sideHit.getOpposite(); 421 DuctUnitFluid fluids = IDuctHolder.getTokenFromTile(world().getTileEntity(pos().offset(sideHit)), DuctToken.FLUID); 422 if (fluids != null) { 423 fluidStack = fluids.getConnectionFluid();
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 419–425 at 5ae9f68 · upstream ↗ 434 if (fluidStack == null) { 435 fluidStack = connectionFluid; 436 } else if (connectionFluid != null && !fluidStack.isFluidEqual(connectionFluid)) { 437 parent.setConnectionType(facing.ordinal(), ConnectionType.BLOCKED);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 427–442 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcdeIn Thermal Dynamics r2.3.9 a fluid-duct grid hands out fluid once a tick. If its tank holds fluid, each duct beside a block that takes fluid offers its neighbours, side by side from where it last stopped, up to the tank's amount divided by the grid's duct count (the remainder when that rounds to 0), and no more than the grid's throughput for that tick. That throughput is the fluid's full rate while the tank is at least three-quarters full, half of it at a quarter full or less, and rises in a straight line from half to full in between. Fluid inside a grid doesn't travel from duct to duct: every outlet draws on the one tank.CodeThermal Dynamics r2.3.9
How we know
61 for (MultiBlockGrid grid : tickingGrids) { 62 grid.tickGrid();
src/main/java/cofh/thermaldynamics/util/WorldGridList.java lines 59–66 at 5ae9f68 · upstream ↗ 162 if (myTank.getFluid() != null && nodeSet.size() > 0) { 163 toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); ⋮ 165 if (toDistribute <= 0) { 166 toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput()); ⋮ 170 for (DuctUnitFluid m : nodeSet) { 171 if (!m.tickPass(0) || m.getGrid() == null) {
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 162–176 at 5ae9f68 · upstream ↗ 233 // if over 3/4 full, full throughput 234 if (myTank.getFluid().amount >= capacity * 3 / 4) { 235 return myTank.fluidThroughput; ⋮ 237 // if under 1/4 full, half throughput 238 if (myTank.getFluid().amount <= capacity / 4) { 239 return myTank.fluidThroughput >> 1; ⋮ 241 // otherwise scale between half and full
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 227–243 at 5ae9f68 · upstream ↗ 89 if (pass == 0) { 90 int available = grid.toDistribute; ⋮ 93 for (int i = this.internalSideCounter; i < 6 && sent < available; i++) { 94 sent += transfer(i, available - sent, false, grid.myTank.getFluid(), true); ⋮ 102 for (int i = 0; i < this.internalSideCounter && sent < available; i++) { 103 sent += transfer(i, available - sent, false, grid.myTank.getFluid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 89–110 at 5ae9f68 · upstream ↗ 165 tempFluid.amount = available; 166 int amountSent = capability.fill(tempFluid, false); ⋮ 173 tempFluid = grid.myTank.drain(amountSent, true); ⋮ 177 return capability.fill(tempFluid, true);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 164–177 at 5ae9f68 · upstream ↗ 157 C c = cacheTile(tile, side); ⋮ 160 if (isNode(c)) { 161 nodeMask |= (1 << side);
src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java lines 157–162 at 5ae9f68 · upstream ↗ 269 public boolean isNode() { ⋮ 271 return nodeMask != 0;
src/main/java/cofh/thermaldynamics/duct/tiles/DuctUnit.java lines 269–272 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abIn Thermal Dynamics r2.3.9, removing a duct that had two or more connected ducts queues its grid to be rebuilt; a duct with one connection or none leaves the grid as it is. A rebuilt grid first gives every duct its share of the tank, the amount divided equally among the ducts that store fluid, with the first duct also taking the remainder, and each duct pours its share into whatever grid it joins next. A duct saved with the world keeps its share the same way.CodeThermal Dynamics r2.3.9
How we know
145 destroyNode(oldBlock); ⋮ 160 if (oldBlock.isSideConnected(i)) { ⋮ 164 if (s <= 1) { 165 balanceGrid(); ⋮ 170 worldGrid.gridsToRecreate.add(this);
src/main/java/cofh/thermaldynamics/multiblock/MultiBlockGrid.java lines 143–171 at 5ae9f68 · upstream ↗ 47 for (MultiBlockGrid<?> grid : gridsToRecreate) { 48 for (IGridTile multiBlock : grid.idleSet) { ⋮ 50 grid.destroyNode(multiBlock); ⋮ 52 for (IGridTile multiBlock : grid.nodeSet) { ⋮ 54 grid.destroyNode(multiBlock);
src/main/java/cofh/thermaldynamics/util/WorldGridList.java lines 44–58 at 5ae9f68 · upstream ↗ 148 if (hasValidFluid()) { 149 ((DuctUnitFluid) node).setFluidForGrid(getNodeShare((DuctUnitFluid) node));
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 145–152 at 5ae9f68 · upstream ↗ 267 if (!duct.canStoreFluid()) { ⋮ 270 int size = getStorableNumberDucts();
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 265–275 at 5ae9f68 · upstream ↗ 51 if (aMultiBlock.getFluidForGrid() != null) { ⋮ 53 myTank.setFluid(aMultiBlock.getFluidForGrid()); ⋮ 55 myTank.fill(aMultiBlock.getFluidForGrid(), true);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 46–59 at 5ae9f68 · upstream ↗ 375 mySavedFluid = grid.getNodeShare(this); ⋮ 377 mySavedFluid.writeToNBT(nbt);
src/main/java/cofh/thermaldynamics/duct/fluid/DuctUnitFluid.java lines 374–377 at 5ae9f68 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abIn 2.1.23 a pipeline's extent is limited by
default_pipeline_extent= 320, which a fluidbox can override; a pipeline takes the smallest extent of its parts. The data doesn't say how the extent is measured. When Wube introduced the limit in 2024, FFF #430 described it as a 250 × 250 tile area whose overrun stops all flow until pumps split the pipeline.Codefactorio-data 2.1.23 · Factorio Prototype API 2.1.23 · Friday Facts #430revisedHow we know
560 default_pipeline_extent = 320, -- Can be overridden on a per-fluidbox basiscore/prototypes/utility-constants.lua line 560 at 39dbd0f · upstream ↗ The max extent that a pipeline with this fluidbox can span. A given pipeline's extent is calculated as the min extent of all the fluidboxes that comprise it.
FluidBox.max_pipeline_extent · archived copy ↗ Pipelines are constrained to a 250x250 tile area, and exceeding this limit will cause all flow through the pipeline to cease. … You can split a pipeline using a pump.
Pipeline extents · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abA pipe and an underground pipe each hold 100 units and a storage tank 25,000. An underground pipe pair has a
max_underground_distanceof 10. A pump moves 20 units a tick (1,200 a second) for 29 kW, a tenth of its earlier rate: Wube cut it in 2024 when pipelines gained their extent limit.Codefactorio-data 2.1.23 · Factorio Prototype API 2.1.23 · Friday Facts #430How we know
2766 volume = 100,base/prototypes/entity/entities.lua line 2766 at 39dbd0f · upstream ↗ 3026 volume = 100,base/prototypes/entity/entities.lua line 3026 at 39dbd0f · upstream ↗ 3035 max_underground_distance = 10,base/prototypes/entity/entities.lua line 3035 at 39dbd0f · upstream ↗ 7094 volume = 25000,base/prototypes/entity/entities.lua line 7094 at 39dbd0f · upstream ↗ 7255 energy_usage = "29kW", 7256 pumping_speed = 20,
base/prototypes/entity/entities.lua lines 7255–7256 at 39dbd0f · upstream ↗ The amount of fluid this pump transfers per tick.
PumpPrototype.pumping_speed · archived copy ↗ Pumps have been nerfed to 1200/s (10x decrease), but this can be increased with quality.
Balance tweaks · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcBuildCraft 2.2.4 and Factorio before 2.0 both move liquid from pipe to pipe, and split it differently where pipes branch. A BuildCraft pipe offers its outputs equal shares, in an order it reshuffles every tick. In Factorio's old system, by Wube's FFF #416 (2024), the amount going to each side of a junction depended on the order the entities were built, as could a pipe's throughput. In the segment system the same post describes for 2.0, junctions don't matter: a segment answers only to the machines connected to it, each limited by how full the segment is, which the post says splits fluid much more evenly, though not perfectly.Dev blogBuildCraft 2.2.4 · Friday Facts #416
How we know
212 int slotExtract = (int) Math 213 .ceil(((double) quantity / (double) outputNumber)); ⋮ 215 int [] splitVector = getSplitVector(worldObj);
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportLiquids.java lines 212–215 at 0a20887 · upstream ↗ Furthermore, the throughput can vary depending on the order that the pipes were built.
Reaching the breaking point · archived copy ↗ The amount of fluid flowing to each side of the junction would be different based on the order that the entities were built. … In the new system, junctions are entirely unimportant; a segment only cares about the machines connected to it. … This results in much more even splitting, which while still not perfect, is pretty damn close.
Junctions · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abFactorio 2.0's fluid segments match the description of Thermal Dynamics r2.3.9's design: connected pipes or ducts merge into one unit that holds one fluid, its volume summed from its parts, and fluid put in is available anywhere along it at once (FFF #416, 2024). In the design as FFF #416 first described it, machines push in at an unlimited rate, as Thermal Dynamics takes fluid up to the tank's free space, and pull at a rate proportional to how full the segment is, half the maximum at half full; each Thermal Dynamics outlet duct instead hands its neighbours between half and all of the fluid's throughput, scaled by how full the tank is (three-quarters of it at half full), and no more than the tank's amount divided by its duct count (or the remainder, when that rounds to 0). FFF #430, still before 2.0's release, gave the rules as changed: pushing in is now slowed as the sink fills, and every transfer is capped at 100 units, a limit that already existed, multiplied by the fullness of both source and sink. The scaling by the source's fullness has a counterpart in Thermal Dynamics; the 100-unit cap and the scaling by the sink's fullness don't. FFF #416 doesn't say which version of the mod Rseding played.Dev blogFriday Facts #416 · Friday Facts #430 · Thermal Dynamics r2.3.9 · CoFH Core r4.3.9revised
How we know
Pipes, underground pipes, and storage tanks are merged into fluid "segments". Each segment inherits its volume from the fluid boxes that comprise it and can hold one fluid. Machines can push fluid into a segment at an unlimited rate, and can pull from a segment at a rate proportional to how full the segment is. … In other words, if a segment is half full, then the pulling rate is half of the maximum. … There is no longer a realistic fluid "flow" through pipes; fluid pushed to a segment will be immediately available at any point along a segment.
The new algorithm · archived copy ↗ Output rate used to be unlimited, but it is now inversely proportional to the fullness ratio of the sink. … Something that I failed to adequately explain before is that while there is no limitation on the total flow through a pipeline in a given tick, there is a hardcoded limit of 100 fluid per flow operation (6000/s). … This limit is multiplied with the fullness ratios of the source and sink to produce the actual flow value
Algorithm tweaks · archived copy ↗ 81 public void balanceGrid() { ⋮ 84 myTank.setCapacity(size() * myTank.fluidPerDuct);
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 80–85 at 5ae9f68 · upstream ↗ return aBlock instanceof DuctUnitFluid &&
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java#L215-L219 · upstream ↗ 163 toDistribute = Math.min(myTank.getFluidAmount() / size(), getFluidThroughput()); ⋮ 165 if (toDistribute <= 0) { 166 toDistribute = Math.min(myTank.getFluidAmount() % size(), getFluidThroughput());
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 162–167 at 5ae9f68 · upstream ↗ 233 // if over 3/4 full, full throughput 234 if (myTank.getFluid().amount >= capacity * 3 / 4) { 235 return myTank.fluidThroughput; ⋮ 237 // if under 1/4 full, half throughput 238 if (myTank.getFluid().amount <= capacity / 4) { 239 return myTank.fluidThroughput >> 1; ⋮ 241 // otherwise scale between half and full
src/main/java/cofh/thermaldynamics/duct/fluid/GridFluid.java lines 227–243 at 5ae9f68 · upstream ↗ 193 int filled = capacity - fluid.amount; ⋮ 199 fluid.amount = capacity;
src/main/java/cofh/core/fluid/FluidTankCore.java lines 193–201 at ccf809a · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^By asie's account, BuildCraft 2.0.0, at the end of July 2011, was the first version to depend on Minecraft Forge and added the power system, Minecraft Joules; 2.0.1 brought oil and wrenches, 2.1.0 liquid pipes and pumps, and 2.2.0 conductive pipes, fuel and the refinery.Dev blogasie, "BuildCraft History and Design", 30 January 2015
How we know
version 2.0.0 came out at the very end of July 2011. It was the first version to depend on Minecraft Forge. The major revolution here was the addition of a power system - Minecraft Joules. … In 2.0.1, we got oil and wrenches, and 2.1.0 gave us fluid pipes and pumps. … BuildCraft 2.2.0 added power pipes, fuel and the refinery.
on 2.0.0 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Team CoFH says Thermal Expansion began as a BuildCraft addon: it needed BuildCraft, or addons such as Forestry, to power its machines with Minecraft Joules, and became independent only in its third iteration.PublisherTeam CoFH, "About"
How we know
TE was a BuildCraft addon: it required BuildCraft itself or addons like Forestry in order to power the machines with Minecraft Joules, BuildCraft’s old power system. It wasn’t until the third iteration that the mod became fully independent.
History · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion 2.1.0 added Tanks and Liquiducts, ducts for fluids, by King Lemming's update note in the Minecraft Forum thread as captured on 2 January 2013.Dev blogKing Lemming, Thermal Expansion 2.1.6 post (2013)revised
How we know
So, 2.1.0 is out! It adds Tanks, Liquiducts, and a couple other things.
#1, Update News (2.1.0) · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Thermal Expansion 3.0.0.0 came out on 25 December 2013, a rewrite in which, by its changelog, "everything has been completely redone"; it introduced Redstone Flux, and its fluid ducts were the Fluiducts, in opaque and transparent kinds.PublisherTeam CoFH, Thermal Expansion changelog (2014)
How we know
3.0.0.0: 12/25/2013 - Merry Christmas! Okay, so everything has been completely redone here. … The new energy system is Redstone Flux. (RF).
3.0.0.0: 12/25/2013 · archived copy ↗ Two types of Fluiducts - Opaque and Transparent.
3.0.0.0, Fluid · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Thermal Expansion 4.0.0 ported the mod to Minecraft 1.7.10 and moved its Energy Conduits, Fluiducts, and Itemducts into a separate mod, Thermal Dynamics. Team CoFH's downloads page, captured on 27 December 2014, says it was released for 1.7.10 without the ducts, which would follow as a separate mod. Team CoFH credit RWTema, the author of Extra Utilities, with most of the work of porting the ducts to 1.7.10, from around late 2014.PublisherCoFH changelogs (Version repo) · Team CoFH, Downloads page (2014) · Team CoFH, About (2017)
How we know
8934.0.0: ⋮ 895Welcome to Thermal Expansion 4! 896-Ported to Minecraft 1.7.10.
thermalexpansion_changelog.txt lines 893–896 at 1bdb613 · upstream ↗ 993 -Energy Conduits, Fluiducts, and Itemducts. 994 Now in Thermal Dynamics.
thermalexpansion_changelog.txt lines 993–994 at 1bdb613 · upstream ↗ Thermal Expansion is now RELEASED for 1.7.10. Ducts are not in the release, they will be a separate mod when they are finished.
The Mods · archived copy ↗ Tema stepped in around late 2014 when there wasn't anyone around that had time to work on Thermal Dynamics. He did most of the work on porting the missing piece of the Thermal mods in 1.7.10: ducts!
Team members, RWTema · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Minecraft Forge's energy interface, IEnergyStorage, states in its documentation that it is derived from the Redstone Flux system that King Lemming designed for Thermal Expansion, and that it was made with the consent of King Lemming and Team CoFH. Thermal Expansion's own CurseForge description says its fluid and energy systems are now both part of Forge.PublisherMinecraftForge, IEnergyStorage · Team CoFH, Thermal Expansion on CurseForge (2019)
How we know
15 * Derived from the Redstone Flux power system designed by King Lemming and originally utilized in Thermal Expansion and related mods. 16 * Created with consent and permission of King Lemming and Team CoFH. Released with permission under LGPL 2.1 when bundled with Forge.
src/main/java/net/minecraftforge/energy/IEnergyStorage.java lines 15–16 at 243490d · upstream ↗ Many of the gameplay ideas and mechanics set forth by Thermal Expansion have become staples in the modded Minecraft community, including both the Fluid and Energy (RF) systems, now both included as part of Minecraft Forge.
description · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^Friday Facts #416 (2024) says that in the discussions on Factorio 2.0's fluids, Rseding proposed an algorithm "very close to" one from the Minecraft mod Thermal Expansion by Team CoFH, in which pipes, underground pipes, and storage tanks merge into fluid "segments".Dev blogFriday Facts #416
How we know
Rseding proposed an algorithm very close to one he had played with several times, an algorithm from the Minecraft mod Thermal Expansion by team CoFH: Pipes, underground pipes, and storage tanks are merged into fluid "segments".
The new algorithm · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^In 2.2.4 an item in a transport pipe travels to the pipe's center and there picks an exit: any side but the one it came in by that leads to a pipe carrying items, a machine that accepts them, or an inventory with room for it. With several, it takes one at random (single-player) or by a fixed hash of the item's id and the pipe's position (multiplayer). With none, the item drops out of the pipe into the world, and an item that reaches a full inventory drops what doesn't fit.CodeBuildCraft 2.2.4
How we know
119 if (Orientations.values()[o] != pos.orientation.reverse() 120 && container.pipe.outputOpen(Orientations.values()[o])) { ⋮ 159 if (entity instanceof IPipeEntry) { 160 return true; 161 } else if (entity instanceof TileGenericPipe) { ⋮ 164 return pipe.pipe.transport instanceof PipeTransportItems; 165 } else if (entity instanceof IInventory) { 166 if (new StackUtil(item.item).checkAvailableSlot((IInventory) entity, ⋮ 168 return true;
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportItems.java lines 114–173 at 0a20887 · upstream ↗ 225 Orientations nextOrientation = resolveDestination (data); ⋮ 227 if (nextOrientation == Orientations.Unknown) { 228 scheduleRemoval(data.item); ⋮ 230 EntityItem dropped = data.item.toEntityItem(data.orientation);
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportItems.java lines 225–237 at 0a20887 · upstream ↗ 263 } else if (tile instanceof IInventory) { ⋮ 271 data.item.remove(); 272 } else { 273 data.item.item = utils.items; 274 EntityItem dropped = data.item.toEntityItem(data.orientation);
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportItems.java lines 263–282 at 0a20887 · upstream ↗ 377 if (listOfPossibleMovements.size() == 0) { 378 return Orientations.Unknown; 379 } else { ⋮ 382 if (APIProxy.isClient(worldObj) || APIProxy.isServerSide()) { 383 i = Math.abs(data.item.entityId + xCoord + yCoord + zCoord 384 + data.item.deterministicRandomization) 385 % listOfPossibleMovements.size(); 386 } else { 387 i = worldObj.rand.nextInt(listOfPossibleMovements.size());
buildcraft/common/net/minecraft/src/buildcraft/transport/PipeTransportItems.java lines 373–393 at 0a20887 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
Sources
Code
- SpaceToad. 2011. “BuildCraft 2.2.4 source.” GitHub (BuildCraft/BuildCraft). Commit 0a20887. BuildCraft/BuildCraft @ 0a20887 · licence, all uses
- Team CoFH. 2018. “Thermal Dynamics 1.12.2 (CoFH/ThermalDynamics-1.12-Legacy), tag r2.3.9.” Commit 5ae9f68. CoFH/ThermalDynamics-1.12-Legacy @ 5ae9f68 · licence, all uses
- Team CoFH. 2018. “CoFH Core 1.12.2 (CoFH/CoFHCore-1.12-Legacy), tag r4.3.9.” Commit ccf809a. CoFH/CoFHCore-1.12-Legacy @ ccf809a · licence, all uses
- Software, Wube. 2026. “factorio-data: Factorio's Lua prototype definitions, version 2.1.23.” Commit 39dbd0f. wube/factorio-data @ 39dbd0f · licence, all uses
- Forge Development LLC and contributors. 2026. “Minecraft Forge: IEnergyStorage (Forge Energy).” Commit 243490d. MinecraftForge/MinecraftForge @ 243490d · licence, all uses
Primary
- raiguard. 2024. “Friday Facts #416 - Fluids 2.0.” Factorio blog. Wube Software. original · archived · licence, all uses
- raiguard. 2024. “Friday Facts #430 - Drowning in Fluids.” Factorio blog. Wube Software. original · archived · licence, all uses
- Software, Wube. 2026. “Factorio Prototype API, version 2.1.23 (machine-readable JSON).” Wube Software (lua-api.factorio.com). original · archived · licence, all uses
- Siekierka, Adrian. 2015. “BuildCraft History and Design.” asie's blog. original · archived · licence, all uses
- Team CoFH. 2026. “About Team CoFH.” Team CoFH. original · archived · licence, all uses
- Lemming, King. 2013. “Thermal Expansion 2.1.6: King Lemming's Minecraft Forum post.” Minecraft Forum. original · archived · licence, all uses
- Team CoFH. 2014. “Thermal Expansion changelog (Team CoFH wiki, January 2014).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2024. “CoFH/Version: Thermal Expansion and Thermal Dynamics changelogs.” Commit 1bdb613. CoFH/Version @ 1bdb613 · licence, all uses
- Team CoFH. 2014. “Downloads (Team CoFH site, December 2014).” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2017. “About Team CoFH: the team's history and members.” Team CoFH. original · archived · licence, all uses
- Team CoFH. 2019. “Thermal Expansion on CurseForge (project description and statistics, 2019).” CurseForge (Twitch). original · archived · licence, all uses