Merge branch 'mc1.15/dev' into mc1.16/dev

This commit is contained in:
simibubi 2020-12-09 21:00:06 +01:00
commit afa477d81b
54 changed files with 2075 additions and 2114 deletions

View file

@ -142,7 +142,7 @@ fc652317e03b57c76e23a805da16a28d15254029 assets/create/blockstates/fancy_scoria_
fc9ac0a7e7191b93516719455a17177fa6524ecc assets/create/blockstates/fancy_weathered_limestone_bricks_slab.json fc9ac0a7e7191b93516719455a17177fa6524ecc assets/create/blockstates/fancy_weathered_limestone_bricks_slab.json
b2a7c321b1795f20e7433f81a55ce4683de081b8 assets/create/blockstates/fancy_weathered_limestone_bricks_stairs.json b2a7c321b1795f20e7433f81a55ce4683de081b8 assets/create/blockstates/fancy_weathered_limestone_bricks_stairs.json
8e532856c3c2b4e4e59c65a2a81a694e35d14658 assets/create/blockstates/fancy_weathered_limestone_bricks_wall.json 8e532856c3c2b4e4e59c65a2a81a694e35d14658 assets/create/blockstates/fancy_weathered_limestone_bricks_wall.json
3d97226b5e8d8f70ed08e45e78db1faf78d5e28b assets/create/blockstates/fluid_pipe.json 6106fc0a0f9d83da89c3e8af98e7c45232602c23 assets/create/blockstates/fluid_pipe.json
f0eaab18e16c4f3f65ebf3b55b08f0dc445720fe assets/create/blockstates/fluid_tank.json f0eaab18e16c4f3f65ebf3b55b08f0dc445720fe assets/create/blockstates/fluid_tank.json
5408d92ab02af86539ac42971d4033545970bb3a assets/create/blockstates/fluid_valve.json 5408d92ab02af86539ac42971d4033545970bb3a assets/create/blockstates/fluid_valve.json
e9da1794b6ece7f9aa8bcb43d42c23a55446133b assets/create/blockstates/flywheel.json e9da1794b6ece7f9aa8bcb43d42c23a55446133b assets/create/blockstates/flywheel.json
@ -337,7 +337,7 @@ e8b0a401c10d1ba67ed71ba31bd5f9bc28571b65 assets/create/blockstates/powered_toggl
d06cd9a1101b18d306a786320aab12018b1325d6 assets/create/blockstates/purple_sail.json d06cd9a1101b18d306a786320aab12018b1325d6 assets/create/blockstates/purple_sail.json
92957119abd5fbcca36a113b2a80255fd70fc303 assets/create/blockstates/purple_seat.json 92957119abd5fbcca36a113b2a80255fd70fc303 assets/create/blockstates/purple_seat.json
61035f8afe75ff7bbd291da5d8690bcbebe679eb assets/create/blockstates/purple_valve_handle.json 61035f8afe75ff7bbd291da5d8690bcbebe679eb assets/create/blockstates/purple_valve_handle.json
8d7e653bfd9846e684a0d3725595714a19201017 assets/create/blockstates/radial_chassis.json ab2a1bf8f37f1c64d00538867f4c3a97195bd3c4 assets/create/blockstates/radial_chassis.json
45877c4d90a7185c2f304edbd67379d800920439 assets/create/blockstates/red_sail.json 45877c4d90a7185c2f304edbd67379d800920439 assets/create/blockstates/red_sail.json
da1b08387af7afa0855ee8d040f620c01f20660a assets/create/blockstates/red_seat.json da1b08387af7afa0855ee8d040f620c01f20660a assets/create/blockstates/red_seat.json
722fc77bbf387af8a4016e42cbf9501d2b968881 assets/create/blockstates/red_valve_handle.json 722fc77bbf387af8a4016e42cbf9501d2b968881 assets/create/blockstates/red_valve_handle.json
@ -402,16 +402,16 @@ a3a11524cd3515fc01d905767b4b7ea782adaf03 assets/create/blockstates/yellow_seat.j
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
666599fc27f1c67f992f989f5c9e707817e378f3 assets/create/lang/en_ud.json 666599fc27f1c67f992f989f5c9e707817e378f3 assets/create/lang/en_ud.json
930cc08e0462936ca440d7271992bf8e624f5247 assets/create/lang/en_us.json e22cf28236c9df0468471aca71d88524a36f46e9 assets/create/lang/en_us.json
98a913dd40c2f5e5d71d85b6a19fd6e5dd9f1a9d assets/create/lang/unfinished/de_de.json da73c1c7d1114e86d74089a471ef5cffea3f5c8f assets/create/lang/unfinished/de_de.json
795fb3cff856391c09bd7065416cc02a07ce4e7d assets/create/lang/unfinished/fr_fr.json ee900e9b93396f1d7bf375d11e23c6d1b9982ed3 assets/create/lang/unfinished/fr_fr.json
fb5db1f1f88e26443aee04e34bd7d776b59fecda assets/create/lang/unfinished/it_it.json be0a870d75ab60c97237c0d95dc24a4c1d15df79 assets/create/lang/unfinished/it_it.json
82935bc99220883e49ccf4f380af030e024df115 assets/create/lang/unfinished/ja_jp.json 02921005e9a71bf9936621648853b881eaa5086c assets/create/lang/unfinished/ja_jp.json
8b25c7856a78ef247acaca6070d5e46d896f5b2b assets/create/lang/unfinished/ko_kr.json 6fb0968222207b9e4e7bfb9f1a1b1bb318a2bb5a assets/create/lang/unfinished/ko_kr.json
83bd4e5d968264dd4e5872146b159cb23d699cd3 assets/create/lang/unfinished/nl_nl.json f352fdfa063fd7c8d63ccb7b2aea64614cc88689 assets/create/lang/unfinished/nl_nl.json
11ab9394b65c0b756ca3693c6f1e2c569aff0095 assets/create/lang/unfinished/pt_br.json e3b801fd879a9c70c8c59e86183e5552215e3767 assets/create/lang/unfinished/pt_br.json
40216a791b1bca38e4230a0f66d8748560c98c22 assets/create/lang/unfinished/ru_ru.json c36b40ec465932a69e416861da6fdaed89b8afb1 assets/create/lang/unfinished/ru_ru.json
548a60e23c0cadd2c34193eae1e3ee7f6d7cd043 assets/create/lang/unfinished/zh_cn.json 6bba967e11433b8804fbb4ccada837cdfaf118ee assets/create/lang/unfinished/zh_cn.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json

View file

@ -1014,6 +1014,8 @@
"create.hint.upward_funnel": "can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "Update Bearing", "create.hint.empty_bearing.title": "Update Bearing",
"create.hint.empty_bearing": "_Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "_Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "Deployer Item Overflow",
"create.hint.full_deployer": "It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "Hi :)", "create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay", "create.gui.config.overlay2": "This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1075", "_": "Missing Localizations: 1077",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 705", "_": "Missing Localizations: 707",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 689", "_": "Missing Localizations: 691",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 688", "_": "Missing Localizations: 690",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 689", "_": "Missing Localizations: 691",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1016", "_": "Missing Localizations: 1018",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1082", "_": "Missing Localizations: 1084",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 358", "_": "Missing Localizations: 360",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 371", "_": "Missing Localizations: 373",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1015,6 +1015,8 @@
"create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "UNLOCALIZED: can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing", "create.hint.empty_bearing.title": "UNLOCALIZED: Update Bearing",
"create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "UNLOCALIZED: _Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "UNLOCALIZED: Deployer Item Overflow",
"create.hint.full_deployer": "UNLOCALIZED: It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "UNLOCALIZED: Hi :)", "create.gui.config.overlay1": "UNLOCALIZED: Hi :)",
"create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay", "create.gui.config.overlay2": "UNLOCALIZED: This is a sample overlay",

View file

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour.AttachmentTypes; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour.AttachmentTypes;
import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlock.HeatLevel;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;

View file

@ -10,6 +10,7 @@ import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.curiosities.tools.SandPaperItem; import com.simibubi.create.content.curiosities.tools.SandPaperItem;
import com.simibubi.create.foundation.advancement.AllTriggers; import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.item.TooltipHelper;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
@ -32,11 +33,11 @@ import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceContext.BlockMode; import net.minecraft.util.math.RayTraceContext.BlockMode;
import net.minecraft.util.math.RayTraceContext.FluidMode; import net.minecraft.util.math.RayTraceContext.FluidMode;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.server.ServerWorld; import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.capabilities.Capability; import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable; import net.minecraftforge.items.IItemHandlerModifiable;
public class DeployerTileEntity extends KineticTileEntity { public class DeployerTileEntity extends KineticTileEntity {
@ -130,13 +131,22 @@ public class DeployerTileEntity extends KineticTileEntity {
return; return;
} }
if (!filtering.test(stack)) { boolean changed = false;
if (!stack.isEmpty()) { for (int i = 0; i < player.inventory.getSizeInventory(); i++) {
overflowItems.add(stack); if (overflowItems.size() > 10)
player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); break;
sendData(); ItemStack item = player.inventory.getStackInSlot(i);
return; if (item.isEmpty())
continue;
if (item != stack || !filtering.test(item)) {
overflowItems.add(item);
player.inventory.setInventorySlotContents(i, ItemStack.EMPTY);
changed = true;
} }
}
if (changed) {
sendData();
timer = getTimerSpeed() * 10; timer = getTimerSpeed() * 10;
return; return;
} }
@ -353,10 +363,21 @@ public class DeployerTileEntity extends KineticTileEntity {
@Override @Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) { public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
if (cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY && invHandler != null) { if (isItemHandlerCap(cap) && invHandler != null)
return invHandler.cast(); return invHandler.cast();
}
return super.getCapability(cap, side); return super.getCapability(cap, side);
} }
@Override
public boolean addToTooltip(List<ITextComponent> tooltip, boolean isPlayerSneaking) {
if (super.addToTooltip(tooltip, isPlayerSneaking))
return true;
if (getSpeed() == 0)
return false;
if (overflowItems.isEmpty())
return false;
TooltipHelper.addHint(tooltip, "hint.full_deployer");
return true;
}
} }

View file

@ -435,6 +435,15 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit
} }
} }
@SuppressWarnings("deprecation")
@Override
public void remove(boolean keepData) {
if (!world.isRemote && !removed && contraption != null) {
contraption.stop(world);
}
super.remove(keepData);
}
protected abstract StructureTransform makeStructureTransform(); protected abstract StructureTransform makeStructureTransform();
@Override @Override

View file

@ -742,7 +742,6 @@ public abstract class Contraption {
} }
public void addBlocksToWorld(World world, StructureTransform transform) { public void addBlocksToWorld(World world, StructureTransform transform) {
stop(world);
for (boolean nonBrittles : Iterate.trueAndFalse) { for (boolean nonBrittles : Iterate.trueAndFalse) {
for (BlockInfo block : blocks.values()) { for (BlockInfo block : blocks.values()) {
if (nonBrittles == BlockMovementTraits.isBrittle(block.state)) if (nonBrittles == BlockMovementTraits.isBrittle(block.state))

View file

@ -1,5 +1,6 @@
package com.simibubi.create.content.contraptions.components.structureMovement; package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -16,7 +17,7 @@ public abstract class TranslatingContraption extends Contraption {
public Set<BlockPos> getColliders(World world, Direction movementDirection) { public Set<BlockPos> getColliders(World world, Direction movementDirection) {
if (getBlocks() == null) if (getBlocks() == null)
return null; return Collections.EMPTY_SET;
if (cachedColliders == null || cachedColliderDirection != movementDirection) { if (cachedColliders == null || cachedColliderDirection != movementDirection) {
cachedColliders = new HashSet<>(); cachedColliders = new HashSet<>();
cachedColliderDirection = movementDirection; cachedColliderDirection = movementDirection;

View file

@ -79,8 +79,10 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
else else
sendData(); sendData();
return; return;
} } else {
if (getSpeed() != 0)
assemble(); assemble();
}
return; return;
} }

View file

@ -53,9 +53,14 @@ public class PistonContraption extends TranslatingContraption {
public boolean assemble(World world, BlockPos pos) { public boolean assemble(World world, BlockPos pos) {
if (!collectExtensions(world, pos, orientation)) if (!collectExtensions(world, pos, orientation))
return false; return false;
int count = blocks.size();
if (!searchMovedStructure(world, anchor, retract ? orientation.getOpposite() : orientation)) if (!searchMovedStructure(world, anchor, retract ? orientation.getOpposite() : orientation))
return false; return false;
if (blocks.size() == count) { // no new blocks added
bounds = pistonExtensionCollisionBox;
} else {
bounds = bounds.union(pistonExtensionCollisionBox); bounds = bounds.union(pistonExtensionCollisionBox);
}
startMoving(world); startMoving(world);
return true; return true;
} }
@ -112,8 +117,10 @@ public class PistonContraption extends TranslatingContraption {
anchor = pos.offset(direction, initialExtensionProgress + 1); anchor = pos.offset(direction, initialExtensionProgress + 1);
extensionLength = extensionsInBack + extensionsInFront; extensionLength = extensionsInBack + extensionsInFront;
initialExtensionProgress = extensionsInFront; initialExtensionProgress = extensionsInFront;
pistonExtensionCollisionBox = new AxisAlignedBB(end.offset(direction, -extensionsInFront) pistonExtensionCollisionBox = new AxisAlignedBB(
.subtract(anchor)); BlockPos.ZERO.offset(direction, -1),
BlockPos.ZERO.offset(direction, -extensionLength - 1)).expand(1,
1, 1);
if (extensionLength == 0) if (extensionLength == 0)
return false; return false;
@ -124,7 +131,7 @@ public class PistonContraption extends TranslatingContraption {
BlockPos relPos = pole.pos.offset(direction, -extensionsInFront); BlockPos relPos = pole.pos.offset(direction, -extensionsInFront);
BlockPos localPos = relPos.subtract(anchor); BlockPos localPos = relPos.subtract(anchor);
getBlocks().put(localPos, new BlockInfo(localPos, pole.state, null)); getBlocks().put(localPos, new BlockInfo(localPos, pole.state, null));
pistonExtensionCollisionBox = pistonExtensionCollisionBox.union(new AxisAlignedBB(localPos)); //pistonExtensionCollisionBox = pistonExtensionCollisionBox.union(new AxisAlignedBB(localPos));
} }
return true; return true;

View file

@ -20,10 +20,16 @@ import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.PoweredRailBlock;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
import net.minecraft.entity.item.minecart.MinecartEntity;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.common.util.Constants.NBT;
@ -79,8 +85,45 @@ public class MinecartController implements INBTSerializable<CompoundNBT> {
internalStall.booleanValue() || otherCart == null || !otherCart.isPresent() || otherCart.isStalled(false)); internalStall.booleanValue() || otherCart == null || !otherCart.isPresent() || otherCart.isStalled(false));
})); }));
if (!world.isRemote) if (!world.isRemote) {
setStalled(internalStall.booleanValue(), true); setStalled(internalStall.booleanValue(), true);
disassemble(cart);
}
}
private void disassemble(AbstractMinecartEntity cart) {
if (cart instanceof MinecartEntity) {
return;
}
List<Entity> passengers = cart.getPassengers();
if (passengers.isEmpty() || !(passengers.get(0) instanceof AbstractContraptionEntity)) {
return;
}
World world = cart.world;
int i = MathHelper.floor(cart.getX());
int j = MathHelper.floor(cart.getY());
int k = MathHelper.floor(cart.getZ());
if (world.getBlockState(new BlockPos(i, j - 1, k))
.isIn(BlockTags.RAILS)) {
--j;
}
BlockPos blockpos = new BlockPos(i, j, k);
BlockState blockstate = world.getBlockState(blockpos);
if (cart.canUseRail() && blockstate.isIn(BlockTags.RAILS)
&& blockstate.getBlock() instanceof PoweredRailBlock
&& ((PoweredRailBlock) blockstate.getBlock())
.isActivatorRail()) {
if (cart.isBeingRidden()) {
cart.removePassengers();
}
if (cart.getRollingAmplitude() == 0) {
cart.setRollingDirection(-cart.getRollingDirection());
cart.setRollingAmplitude(10);
cart.setDamage(50.0F);
cart.velocityChanged = true;
}
}
} }
public boolean isFullyCoupled() { public boolean isFullyCoupled() {

View file

@ -13,6 +13,8 @@ import com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld;
import mcp.MethodsReturnNonnullByDefault; import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.BubbleColumnBlock;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids; import net.minecraft.fluid.Fluids;
import net.minecraft.item.BlockItemUseContext; import net.minecraft.item.BlockItemUseContext;
@ -84,18 +86,25 @@ public class WaterWheelBlock extends HorizontalKineticBlock implements ITE<Water
updateWheelSpeed(worldIn, pos); updateWheelSpeed(worldIn, pos);
} }
private void updateFlowAt(BlockState state, IWorld world, BlockPos pos, Direction f) { private void updateFlowAt(BlockState state, IWorld world, BlockPos pos, Direction side) {
if (f.getAxis() == state.get(HORIZONTAL_FACING) if (side.getAxis() == state.get(HORIZONTAL_FACING)
.getAxis()) .getAxis())
return; return;
FluidState fluid = world.getFluidState(pos.offset(f)); FluidState fluid = world.getFluidState(pos.offset(side));
Direction wf = state.get(HORIZONTAL_FACING); Direction wf = state.get(HORIZONTAL_FACING);
boolean clockwise = wf.getAxisDirection() == AxisDirection.POSITIVE; boolean clockwise = wf.getAxisDirection() == AxisDirection.POSITIVE;
int clockwiseMultiplier = 2; int clockwiseMultiplier = 2;
Vector3d vec = fluid.getFlow(world, pos.offset(f)); Vector3d vec = fluid.getFlow(world, pos.offset(side));
vec = vec.scale(f.getAxisDirection() if (side.getAxis()
.isHorizontal()) {
BlockState adjacentBlock = world.getBlockState(pos.offset(side));
if (adjacentBlock.getBlock() == Blocks.BUBBLE_COLUMN.getBlock())
vec = new Vector3d(0, adjacentBlock.get(BubbleColumnBlock.DRAG) ? -1 : 1, 0);
}
vec = vec.scale(side.getAxisDirection()
.getOffset()); .getOffset());
vec = new Vector3d(Math.signum(vec.x), Math.signum(vec.y), Math.signum(vec.z)); vec = new Vector3d(Math.signum(vec.x), Math.signum(vec.y), Math.signum(vec.z));
Vector3d flow = vec; Vector3d flow = vec;
@ -104,16 +113,16 @@ public class WaterWheelBlock extends HorizontalKineticBlock implements ITE<Water
double flowStrength = 0; double flowStrength = 0;
if (wf.getAxis() == Axis.Z) { if (wf.getAxis() == Axis.Z) {
if (f.getAxis() == Axis.Y) if (side.getAxis() == Axis.Y)
flowStrength = flow.x > 0 ^ !clockwise ? -flow.x * clockwiseMultiplier : -flow.x; flowStrength = flow.x > 0 ^ !clockwise ? -flow.x * clockwiseMultiplier : -flow.x;
if (f.getAxis() == Axis.X) if (side.getAxis() == Axis.X)
flowStrength = flow.y < 0 ^ !clockwise ? flow.y * clockwiseMultiplier : flow.y; flowStrength = flow.y < 0 ^ !clockwise ? flow.y * clockwiseMultiplier : flow.y;
} }
if (wf.getAxis() == Axis.X) { if (wf.getAxis() == Axis.X) {
if (f.getAxis() == Axis.Y) if (side.getAxis() == Axis.Y)
flowStrength = flow.z < 0 ^ !clockwise ? flow.z * clockwiseMultiplier : flow.z; flowStrength = flow.z < 0 ^ !clockwise ? flow.z * clockwiseMultiplier : flow.z;
if (f.getAxis() == Axis.Z) if (side.getAxis() == Axis.Z)
flowStrength = flow.y > 0 ^ !clockwise ? -flow.y * clockwiseMultiplier : -flow.y; flowStrength = flow.y > 0 ^ !clockwise ? -flow.y * clockwiseMultiplier : -flow.y;
} }
@ -124,7 +133,7 @@ public class WaterWheelBlock extends HorizontalKineticBlock implements ITE<Water
} }
Integer flowModifier = AllConfigs.SERVER.kinetics.waterWheelFlowSpeed.get(); Integer flowModifier = AllConfigs.SERVER.kinetics.waterWheelFlowSpeed.get();
te.setFlow(f, (float) (flowStrength * flowModifier / 2f)); te.setFlow(side, (float) (flowStrength * flowModifier / 2f));
}); });
} }

View file

@ -0,0 +1,137 @@
package com.simibubi.create.content.contraptions.fluids;
import java.lang.ref.WeakReference;
import java.util.function.Predicate;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockFace;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
public abstract class FlowSource {
private static final LazyOptional<IFluidHandler> EMPTY = LazyOptional.empty();
BlockFace location;
public FlowSource(BlockFace location) {
this.location = location;
}
public FluidStack provideFluid(Predicate<FluidStack> extractionPredicate) {
IFluidHandler tank = provideHandler().orElse(null);
if (tank == null)
return FluidStack.EMPTY;
FluidStack immediateFluid = tank.drain(1, FluidAction.SIMULATE);
if (extractionPredicate.test(immediateFluid))
return immediateFluid;
for (int i = 0; i < tank.getTanks(); i++) {
FluidStack contained = tank.getFluidInTank(i);
if (contained.isEmpty())
continue;
if (!extractionPredicate.test(contained))
continue;
FluidStack toExtract = contained.copy();
toExtract.setAmount(1);
return tank.drain(toExtract, FluidAction.SIMULATE);
}
return FluidStack.EMPTY;
}
// Layer III. PFIs need active attention to prevent them from disengaging early
public void keepAlive() {}
public abstract boolean isEndpoint();
public void manageSource(World world) {}
public void whileFlowPresent(World world, boolean pulling) {}
public LazyOptional<IFluidHandler> provideHandler() {
return EMPTY;
}
public static class FluidHandler extends FlowSource {
LazyOptional<IFluidHandler> fluidHandler;
public FluidHandler(BlockFace location) {
super(location);
fluidHandler = EMPTY;
}
public void manageSource(World world) {
if (fluidHandler.isPresent())
return;
TileEntity tileEntity = world.getTileEntity(location.getConnectedPos());
if (tileEntity != null)
fluidHandler = tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
location.getOppositeFace());
}
@Override
public LazyOptional<IFluidHandler> provideHandler() {
return fluidHandler;
}
@Override
public boolean isEndpoint() {
return true;
}
}
public static class OtherPipe extends FlowSource {
WeakReference<FluidTransportBehaviour> cached;
public OtherPipe(BlockFace location) {
super(location);
}
@Override
public void manageSource(World world) {
if (cached != null && cached.get() != null && !cached.get().tileEntity.isRemoved())
return;
cached = null;
FluidTransportBehaviour fluidTransportBehaviour =
TileEntityBehaviour.get(world, location.getConnectedPos(), FluidTransportBehaviour.TYPE);
if (fluidTransportBehaviour != null)
cached = new WeakReference<>(fluidTransportBehaviour);
}
@Override
public FluidStack provideFluid(Predicate<FluidStack> extractionPredicate) {
if (cached == null || cached.get() == null)
return FluidStack.EMPTY;
FluidTransportBehaviour behaviour = cached.get();
FluidStack providedOutwardFluid = behaviour.getProvidedOutwardFluid(location.getOppositeFace());
return extractionPredicate.test(providedOutwardFluid) ? providedOutwardFluid : FluidStack.EMPTY;
}
@Override
public boolean isEndpoint() {
return false;
}
}
public static class Blocked extends FlowSource {
public Blocked(BlockFace location) {
super(location);
}
@Override
public boolean isEndpoint() {
return false;
}
}
}

View file

@ -0,0 +1,58 @@
package com.simibubi.create.content.contraptions.fluids;
import com.simibubi.create.Create;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.IFluidState;
import net.minecraft.item.GlassBottleItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceContext;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class FluidBottleItemHook extends Item {
public FluidBottleItemHook(Properties p_i48487_1_) {
super(p_i48487_1_);
}
@SubscribeEvent
public static void preventWaterBottlesFromCreatesFluids(PlayerInteractEvent.RightClickItem event) {
ItemStack itemStack = event.getItemStack();
if (itemStack.isEmpty())
return;
if (!(itemStack.getItem() instanceof GlassBottleItem))
return;
World world = event.getWorld();
PlayerEntity player = event.getPlayer();
RayTraceResult raytraceresult = rayTrace(world, player, RayTraceContext.FluidMode.SOURCE_ONLY);
if (raytraceresult.getType() != RayTraceResult.Type.BLOCK)
return;
BlockPos blockpos = ((BlockRayTraceResult) raytraceresult).getPos();
if (!world.isBlockModifiable(player, blockpos))
return;
IFluidState fluidState = world.getFluidState(blockpos);
if (fluidState.isTagged(FluidTags.WATER) && fluidState.getFluid()
.getRegistryName()
.getNamespace()
.equals(Create.ID)) {
event.setCancellationResult(ActionResultType.PASS);
event.setCanceled(true);
return;
}
return;
}
}

View file

@ -1,359 +1,263 @@
package com.simibubi.create.content.contraptions.fluids; package com.simibubi.create.content.contraptions.fluids;
import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import com.google.common.collect.ImmutableList; import javax.annotation.Nullable;
import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockFace; import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
public class FluidNetwork { public class FluidNetwork {
BlockFace pumpLocation; private static int CYCLES_PER_TICK = 16;
Map<BlockPos, Pair<Integer, Map<Direction, Boolean>>> pipeGraph;
List<FluidNetworkFlow> flows;
Set<FluidNetworkEndpoint> targets;
Set<BlockFace> rangeEndpoints;
Map<BlockFace, FluidStack> previousFlow;
boolean connectToPumps; World world;
int waitForUnloadedNetwork; BlockFace start;
public FluidNetwork() { Supplier<LazyOptional<IFluidHandler>> sourceSupplier;
pipeGraph = new HashMap<>(); LazyOptional<IFluidHandler> source;
flows = new ArrayList<>(); int transferSpeed;
targets = new HashSet<>();
rangeEndpoints = new HashSet<>(); int pauseBeforePropagation;
previousFlow = new HashMap<>(); List<BlockFace> queued;
Set<Pair<BlockFace, PipeConnection>> frontier;
Set<BlockPos> visited;
List<Pair<BlockFace, LazyOptional<IFluidHandler>>> targets;
Map<BlockPos, WeakReference<FluidTransportBehaviour>> cache;
public FluidNetwork(World world, BlockFace location, Supplier<LazyOptional<IFluidHandler>> sourceSupplier) {
this.world = world;
this.start = location;
this.sourceSupplier = sourceSupplier;
this.source = LazyOptional.empty();
this.frontier = new HashSet<>();
this.visited = new HashSet<>();
this.targets = new ArrayList<>();
this.cache = new HashMap<>();
this.queued = new ArrayList<>();
reset();
} }
public boolean hasEndpoints() { public void tick() {
for (FluidNetworkFlow pipeFlow : flows) if (pauseBeforePropagation > 0) {
if (pipeFlow.hasValidTargets()) pauseBeforePropagation--;
return true;
return false;
}
public Collection<FluidNetworkEndpoint> getEndpoints(boolean pulling) {
if (!pulling) {
for (FluidNetworkFlow pipeFlow : flows)
return pipeFlow.outputEndpoints;
return Collections.emptySet();
}
List<FluidNetworkEndpoint> list = new ArrayList<>();
for (FluidNetworkFlow pipeFlow : flows) {
if (!pipeFlow.hasValidTargets())
continue;
list.add(pipeFlow.source);
}
return list;
}
public void tick(IWorld world, PumpTileEntity pumpTE) {
if (connectToPumps) {
connectToOtherFNs(world, pumpTE);
connectToPumps = false;
}
}
public void tickFlows(IWorld world, PumpTileEntity pumpTE, boolean pulling, float speed) {
if (connectToPumps)
return; return;
initFlows(pumpTE, pulling);
previousFlow.clear();
flows.forEach(ep -> ep.tick(world, speed));
} }
private void initFlows(PumpTileEntity pumpTE, boolean pulling) { for (int cycle = 0; cycle < CYCLES_PER_TICK; cycle++) {
if (targets.isEmpty()) boolean shouldContinue = false;
return; for (Iterator<BlockFace> iterator = queued.iterator(); iterator.hasNext();) {
if (!flows.isEmpty()) BlockFace blockFace = iterator.next();
return; if (!isPresent(blockFace))
World world = pumpTE.getWorld(); continue;
if (pulling) { PipeConnection pipeConnection = get(blockFace);
targets.forEach(ne -> flows.add(new FluidNetworkFlow(this, ne, world, pulling))); if (pipeConnection != null) {
} else { if (blockFace.equals(start))
PumpEndpoint pumpEndpoint = new PumpEndpoint(pumpLocation.getOpposite(), pumpTE); transferSpeed = (int) Math.max(1, pipeConnection.pressure.get(true) / 2f);
flows.add(new FluidNetworkFlow(this, pumpEndpoint, world, pulling)); frontier.add(Pair.of(blockFace, pipeConnection));
} }
iterator.remove();
} }
public void connectToOtherFNs(IWorld world, PumpTileEntity pump) { // drawDebugOutlines();
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
int maxDistance = FluidPropagator.getPumpRange() * 2;
frontier.add(Pair.of(-1, pumpLocation.getPos()));
while (!frontier.isEmpty()) { for (Iterator<Pair<BlockFace, PipeConnection>> iterator = frontier.iterator(); iterator.hasNext();) {
Pair<Integer, BlockPos> entry = frontier.remove(0); Pair<BlockFace, PipeConnection> pair = iterator.next();
int distance = entry.getFirst(); BlockFace blockFace = pair.getFirst();
BlockPos currentPos = entry.getSecond(); PipeConnection pipeConnection = pair.getSecond();
if (!world.isAreaLoaded(currentPos, 0)) if (!pipeConnection.hasFlow())
continue; continue;
if (visited.contains(currentPos)) Flow flow = pipeConnection.flow.get();
if (!flow.inbound) {
if (pipeConnection.comparePressure() >= 0)
iterator.remove();
continue;
}
if (!flow.complete)
continue; continue;
visited.add(currentPos);
List<Direction> connections; boolean canRemove = true;
if (currentPos.equals(pumpLocation.getPos())) { for (Direction side : Iterate.directions) {
connections = ImmutableList.of(pumpLocation.getFace()); if (side == blockFace.getFace())
} else { continue;
BlockState currentState = world.getBlockState(currentPos); BlockFace adjacentLocation = new BlockFace(blockFace.getPos(), side);
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos); PipeConnection adjacent = get(adjacentLocation);
if (pipe == null) if (adjacent == null)
continue;
if (!adjacent.hasFlow()) {
// Branch could potentially still appear
if (adjacent.hasPressure() && adjacent.pressure.getSecond() > 0)
canRemove = false;
continue;
}
Flow outFlow = adjacent.flow.get();
if (outFlow.inbound) {
if (adjacent.comparePressure() > 0)
canRemove = false;
continue;
}
if (!outFlow.complete) {
canRemove = false;
continue; continue;
connections = FluidPropagator.getPipeConnections(currentState, pipe);
} }
for (Direction face : connections) { if (adjacent.source.isPresent() && adjacent.source.get()
BlockFace blockFace = new BlockFace(currentPos, face); .isEndpoint()) {
BlockPos connectedPos = blockFace.getConnectedPos(); targets.add(Pair.of(adjacentLocation, adjacent.source.get()
BlockState connectedState = world.getBlockState(connectedPos); .provideHandler()));
continue;
}
if (connectedPos.equals(pumpLocation.getPos())) if (visited.add(adjacentLocation.getConnectedPos())) {
continue; queued.add(adjacentLocation.getOpposite());
if (!world.isAreaLoaded(connectedPos, 0)) shouldContinue = true;
continue; }
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) }
.getAxis() == face.getAxis()) { if (canRemove)
TileEntity tileEntity = world.getTileEntity(connectedPos); iterator.remove();
if (tileEntity instanceof PumpTileEntity) { }
PumpTileEntity otherPump = (PumpTileEntity) tileEntity; if (!shouldContinue)
if (otherPump.networks == null)
continue;
otherPump.networks.forEach(fn -> {
int nearest = Integer.MAX_VALUE;
BlockFace argNearest = null;
for (BlockFace pumpEndpoint : fn.rangeEndpoints) {
if (pumpEndpoint.isEquivalent(pumpLocation)) {
argNearest = pumpEndpoint;
break; break;
} }
Pair<Integer, Map<Direction, Boolean>> pair =
pipeGraph.get(pumpEndpoint.getConnectedPos());
if (pair == null)
continue;
Integer distanceFromPump = pair.getFirst();
Map<Direction, Boolean> pipeConnections = pair.getSecond();
if (!pipeConnections.containsKey(pumpEndpoint.getOppositeFace())) // drawDebugOutlines();
continue;
if (nearest <= distanceFromPump)
continue;
nearest = distanceFromPump;
argNearest = pumpEndpoint;
} if (!source.isPresent())
if (argNearest != null) { source = sourceSupplier.get();
InterPumpEndpoint endpoint = new InterPumpEndpoint(world, argNearest.getOpposite(), if (!source.isPresent())
pump, otherPump, pumpLocation, fn.pumpLocation); return;
targets.add(endpoint); if (targets.isEmpty())
fn.targets.add(endpoint.opposite(world)); return;
} for (Pair<BlockFace, LazyOptional<IFluidHandler>> pair : targets) {
if (pair.getSecond()
.isPresent())
continue;
PipeConnection pipeConnection = get(pair.getFirst());
if (pipeConnection == null)
continue;
pipeConnection.source.ifPresent(fs -> {
if (fs.isEndpoint())
pair.setSecond(fs.provideHandler());
}); });
}
continue;
}
if (visited.contains(connectedPos))
continue;
if (distance > maxDistance)
continue;
FluidPipeBehaviour targetPipe = FluidPropagator.getPipe(world, connectedPos);
if (targetPipe == null)
continue;
if (targetPipe.isConnectedTo(connectedState, face.getOpposite()))
frontier.add(Pair.of(distance + 1, connectedPos));
}
} }
int flowSpeed = transferSpeed;
for (boolean simulate : Iterate.trueAndFalse) {
FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE;
IFluidHandler handler = source.orElse(null);
if (handler == null)
return;
FluidStack transfer = handler.drain(flowSpeed, action);
if (transfer.isEmpty())
return;
List<Pair<BlockFace, LazyOptional<IFluidHandler>>> availableOutputs = new ArrayList<>(targets);
while (!availableOutputs.isEmpty() && transfer.getAmount() > 0) {
int dividedTransfer = transfer.getAmount() / availableOutputs.size();
int remainder = transfer.getAmount() % availableOutputs.size();
for (Iterator<Pair<BlockFace, LazyOptional<IFluidHandler>>> iterator =
availableOutputs.iterator(); iterator.hasNext();) {
Pair<BlockFace, LazyOptional<IFluidHandler>> pair = iterator.next();
int toTransfer = dividedTransfer;
if (remainder > 0) {
toTransfer++;
remainder--;
} }
public void assemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) { if (transfer.isEmpty())
Map<BlockFace, OpenEndedPipe> openEnds = pumpTE.getOpenEnds(pumpLocation.getFace()); break;
openEnds.values() IFluidHandler targetHandler = pair.getSecond()
.forEach(OpenEndedPipe::markStale); .orElse(null);
if (targetHandler == null) {
this.pumpLocation = pumpLocation; iterator.remove();
if (!collectEndpoint(world, pumpLocation, openEnds, 0)) {
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
int maxDistance = FluidPropagator.getPumpRange();
frontier.add(Pair.of(0, pumpLocation.getConnectedPos()));
while (!frontier.isEmpty()) {
Pair<Integer, BlockPos> entry = frontier.remove(0);
int distance = entry.getFirst();
BlockPos currentPos = entry.getSecond();
if (!world.isAreaLoaded(currentPos, 0))
continue;
if (visited.contains(currentPos))
continue;
visited.add(currentPos);
BlockState currentState = world.getBlockState(currentPos);
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, currentPos);
if (pipe == null)
continue;
for (Direction face : FluidPropagator.getPipeConnections(currentState, pipe)) {
if (!pipe.canTransferToward(FluidStack.EMPTY, world.getBlockState(currentPos), face, false))
continue;
BlockFace blockFace = new BlockFace(currentPos, face);
BlockPos connectedPos = blockFace.getConnectedPos();
if (connectedPos.equals(pumpLocation.getPos())) {
addEntry(blockFace.getPos(), blockFace.getFace(), true, distance);
continue;
}
if (!world.isAreaLoaded(connectedPos, 0))
continue;
if (collectEndpoint(world, blockFace, openEnds, distance))
continue;
FluidPipeBehaviour pipeBehaviour = FluidPropagator.getPipe(world, connectedPos);
if (pipeBehaviour == null)
continue;
if (visited.contains(connectedPos))
continue;
if (distance + 1 >= maxDistance) {
rangeEndpoints.add(blockFace);
addEntry(currentPos, face, false, distance);
FluidPropagator.showBlockFace(blockFace)
.lineWidth(1 / 8f)
.colored(0xff0000);
continue; continue;
} }
addConnection(connectedPos, currentPos, face.getOpposite(), distance); FluidStack divided = transfer.copy();
frontier.add(Pair.of(distance + 1, connectedPos)); divided.setAmount(toTransfer);
int fill = targetHandler.fill(divided, action);
transfer.setAmount(transfer.getAmount() - fill);
if (fill < toTransfer)
iterator.remove();
} }
}
}
Set<BlockFace> staleEnds = new HashSet<>();
openEnds.entrySet()
.forEach(e -> {
if (e.getValue()
.isStale())
staleEnds.add(e.getKey());
});
staleEnds.forEach(openEnds::remove);
connectToPumps = true;
}
private FluidNetworkEndpoint reuseOrCreateOpenEnd(IWorld world, Map<BlockFace, OpenEndedPipe> openEnds,
BlockFace toCreate) {
OpenEndedPipe openEndedPipe = null;
if (openEnds.containsKey(toCreate)) {
openEndedPipe = openEnds.get(toCreate);
openEndedPipe.unmarkStale();
} else {
openEndedPipe = new OpenEndedPipe(toCreate);
openEnds.put(toCreate, openEndedPipe);
}
return new FluidNetworkEndpoint(world, toCreate, openEndedPipe.getCapability());
} }
private boolean collectEndpoint(IWorld world, BlockFace blockFace, Map<BlockFace, OpenEndedPipe> openEnds, flowSpeed -= transfer.getAmount();
int distance) { transfer = FluidStack.EMPTY;
BlockPos connectedPos = blockFace.getConnectedPos();
BlockState connectedState = world.getBlockState(connectedPos);
// other pipe, no endpoint
FluidPipeBehaviour pipe = FluidPropagator.getPipe(world, connectedPos);
if (pipe != null && pipe.isConnectedTo(connectedState, blockFace.getOppositeFace()))
return false;
TileEntity tileEntity = world.getTileEntity(connectedPos);
// fluid handler endpoint
Direction face = blockFace.getFace();
if (tileEntity != null) {
LazyOptional<IFluidHandler> capability =
tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face.getOpposite());
if (capability.isPresent()) {
targets.add(new FluidNetworkEndpoint(world, blockFace, capability));
addEntry(blockFace.getPos(), face, false, distance);
FluidPropagator.showBlockFace(blockFace)
.colored(0x00b7c2)
.lineWidth(1 / 8f);
return true;
} }
} }
// open endpoint // private void drawDebugOutlines() {
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) // FluidPropagator.showBlockFace(start)
.getAxis() == face.getAxis()) { // .lineWidth(1 / 8f)
rangeEndpoints.add(blockFace); // .colored(0xff0000);
addEntry(blockFace.getPos(), face, false, distance); // for (Pair<BlockFace, LazyOptional<IFluidHandler>> pair : targets)
return true; // FluidPropagator.showBlockFace(pair.getFirst())
} // .lineWidth(1 / 8f)
if (!FluidPropagator.isOpenEnd(world, blockFace.getPos(), face)) // .colored(0x00ff00);
return false; // for (Pair<BlockFace, PipeConnection> pair : frontier)
// FluidPropagator.showBlockFace(pair.getFirst())
// .lineWidth(1 / 4f)
// .colored(0xfaaa33);
// }
targets.add(reuseOrCreateOpenEnd(world, openEnds, blockFace)); public void reset() {
addEntry(blockFace.getPos(), face, false, distance); frontier.clear();
FluidPropagator.showBlockFace(blockFace) visited.clear();
.colored(0xb700c2)
.lineWidth(1 / 8f);
return true;
}
private void addConnection(BlockPos from, BlockPos to, Direction direction, int distance) {
addEntry(from, direction, true, distance);
addEntry(to, direction.getOpposite(), false, distance + 1);
}
private void addEntry(BlockPos pos, Direction direction, boolean outbound, int distance) {
if (!pipeGraph.containsKey(pos))
pipeGraph.put(pos, Pair.of(distance, new HashMap<>()));
pipeGraph.get(pos)
.getSecond()
.put(direction, outbound);
}
public void reAssemble(IWorld world, PumpTileEntity pumpTE, BlockFace pumpLocation) {
rangeEndpoints.clear();
targets.clear(); targets.clear();
pipeGraph.clear(); queued.clear();
assemble(world, pumpTE, pumpLocation); queued.add(start);
pauseBeforePropagation = 2;
} }
public void remove(IWorld world) { @Nullable
clearFlows(world, false); private PipeConnection get(BlockFace location) {
BlockPos pos = location.getPos();
FluidTransportBehaviour fluidTransfer = getFluidTransfer(pos);
if (fluidTransfer == null)
return null;
return fluidTransfer.getConnection(location.getFace());
} }
public void clearFlows(IWorld world, boolean saveState) { private boolean isPresent(BlockFace location) {
for (FluidNetworkFlow networkFlow : flows) { return world.isAreaLoaded(location.getPos(), 0);
if (!networkFlow.getFluidStack()
.isEmpty())
networkFlow.addToSkippedConnections(world);
networkFlow.resetFlow(world);
} }
flows.clear();
@Nullable
private FluidTransportBehaviour getFluidTransfer(BlockPos pos) {
WeakReference<FluidTransportBehaviour> weakReference = cache.get(pos);
FluidTransportBehaviour behaviour = weakReference != null ? weakReference.get() : null;
if (behaviour != null && behaviour.tileEntity.isRemoved())
behaviour = null;
if (behaviour == null) {
behaviour = TileEntityBehaviour.get(world, pos, FluidTransportBehaviour.TYPE);
if (behaviour != null)
cache.put(pos, new WeakReference<>(behaviour));
}
return behaviour;
} }
} }

View file

@ -1,49 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import com.simibubi.create.foundation.utility.BlockFace;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.IWorld;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
public class FluidNetworkEndpoint {
BlockFace location;
protected LazyOptional<IFluidHandler> handler;
public FluidNetworkEndpoint(IWorld world, BlockFace location, LazyOptional<IFluidHandler> handler) {
this.location = location;
this.handler = handler;
this.handler.addListener($ -> onHandlerInvalidated(world));
}
protected void onHandlerInvalidated(IWorld world) {
IFluidHandler tank = handler.orElse(null);
if (tank != null)
return;
TileEntity tileEntity = world.getTileEntity(location.getConnectedPos());
if (tileEntity == null)
return;
LazyOptional<IFluidHandler> capability =
tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, location.getOppositeFace());
if (capability.isPresent()) {
handler = capability;
handler.addListener($ -> onHandlerInvalidated(world));
}
}
public FluidStack provideFluid() {
IFluidHandler tank = provideHandler().orElse(null);
if (tank == null)
return FluidStack.EMPTY;
return tank.drain(1, FluidAction.SIMULATE);
}
public LazyOptional<IFluidHandler> provideHandler() {
return handler;
}
}

View file

@ -1,306 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IWorld;
import net.minecraftforge.fluids.FluidStack;
class FluidNetworkFlow {
@FunctionalInterface
static interface PipeFlowConsumer {
void accept(FluidPipeBehaviour pipe, Direction face, boolean inbound);
}
/**
*
*/
private final FluidNetwork activePipeNetwork;
FluidNetworkEndpoint source;
FluidStack fluidStack;
Set<BlockFace> flowPointers;
Set<FluidNetworkEndpoint> outputEndpoints;
boolean pumpReached;
boolean pulling;
float speed;
public FluidNetworkFlow(FluidNetwork activePipeNetwork, FluidNetworkEndpoint source, IWorld world,
boolean pulling) {
this.activePipeNetwork = activePipeNetwork;
this.source = source;
this.pulling = pulling;
flowPointers = new HashSet<>();
outputEndpoints = new HashSet<>();
fluidStack = FluidStack.EMPTY;
tick(world, 0);
}
void resetFlow(IWorld world) {
fluidStack = FluidStack.EMPTY;
flowPointers.clear();
outputEndpoints.clear();
pumpReached = false;
forEachPipeFlow(world, (pipe, face, inbound) -> pipe.removeFlow(this, face, inbound));
}
void addToSkippedConnections(IWorld world) {
forEachPipeFlow(world, (pipe, face, inbound) -> {
if (!pipe.getFluid().isFluidEqual(fluidStack))
return;
BlockFace blockFace = new BlockFace(pipe.getPos(), face);
this.activePipeNetwork.previousFlow.put(blockFace, pipe.getFluid());
});
}
void forEachPipeFlow(IWorld world, FluidNetworkFlow.PipeFlowConsumer consumer) {
Set<BlockFace> flowPointers = new HashSet<>();
flowPointers.add(getSource());
// Update all branches of this flow, and create new ones if necessary
while (!flowPointers.isEmpty()) {
List<BlockFace> toAdd = new ArrayList<>();
for (Iterator<BlockFace> iterator = flowPointers.iterator(); iterator.hasNext();) {
BlockFace flowPointer = iterator.next();
BlockPos currentPos = flowPointer.getPos();
FluidPipeBehaviour pipe = getPipeInTree(world, currentPos);
if (pipe == null) {
iterator.remove();
continue;
}
Map<Direction, Boolean> directions = this.activePipeNetwork.pipeGraph.get(currentPos)
.getSecond();
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
boolean inbound = entry.getValue() != pulling;
Direction face = entry.getKey();
if (inbound && face != flowPointer.getFace())
continue;
consumer.accept(pipe, face, inbound);
if (inbound)
continue;
toAdd.add(new BlockFace(currentPos.offset(face), face.getOpposite()));
}
iterator.remove();
}
flowPointers.addAll(toAdd);
}
}
void tick(IWorld world, float speed) {
boolean skipping = speed == 0;
Map<BlockFace, FluidStack> previousFlow = this.activePipeNetwork.previousFlow;
if (skipping && previousFlow.isEmpty())
return;
this.speed = speed;
FluidStack provideFluid = source.provideFluid();
if (!fluidStack.isEmpty() && !fluidStack.isFluidEqual(provideFluid)) {
resetFlow(world);
return;
}
fluidStack = provideFluid.copy();
// There is currently no unfinished flow being followed
if (flowPointers.isEmpty()) {
// The fluid source has run out -> reset
if (fluidStack.isEmpty()) {
if (hasValidTargets())
resetFlow(world);
return;
}
// Keep the flows if all is well
if (hasValidTargets())
return;
// Start a new flow from or towards the pump
BlockFace source = getSource();
if (tryConnectTo(world, source.getOpposite()))
return;
flowPointers.add(source);
}
boolean skipped = false;
Set<BlockFace> pausedPointers = new HashSet<>();
do {
skipped = false;
List<BlockFace> toAdd = null;
// Update all branches of this flow, and create new ones if necessary
for (Iterator<BlockFace> iterator = flowPointers.iterator(); iterator.hasNext();) {
BlockFace flowPointer = iterator.next();
BlockPos currentPos = flowPointer.getPos();
if (pausedPointers.contains(flowPointer))
continue;
FluidPipeBehaviour pipe = getPipeInTree(world, currentPos);
if (pipe == null) {
iterator.remove();
continue;
}
Map<Direction, Boolean> directions = this.activePipeNetwork.pipeGraph.get(currentPos)
.getSecond();
boolean inboundComplete = false;
boolean allFlowsComplete = true;
BlockState state = world.getBlockState(currentPos);
// First loop only inbound flows of a pipe to see if they have reached the
// center
for (boolean inboundPass : Iterate.trueAndFalse) {
if (!inboundPass && !inboundComplete)
break;
// For all connections of the pipe tree of the pump
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
Boolean awayFromPump = entry.getValue();
Direction direction = entry.getKey();
boolean inbound = awayFromPump != pulling;
if (inboundPass && direction != flowPointer.getFace())
continue;
if (!inboundPass && inbound)
continue;
if (!pipe.canTransferToward(fluidStack, state, direction, inbound))
continue;
BlockFace blockface = new BlockFace(currentPos, direction);
if (!pipe.hasStartedFlow(this, direction, inbound))
pipe.addFlow(this, direction, inbound, false);
if (skipping && canSkip(previousFlow, blockface)) {
pipe.skipFlow(direction, inbound);
FluidPropagator.showBlockFace(blockface)
.colored(0x0)
.lineWidth(1 / 8f);
skipped = true;
}
if (!pipe.hasCompletedFlow(direction, inbound)) {
allFlowsComplete = false;
continue;
}
if (inboundPass) {
inboundComplete = true;
continue;
}
// Outward pass, check if any target was reached
tryConnectTo(world, blockface);
}
}
if (!allFlowsComplete && !skipping)
continue;
// Create a new flow branch at each outward pipe connection
for (Entry<Direction, Boolean> entry : directions.entrySet()) {
if (entry.getValue() != pulling)
continue;
Direction face = entry.getKey();
if (!pipe.canTransferToward(fluidStack, state, face, false))
continue;
BlockFace addedBlockFace = new BlockFace(currentPos.offset(face), face.getOpposite());
if (skipping && !canSkip(previousFlow, addedBlockFace)) {
allFlowsComplete = false;
continue;
}
if (toAdd == null)
toAdd = new ArrayList<>();
toAdd.add(addedBlockFace);
}
if (!allFlowsComplete && skipping) {
pausedPointers.add(flowPointer);
continue;
}
iterator.remove();
} // End of branch loop
if (toAdd != null)
flowPointers.addAll(toAdd);
} while (skipping && skipped);
}
private boolean canSkip(Map<BlockFace, FluidStack> previousFlow, BlockFace blockface) {
return previousFlow.containsKey(blockface) && previousFlow.get(blockface)
.isFluidEqual(fluidStack);
}
private boolean tryConnectTo(IWorld world, BlockFace blockface) {
// Pulling flow, target is the pump
if (pulling) {
if (!this.activePipeNetwork.pumpLocation.getOpposite()
.equals(blockface))
return false;
pumpReached = true;
TileEntity targetTE = world.getTileEntity(this.activePipeNetwork.pumpLocation.getPos());
if (targetTE instanceof PumpTileEntity)
((PumpTileEntity) targetTE).setProvidedFluid(fluidStack);
FluidPropagator.showBlockFace(this.activePipeNetwork.pumpLocation)
.colored(0x799351)
.lineWidth(1 / 8f);
return true;
}
// Pushing flow, targets are the endpoints
for (FluidNetworkEndpoint networkEndpoint : this.activePipeNetwork.targets) {
if (!networkEndpoint.location.isEquivalent(blockface))
continue;
outputEndpoints.add(networkEndpoint);
FluidPropagator.showBlockFace(blockface)
.colored(0x799351)
.lineWidth(1 / 8f);
return !(networkEndpoint instanceof InterPumpEndpoint);
}
return false;
}
private BlockFace getSource() {
return pulling ? source.location : this.activePipeNetwork.pumpLocation.getOpposite();
}
private FluidPipeBehaviour getPipeInTree(IWorld world, BlockPos currentPos) {
if (!world.isAreaLoaded(currentPos, 0))
return null;
if (!this.activePipeNetwork.pipeGraph.containsKey(currentPos))
return null;
return TileEntityBehaviour.get(world, currentPos, FluidPipeBehaviour.TYPE);
}
boolean hasValidTargets() {
return pumpReached || !outputEndpoints.isEmpty();
}
public float getSpeed() {
return speed;
}
public FluidStack getFluidStack() {
return fluidStack;
}
}

View file

@ -1,77 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.fluids.pipes.EncasedPipeBlock;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import net.minecraft.block.BlockState;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockDisplayReader;
public class FluidPipeAttachmentBehaviour extends BracketedTileEntityBehaviour {
public static BehaviourType<FluidPipeAttachmentBehaviour> TYPE = new BehaviourType<>();
public AttachmentTypes getAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state, Direction direction) {
if (!isPipeConnectedTowards(state, direction))
return AttachmentTypes.NONE;
BlockPos offsetPos = pos.offset(direction);
BlockState facingState = world.getBlockState(offsetPos);
if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING)
.getAxis() == direction.getAxis())
return AttachmentTypes.NONE;
if (AllBlocks.ENCASED_FLUID_PIPE.has(facingState)
&& facingState.get(EncasedPipeBlock.FACING_TO_PROPERTY_MAP.get(direction.getOpposite())))
return AttachmentTypes.NONE;
if (FluidPropagator.hasFluidCapability(facingState, world, offsetPos, direction)
&& !AllBlocks.HOSE_PULLEY.has(facingState))
return AttachmentTypes.DRAIN;
return AttachmentTypes.RIM;
}
public boolean isPipeConnectedTowards(BlockState state, Direction direction) {
FluidPipeBehaviour fluidPipeBehaviour = tileEntity.getBehaviour(FluidPipeBehaviour.TYPE);
if (fluidPipeBehaviour == null)
return false;
// BlockState bracket = getBracket();
// if (bracket != Blocks.AIR.getDefaultState() && bracket.get(BracketBlock.FACING) == direction)
// return false;
return fluidPipeBehaviour.isConnectedTo(state, direction);
}
public static enum AttachmentTypes {
NONE, RIM, DRAIN;
public boolean hasModel() {
return this != NONE;
}
}
public FluidPipeAttachmentBehaviour(SmartTileEntity te) {
super(te);
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
@Override
public boolean canHaveBracket() {
BlockState blockState = tileEntity.getBlockState();
if (blockState.getBlock() instanceof PumpBlock)
return false;
if (blockState.getBlock() instanceof EncasedPipeBlock)
return false;
return true;
}
}

View file

@ -1,484 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import javax.annotation.Nullable;
import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.KineticDebugger;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.LerpedFloat;
import com.simibubi.create.foundation.utility.LerpedFloat.Chaser;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.particles.IParticleData;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.DistExecutor;
public abstract class FluidPipeBehaviour extends TileEntityBehaviour {
public static BehaviourType<FluidPipeBehaviour> TYPE = new BehaviourType<>();
public static final int MAX_PARTICLE_RENDER_DISTANCE = 20;
public static final int SPLASH_PARTICLE_AMOUNT = 1;
public static final float IDLE_PARTICLE_SPAWN_CHANCE = 1 / 800f;
public static final Random r = new Random();
// Direction -> (inboundflows{}, outwardflows{})
Map<Direction, Couple<PipeFlows>> allFlows;
FluidStack fluid;
Couple<FluidStack> collision;
public FluidPipeBehaviour(SmartTileEntity te) {
super(te);
allFlows = new IdentityHashMap<>();
fluid = FluidStack.EMPTY;
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
public void notifyNetwork() {
FluidPropagator.propagateChangedPipe(this.getWorld(), tileEntity.getPos(), tileEntity.getBlockState());
}
public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) {
return isConnectedTo(state, direction);
}
public abstract boolean isConnectedTo(BlockState state, Direction direction);
public float getRimRadius(BlockState state, Direction direction) {
return 1 / 4f + 1 / 64f;
}
public boolean hasStartedFlow(FluidNetworkFlow flow, Direction face, boolean inbound) {
return allFlows.containsKey(face) && allFlows.get(face)
.get(inbound)
.hasFlow(flow);
}
public boolean hasCompletedFlow(Direction face, boolean inbound) {
return allFlows.containsKey(face) && allFlows.get(face)
.get(inbound)
.isCompleted();
}
@Override
public void write(CompoundNBT compound, boolean client) {
compound.put("Fluid", fluid.writeToNBT(new CompoundNBT()));
ListNBT flows = new ListNBT();
for (Direction face : Iterate.directions)
for (boolean inbound : Iterate.trueAndFalse) {
LerpedFloat flowProgress = getFlowProgress(face, inbound);
if (flowProgress == null)
continue;
CompoundNBT nbt = new CompoundNBT();
NBTHelper.writeEnum(nbt, "Face", face);
nbt.putBoolean("In", inbound);
PipeFlows pipeFlows = allFlows.get(face)
.get(inbound);
Set<FluidNetworkFlow> participants = pipeFlows.participants;
nbt.putBoolean("Silent", participants == null || participants.isEmpty());
nbt.put("Progress", flowProgress.writeNBT());
if (client)
nbt.putFloat("Strength", pipeFlows.bestFlowStrength);
flows.add(nbt);
}
compound.put("Flows", flows);
}
@Override
public void read(CompoundNBT compound, boolean client) {
fluid = FluidStack.loadFluidStackFromNBT(compound.getCompound("Fluid"));
if (client) {
for (Direction face : Iterate.directions)
if (allFlows.containsKey(face))
allFlows.get(face)
.forEach(pf -> pf.progress = null);
}
NBTHelper.iterateCompoundList(compound.getList("Flows", NBT.TAG_COMPOUND), nbt -> {
Direction face = NBTHelper.readEnum(nbt, "Face", Direction.class);
boolean inbound = nbt.getBoolean("In");
LerpedFloat progress = createFlowProgress(0);
progress.readNBT(nbt.getCompound("Progress"), false);
addFlow(null, face, inbound, nbt.getBoolean("Silent"));
setFlowProgress(face, inbound, progress);
if (client)
setVisualFlowStrength(face, inbound, nbt.getFloat("Strength"));
});
if (!client)
return;
for (Direction face : Iterate.directions) {
if (!allFlows.containsKey(face))
return;
Couple<PipeFlows> couple = allFlows.get(face);
if (couple.get(true).progress == null && couple.get(false).progress == null)
allFlows.remove(face);
if (allFlows.isEmpty())
clear();
}
}
public void addFlow(@Nullable FluidNetworkFlow flow, Direction face, boolean inbound, boolean silent) {
if (flow != null) {
FluidStack fluid = flow.getFluidStack();
if (!this.fluid.isEmpty() && !fluid.isFluidEqual(this.fluid)) {
collision = Couple.create(this.fluid, fluid);
return;
}
this.fluid = fluid;
}
if (!allFlows.containsKey(face)) {
allFlows.put(face, Couple.create(PipeFlows::new));
if (inbound && !silent)
spawnSplashOnRim(face);
}
if (flow != null) {
PipeFlows flows = allFlows.get(face)
.get(inbound);
flows.addFlow(flow);
contentsChanged();
}
}
public void removeFlow(FluidNetworkFlow flow, Direction face, boolean inbound) {
if (!allFlows.containsKey(face))
return;
Couple<PipeFlows> couple = allFlows.get(face);
couple.get(inbound)
.removeFlow(flow);
contentsChanged();
if (!couple.get(true)
.isActive()
&& !couple.get(false)
.isActive())
allFlows.remove(face);
if (allFlows.isEmpty())
clear();
}
public void setVisualFlowStrength(Direction face, boolean inbound, float strength) {
if (!allFlows.containsKey(face))
return;
allFlows.get(face)
.get(inbound).bestFlowStrength = strength;
}
public void setFlowProgress(Direction face, boolean inbound, LerpedFloat progress) {
if (!allFlows.containsKey(face))
return;
allFlows.get(face)
.get(inbound).progress = progress;
}
public LerpedFloat getFlowProgress(Direction face, boolean inbound) {
if (!allFlows.containsKey(face))
return null;
return allFlows.get(face)
.get(inbound).progress;
}
public void skipFlow(Direction face, boolean inbound) {
if (!allFlows.containsKey(face))
return;
Couple<PipeFlows> couple = allFlows.get(face);
couple.get(inbound)
.skip();
}
public void clear() {
allFlows.clear();
fluid = FluidStack.EMPTY;
contentsChanged();
}
public void spawnSplashOnRim(Direction face) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnSplashOnRimInner(face));
}
public void spawnParticles() {
DistExecutor.runWhenOn(Dist.CLIENT, () -> this::spawnParticlesInner);
}
@OnlyIn(Dist.CLIENT)
private void spawnParticlesInner() {
if (!isRenderEntityWithinDistance(tileEntity.getPos()))
return;
if (fluid.isEmpty())
return;
World world = Minecraft.getInstance().world;
BlockPos pos = tileEntity.getPos();
BlockState state = world.getBlockState(pos);
for (Direction face : Iterate.directions) {
boolean open = FluidPropagator.isOpenEnd(world, pos, face);
if (isConnectedTo(state, face)) {
if (open) {
spawnPouringLiquid(world, state, fluid, face, 1);
continue;
}
if (r.nextFloat() < IDLE_PARTICLE_SPAWN_CHANCE)
spawnRimParticles(world, state, fluid, face, 1);
}
}
}
@OnlyIn(Dist.CLIENT)
private void spawnSplashOnRimInner(Direction face) {
if (!isRenderEntityWithinDistance(tileEntity.getPos()))
return;
if (fluid.isEmpty())
return;
World world = Minecraft.getInstance().world;
BlockPos pos = tileEntity.getPos();
BlockState state = world.getBlockState(pos);
spawnRimParticles(world, state, fluid, face, SPLASH_PARTICLE_AMOUNT);
}
@OnlyIn(Dist.CLIENT)
private void spawnRimParticles(World world, BlockState state, FluidStack fluid, Direction side, int amount) {
BlockPos pos = tileEntity.getPos();
if (FluidPropagator.isOpenEnd(world, pos, side)) {
spawnPouringLiquid(world, state, fluid, side, amount);
return;
}
IParticleData particle = FluidFX.getDrippingParticle(fluid);
float rimRadius = getRimRadius(state, side);
FluidFX.spawnRimParticles(world, pos, side, amount, particle, rimRadius);
}
@OnlyIn(Dist.CLIENT)
private void spawnPouringLiquid(World world, BlockState state, FluidStack fluid, Direction side, int amount) {
IParticleData particle = FluidFX.getFluidParticle(fluid);
float rimRadius = getRimRadius(state, side);
Vector3d directionVec = Vector3d.of(side.getDirectionVec());
BlockPos pos = tileEntity.getPos();
Couple<PipeFlows> couple = allFlows.get(side);
if (couple == null)
return;
couple.forEachWithContext((flow, inbound) -> {
if (flow.progress == null)
return;
FluidFX.spawnPouringLiquid(world, pos, amount, particle, rimRadius, directionVec, inbound);
});
}
@OnlyIn(Dist.CLIENT)
public static boolean isRenderEntityWithinDistance(BlockPos pos) {
Entity renderViewEntity = Minecraft.getInstance()
.getRenderViewEntity();
if (renderViewEntity == null)
return false;
Vector3d center = VecHelper.getCenterOf(pos);
if (renderViewEntity.getPositionVec()
.distanceTo(center) > MAX_PARTICLE_RENDER_DISTANCE)
return false;
return true;
}
static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25);
@Override
public void tick() {
super.tick();
boolean isRemote = getWorld().isRemote;
allFlows.values()
.forEach(c -> c.forEach(pf -> pf.tick(isRemote)));
if (isRemote) {
clientTick();
return;
}
if (collision != null) {
FluidReactions.handlePipeFlowCollision(getWorld(), tileEntity.getPos(), collision.getFirst(),
collision.getSecond());
collision = null;
return;
}
}
public Pair<Boolean, LerpedFloat> getStrogestFlow(Direction side) {
Couple<PipeFlows> couple = allFlows.get(side);
if (couple == null)
return null;
PipeFlows in = couple.get(true);
PipeFlows out = couple.get(false);
Couple<LerpedFloat> progress = couple.map(pf -> pf.progress);
boolean inboundStronger = false;
if (in.isCompleted() != out.isCompleted()) {
inboundStronger = in.isCompleted();
} else if ((progress.get(true) == null) != (progress.get(false) == null)) {
inboundStronger = progress.get(true) != null;
} else {
if (progress.get(true) != null)
inboundStronger = in.bestFlowStrength > out.bestFlowStrength;
}
return Pair.of(inboundStronger, progress.get(inboundStronger));
}
private void clientTick() {
spawnParticles();
if (!KineticDebugger.isActive())
return;
if (fluid.isEmpty())
return;
for (Entry<Direction, Couple<PipeFlows>> entry : allFlows.entrySet()) {
Direction face = entry.getKey();
Vector3d directionVec = Vector3d.of(face.getDirectionVec());
float size = 1 / 4f;
boolean extended = !isConnectedTo(tileEntity.getBlockState(), face.getOpposite());
float length = extended ? .75f : .5f;
entry.getValue()
.forEachWithContext((flow, inbound) -> {
if (flow.progress == null)
return;
float value = flow.progress.getValue();
Vector3d start = directionVec.scale(inbound ? .5 : .5f - length);
Vector3d offset = directionVec.scale(length * (inbound ? -1 : 1))
.scale(value);
Vector3d scale = new Vector3d(1, 1, 1).subtract(directionVec.scale(face.getAxisDirection()
.getOffset()))
.scale(size);
AxisAlignedBB bb =
new AxisAlignedBB(start, start.add(offset)).offset(VecHelper.getCenterOf(tileEntity.getPos()))
.grow(scale.x, scale.y, scale.z);
int color = 0x7fdbda;
if (!fluid.isEmpty()) {
Fluid fluid2 = fluid.getFluid();
if (fluid2 == Fluids.WATER)
color = 0x1D4D9B;
if (fluid2 == Fluids.LAVA)
color = 0xFF773D;
}
CreateClient.outliner.chaseAABB(Pair.of(this, face), bb)
.withFaceTexture(AllSpecialTextures.CUTOUT_CHECKERED)
.colored(color)
.lineWidth(1 / 16f);
});
}
}
private void contentsChanged() {
tileEntity.markDirty();
tileEntity.sendData();
}
private LerpedFloat createFlowProgress(double speed) {
return LerpedFloat.linear()
.startWithValue(0)
.chase(1, speed, Chaser.LINEAR);
}
public FluidStack getFluid() {
return fluid;
}
class PipeFlows {
LerpedFloat progress;
Set<FluidNetworkFlow> participants;
float bestFlowStrength;
void addFlow(FluidNetworkFlow flow) {
if (participants == null)
participants = new HashSet<>();
participants.add(flow);
if (progress == null) {
progress = createFlowProgress(flow.getSpeed());
}
}
boolean hasFlow(FluidNetworkFlow flow) {
return participants != null && participants.contains(flow);
}
void tick(boolean onClient) {
if (progress == null)
return;
if (!onClient) {
if (participants == null)
return;
bestFlowStrength = 0;
for (FluidNetworkFlow networkFlow : participants)
bestFlowStrength = Math.max(bestFlowStrength, networkFlow.getSpeed());
if (isCompleted())
return;
if (progress.updateChaseSpeed(bestFlowStrength))
contentsChanged();
}
progress.tickChaser();
}
void skip() {
progress = LerpedFloat.linear()
.startWithValue(1);
}
void removeFlow(FluidNetworkFlow flow) {
if (participants == null)
return;
participants.remove(flow);
}
boolean isActive() {
return participants != null && !participants.isEmpty();
}
boolean isCompleted() {
return progress != null && progress.getValue() == 1;
}
}
}

View file

@ -7,17 +7,15 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow;
import com.simibubi.create.content.contraptions.fluids.pipes.AxisPipeBlock; import com.simibubi.create.content.contraptions.fluids.pipes.AxisPipeBlock;
import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock; import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock;
import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.outliner.Outline.OutlineParams; import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -27,7 +25,6 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld; import net.minecraft.world.IWorld;
@ -36,6 +33,94 @@ import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
public class FluidPropagator { public class FluidPropagator {
public static void propagateChangedPipe(IWorld world, BlockPos pipePos, BlockState pipeState) {
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
Set<Pair<PumpTileEntity, Direction>> discoveredPumps = new HashSet<>();
frontier.add(Pair.of(0, pipePos));
// Visit all connected pumps to update their network
while (!frontier.isEmpty()) {
Pair<Integer, BlockPos> pair = frontier.remove(0);
BlockPos currentPos = pair.getSecond();
if (visited.contains(currentPos))
continue;
visited.add(currentPos);
BlockState currentState = currentPos.equals(pipePos) ? pipeState : world.getBlockState(currentPos);
FluidTransportBehaviour pipe = getPipe(world, currentPos);
if (pipe == null)
continue;
pipe.wipePressure();
for (Direction direction : getPipeConnections(currentState, pipe)) {
BlockPos target = currentPos.offset(direction);
if (!world.isAreaLoaded(target, 0))
continue;
TileEntity tileEntity = world.getTileEntity(target);
BlockState targetState = world.getBlockState(target);
if (tileEntity instanceof PumpTileEntity) {
if (!AllBlocks.MECHANICAL_PUMP.has(targetState) || targetState.get(PumpBlock.FACING)
.getAxis() != direction.getAxis())
continue;
discoveredPumps.add(Pair.of((PumpTileEntity) tileEntity, direction.getOpposite()));
continue;
}
if (visited.contains(target))
continue;
FluidTransportBehaviour targetPipe = getPipe(world, target);
if (targetPipe == null)
continue;
Integer distance = pair.getFirst();
if (distance >= getPumpRange() && !targetPipe.hasAnyPressure())
continue;
if (targetPipe.canHaveFlowToward(targetState, direction.getOpposite()))
frontier.add(Pair.of(distance + 1, target));
}
}
discoveredPumps.forEach(pair -> pair.getFirst()
.updatePipesOnSide(pair.getSecond()));
}
public static void resetAffectedFluidNetworks(World world, BlockPos start, Direction side) {
List<BlockPos> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
frontier.add(start);
while (!frontier.isEmpty()) {
BlockPos pos = frontier.remove(0);
if (visited.contains(pos))
continue;
visited.add(pos);
FluidTransportBehaviour pipe = getPipe(world, pos);
if (pipe == null)
continue;
for (Direction d : Iterate.directions) {
if (pos.equals(start) && d != side)
continue;
BlockPos target = pos.offset(d);
if (visited.contains(target))
continue;
PipeConnection connection = pipe.getConnection(d);
if (connection == null)
continue;
if (!connection.hasFlow())
continue;
Flow flow = connection.flow.get();
if (!flow.inbound)
continue;
connection.resetNetwork();
frontier.add(target);
}
}
}
public static Direction validateNeighbourChange(BlockState state, World world, BlockPos pos, Block otherBlock, public static Direction validateNeighbourChange(BlockState state, World world, BlockPos pos, Block otherBlock,
BlockPos neighborPos, boolean isMoving) { BlockPos neighborPos, boolean isMoving) {
if (world.isRemote) if (world.isRemote)
@ -59,15 +144,15 @@ public class FluidPropagator {
return null; return null;
} }
public static FluidPipeBehaviour getPipe(IBlockReader reader, BlockPos pos) { public static FluidTransportBehaviour getPipe(IBlockReader reader, BlockPos pos) {
return TileEntityBehaviour.get(reader, pos, FluidPipeBehaviour.TYPE); return TileEntityBehaviour.get(reader, pos, FluidTransportBehaviour.TYPE);
} }
public static boolean isOpenEnd(IBlockReader reader, BlockPos pos, Direction side) { public static boolean isOpenEnd(IBlockReader reader, BlockPos pos, Direction side) {
BlockPos connectedPos = pos.offset(side); BlockPos connectedPos = pos.offset(side);
BlockState connectedState = reader.getBlockState(connectedPos); BlockState connectedState = reader.getBlockState(connectedPos);
FluidPipeBehaviour pipe = FluidPropagator.getPipe(reader, connectedPos); FluidTransportBehaviour pipe = FluidPropagator.getPipe(reader, connectedPos);
if (pipe != null && pipe.isConnectedTo(connectedState, side.getOpposite())) if (pipe != null && pipe.canHaveFlowToward(connectedState, side.getOpposite()))
return false; return false;
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING) if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING)
.getAxis() == side.getAxis()) .getAxis() == side.getAxis())
@ -81,52 +166,10 @@ public class FluidPropagator {
return true; return true;
} }
public static void propagateChangedPipe(IWorld world, BlockPos pipePos, BlockState pipeState) { public static List<Direction> getPipeConnections(BlockState state, FluidTransportBehaviour pipe) {
List<BlockPos> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
frontier.add(pipePos);
// Visit all connected pumps to update their network
while (!frontier.isEmpty()) {
BlockPos currentPos = frontier.remove(0);
if (visited.contains(currentPos))
continue;
visited.add(currentPos);
BlockState currentState = currentPos.equals(pipePos) ? pipeState : world.getBlockState(currentPos);
FluidPipeBehaviour pipe = getPipe(world, currentPos);
if (pipe == null)
continue;
for (Direction direction : getPipeConnections(currentState, pipe)) {
BlockPos target = currentPos.offset(direction);
if (!world.isAreaLoaded(target, 0))
continue;
TileEntity tileEntity = world.getTileEntity(target);
BlockState targetState = world.getBlockState(target);
if (tileEntity instanceof PumpTileEntity) {
if (!AllBlocks.MECHANICAL_PUMP.has(targetState) || targetState.get(PumpBlock.FACING)
.getAxis() != direction.getAxis())
continue;
PumpTileEntity pump = (PumpTileEntity) tileEntity;
pump.updatePipesOnSide(direction.getOpposite());
continue;
}
if (visited.contains(target))
continue;
FluidPipeBehaviour targetPipe = getPipe(world, target);
if (targetPipe == null)
continue;
if (targetPipe.isConnectedTo(targetState, direction.getOpposite()))
frontier.add(target);
}
}
}
public static List<Direction> getPipeConnections(BlockState state, FluidPipeBehaviour pipe) {
List<Direction> list = new ArrayList<>(); List<Direction> list = new ArrayList<>();
for (Direction d : Iterate.directions) for (Direction d : Iterate.directions)
if (pipe.isConnectedTo(state, d)) if (pipe.canHaveFlowToward(state, d))
list.add(d); list.add(d);
return list; return list;
} }
@ -135,37 +178,38 @@ public class FluidPropagator {
return AllConfigs.SERVER.fluids.mechanicalPumpRange.get(); return AllConfigs.SERVER.fluids.mechanicalPumpRange.get();
} }
@Deprecated // Remove after pipes are fixed; comment out for production // static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25);
public static OutlineParams showBlockFace(BlockFace face) { //
MutableObject<OutlineParams> params = new MutableObject<>(new OutlineParams()); // @Deprecated
// public static OutlineParams showBlockFace(BlockFace face) {
// MutableObject<OutlineParams> params = new MutableObject<>(new OutlineParams());
// DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { // DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> {
// Vector3d directionVec = new Vector3d(face.getFace() // Vector3d directionVec = new Vector3d(face.getFace()
// .getDirectionVec()); // .getDirectionVec());
// Vector3d scaleVec = directionVec.scale(-.25f * face.getFace() // Vector3d scaleVec = directionVec.scale(-.25f * face.getFace()
// .getAxisDirection() // .getAxisDirection()
// .getOffset()); // .getOffset());
// directionVec = directionVec.scale(.5f); // directionVec = directionVec.scale(.45f);
// params.setValue(CreateClient.outliner.showAABB(face, // params.setValue(CreateClient.outliner.showAABB(face,
// FluidPropagator.smallCenter.offset(directionVec.add(new Vector3d(face.getPos()))) // FluidPropagator.smallCenter.offset(directionVec.add(new Vector3d(face.getPos())))
// .grow(scaleVec.x, scaleVec.y, scaleVec.z) // .grow(scaleVec.x, scaleVec.y, scaleVec.z)
// .grow(1 / 16f))); // .grow(1 / 16f)));
// }); // });
return params.getValue(); // return params.getValue()
} // .lineWidth(1 / 16f);
// }
static AxisAlignedBB smallCenter = new AxisAlignedBB(BlockPos.ZERO).shrink(.25); public static boolean hasFluidCapability(IBlockReader world, BlockPos pos, Direction side) {
public static boolean hasFluidCapability(BlockState state, IBlockReader world, BlockPos pos, Direction blockFace) {
if (!state.hasTileEntity())
return false;
TileEntity tileEntity = world.getTileEntity(pos); TileEntity tileEntity = world.getTileEntity(pos);
return tileEntity != null return tileEntity != null && tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side)
&& tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, blockFace.getOpposite())
.isPresent(); .isPresent();
} }
@Nullable @Nullable
public static Axis getStraightPipeAxis(BlockState state) { public static Axis getStraightPipeAxis(BlockState state) {
if (state.getBlock() instanceof PumpBlock)
return state.get(PumpBlock.FACING)
.getAxis();
if (state.getBlock() instanceof AxisPipeBlock) if (state.getBlock() instanceof AxisPipeBlock)
return state.get(AxisPipeBlock.AXIS); return state.get(AxisPipeBlock.AXIS);
if (!FluidPipeBlock.isPipe(state)) if (!FluidPipeBlock.isPipe(state))

View file

@ -1,12 +1,15 @@
package com.simibubi.create.content.contraptions.fluids; package com.simibubi.create.content.contraptions.fluids;
import com.simibubi.create.AllFluids;
import com.simibubi.create.foundation.fluid.FluidHelper; import com.simibubi.create.foundation.fluid.FluidHelper;
import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.BlockHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.fluid.Fluid; import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids; import net.minecraft.fluid.Fluids;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
@ -19,19 +22,41 @@ public class FluidReactions {
BlockHelper.destroyBlock(world, pos, 1); BlockHelper.destroyBlock(world, pos, 1);
if (f1 == Fluids.WATER && f2 == Fluids.LAVA || f2 == Fluids.WATER && f1 == Fluids.LAVA) if (f1 == Fluids.WATER && f2 == Fluids.LAVA || f2 == Fluids.WATER && f1 == Fluids.LAVA)
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
else if (f1 == Fluids.LAVA && FluidHelper.hasBlockState(f2)) {
BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(f2)
.getDefaultState());
if (lavaInteraction != null)
world.setBlockState(pos, lavaInteraction);
} else if (f2 == Fluids.LAVA && FluidHelper.hasBlockState(f1)) {
BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(f1)
.getDefaultState());
if (lavaInteraction != null)
world.setBlockState(pos, lavaInteraction);
}
} }
public static void handlePipeSpillCollision(World world, BlockPos pos, Fluid pipeFluid, FluidState worldFluid) { public static void handlePipeSpillCollision(World world, BlockPos pos, Fluid pipeFluid, FluidState worldFluid) {
Fluid pf = FluidHelper.convertToStill(pipeFluid); Fluid pf = FluidHelper.convertToStill(pipeFluid);
Fluid wf = worldFluid.getFluid(); Fluid wf = worldFluid.getFluid();
if (pf == Fluids.WATER && wf == Fluids.LAVA) if (pf.isIn(FluidTags.WATER) && wf == Fluids.LAVA)
world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState()); world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState());
if (pf == Fluids.WATER && wf == Fluids.FLOWING_LAVA) else if (pf == Fluids.WATER && wf == Fluids.FLOWING_LAVA)
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
else if (pf == Fluids.LAVA && wf == Fluids.WATER) else if (pf == Fluids.LAVA && wf == Fluids.WATER)
world.setBlockState(pos, Blocks.STONE.getDefaultState()); world.setBlockState(pos, Blocks.STONE.getDefaultState());
else if (pf == Fluids.LAVA && wf == Fluids.FLOWING_WATER) else if (pf == Fluids.LAVA && wf == Fluids.FLOWING_WATER)
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState()); world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
if (pf == Fluids.LAVA) {
BlockState lavaInteraction = AllFluids.getLavaInteraction(worldFluid);
if (lavaInteraction != null)
world.setBlockState(pos, lavaInteraction);
} else if (wf == Fluids.FLOWING_LAVA && FluidHelper.hasBlockState(pf)) {
BlockState lavaInteraction = AllFluids.getLavaInteraction(FluidHelper.convertToFlowing(pf)
.getDefaultState());
if (lavaInteraction != null)
world.setBlockState(pos, lavaInteraction);
}
} }
} }

View file

@ -0,0 +1,266 @@
package com.simibubi.create.content.contraptions.fluids;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.fluids.pipes.EncasedPipeBlock;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.ILightReader;
import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack;
public abstract class FluidTransportBehaviour extends TileEntityBehaviour {
public static BehaviourType<FluidTransportBehaviour> TYPE = new BehaviourType<>();
enum UpdatePhase {
WAIT_FOR_PUMPS, // Do not run Layer II logic while pumps could still be distributing pressure
FLIP_FLOWS, // Do not cut any flows until all pipes had a chance to reverse them
IDLE; // Operate normally
}
Map<Direction, PipeConnection> interfaces;
UpdatePhase phase;
public FluidTransportBehaviour(SmartTileEntity te) {
super(te);
phase = UpdatePhase.WAIT_FOR_PUMPS;
}
public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) {
return true;
}
public abstract boolean canHaveFlowToward(BlockState state, Direction direction);
@Override
public void initialize() {
super.initialize();
createConnectionData();
}
@Override
public void tick() {
super.tick();
World world = getWorld();
BlockPos pos = getPos();
boolean onClient = world.isRemote;
Collection<PipeConnection> connections = interfaces.values();
// Do not provide a lone pipe connection with its own flow input
PipeConnection singleSource = null;
// if (onClient) {
// connections.forEach(connection -> {
// connection.visualizeFlow(pos);
// connection.visualizePressure(pos);
// });
// }
if (phase == UpdatePhase.WAIT_FOR_PUMPS) {
phase = UpdatePhase.FLIP_FLOWS;
return;
}
if (!onClient) {
boolean sendUpdate = false;
for (PipeConnection connection : connections) {
sendUpdate |= connection.flipFlowsIfPressureReversed();
connection.manageSource(world, pos);
}
if (sendUpdate)
tileEntity.notifyUpdate();
}
if (phase == UpdatePhase.FLIP_FLOWS) {
phase = UpdatePhase.IDLE;
return;
}
if (!onClient) {
FluidStack availableFlow = FluidStack.EMPTY;
FluidStack collidingFlow = FluidStack.EMPTY;
for (PipeConnection connection : connections) {
FluidStack fluidInFlow = connection.getProvidedFluid();
if (fluidInFlow.isEmpty())
continue;
if (availableFlow.isEmpty()) {
singleSource = connection;
availableFlow = fluidInFlow;
continue;
}
if (availableFlow.isFluidEqual(fluidInFlow)) {
singleSource = null;
availableFlow = fluidInFlow;
continue;
}
collidingFlow = fluidInFlow;
break;
}
if (!collidingFlow.isEmpty()) {
FluidReactions.handlePipeFlowCollision(world, pos, availableFlow, collidingFlow);
return;
}
boolean sendUpdate = false;
for (PipeConnection connection : connections) {
FluidStack internalFluid = singleSource != connection ? availableFlow : FluidStack.EMPTY;
Predicate<FluidStack> extractionPredicate =
extracted -> canPullFluidFrom(extracted, tileEntity.getBlockState(), connection.side);
sendUpdate |= connection.manageFlows(world, pos, internalFluid, extractionPredicate);
}
if (sendUpdate)
tileEntity.notifyUpdate();
}
for (PipeConnection connection : connections)
connection.tickFlowProgress(world, pos);
}
@Override
public void read(CompoundNBT nbt, boolean clientPacket) {
super.read(nbt, clientPacket);
if (interfaces == null)
interfaces = new IdentityHashMap<>();
for (Direction face : Iterate.directions)
if (nbt.contains(face.getName()))
interfaces.computeIfAbsent(face, d -> new PipeConnection(d));
// Invalid data (missing/outdated). Defer init to runtime
if (interfaces.isEmpty()) {
interfaces = null;
return;
}
interfaces.values()
.forEach(connection -> connection.deserializeNBT(nbt, clientPacket));
}
@Override
public void write(CompoundNBT nbt, boolean clientPacket) {
super.write(nbt, clientPacket);
if (clientPacket)
createConnectionData();
if (interfaces == null)
return;
interfaces.values()
.forEach(connection -> connection.serializeNBT(nbt, clientPacket));
}
public FluidStack getProvidedOutwardFluid(Direction side) {
createConnectionData();
if (!interfaces.containsKey(side))
return FluidStack.EMPTY;
return interfaces.get(side)
.provideOutboundFlow();
}
@Nullable
public PipeConnection getConnection(Direction side) {
createConnectionData();
return interfaces.get(side);
}
public boolean hasAnyPressure() {
createConnectionData();
for (PipeConnection pipeConnection : interfaces.values())
if (pipeConnection.hasPressure())
return true;
return false;
}
@Nullable
public PipeConnection.Flow getFlow(Direction side) {
createConnectionData();
if (!interfaces.containsKey(side))
return null;
return interfaces.get(side).flow.orElse(null);
}
public void addPressure(Direction side, boolean inbound, float pressure) {
createConnectionData();
if (!interfaces.containsKey(side))
return;
interfaces.get(side)
.addPressure(inbound, pressure);
tileEntity.sendData();
}
public void wipePressure() {
if (interfaces != null)
for (Direction d : Iterate.directions) {
if (!canHaveFlowToward(tileEntity.getBlockState(), d))
interfaces.remove(d);
else
interfaces.computeIfAbsent(d, PipeConnection::new);
}
phase = UpdatePhase.WAIT_FOR_PUMPS;
createConnectionData();
interfaces.values()
.forEach(PipeConnection::wipePressure);
tileEntity.sendData();
}
private void createConnectionData() {
if (interfaces != null)
return;
interfaces = new IdentityHashMap<>();
for (Direction d : Iterate.directions)
if (canHaveFlowToward(tileEntity.getBlockState(), d))
interfaces.put(d, new PipeConnection(d));
}
public AttachmentTypes getRenderedRimAttachment(ILightReader world, BlockPos pos, BlockState state,
Direction direction) {
if (!canHaveFlowToward(state, direction))
return AttachmentTypes.NONE;
BlockPos offsetPos = pos.offset(direction);
BlockState facingState = world.getBlockState(offsetPos);
if (facingState.getBlock() instanceof PumpBlock && facingState.get(PumpBlock.FACING)
.getAxis() == direction.getAxis())
return AttachmentTypes.NONE;
if (AllBlocks.ENCASED_FLUID_PIPE.has(facingState)
&& facingState.get(EncasedPipeBlock.FACING_TO_PROPERTY_MAP.get(direction.getOpposite())))
return AttachmentTypes.NONE;
if (FluidPropagator.hasFluidCapability(world, offsetPos, direction.getOpposite())
&& !AllBlocks.HOSE_PULLEY.has(facingState))
return AttachmentTypes.DRAIN;
return AttachmentTypes.RIM;
}
public static enum AttachmentTypes {
NONE, RIM, DRAIN;
public boolean hasModel() {
return this != NONE;
}
}
@Override
public BehaviourType<?> getType() {
return TYPE;
}
}

View file

@ -1,108 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import java.lang.ref.WeakReference;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.world.IWorld;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
public class InterPumpEndpoint extends FluidNetworkEndpoint {
Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> pumps;
private InterPumpEndpoint(IWorld world, BlockFace location, LazyOptional<IFluidHandler> handler) {
super(world, location, handler);
}
public InterPumpEndpoint(IWorld world, BlockFace location, PumpTileEntity source, PumpTileEntity interfaced,
BlockFace sourcePos, BlockFace interfacedPos) {
this(world, location, LazyOptional.empty());
handler = LazyOptional.of(() -> new InterPumpFluidHandler(this));
pumps = Couple.create(Pair.of(sourcePos, new WeakReference<>(source)),
Pair.of(interfacedPos, new WeakReference<>(interfaced)));
}
public InterPumpEndpoint opposite(IWorld world) {
InterPumpEndpoint interPumpEndpoint = new InterPumpEndpoint(world, this.location.getOpposite(), handler);
interPumpEndpoint.pumps = pumps.copy();
return interPumpEndpoint;
}
public Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> getPumps() {
return pumps;
}
public boolean isPulling(boolean first) {
Pair<BlockFace, WeakReference<PumpTileEntity>> pair = getPumps().get(first);
PumpTileEntity pumpTileEntity = pair.getSecond()
.get();
if (pumpTileEntity == null || pumpTileEntity.isRemoved())
return false;
return pumpTileEntity.isPullingOnSide(pumpTileEntity.isFront(pair.getFirst()
.getFace()));
}
public int getTransferSpeed(boolean first) {
PumpTileEntity pumpTileEntity = getPumps().get(first)
.getSecond()
.get();
if (pumpTileEntity == null || pumpTileEntity.isRemoved())
return 0;
return pumpTileEntity.getFluidTransferSpeed();
}
@Override
public LazyOptional<IFluidHandler> provideHandler() {
if (isPulling(true) == isPulling(false))
return LazyOptional.empty();
if (getTransferSpeed(true) > getTransferSpeed(false))
return LazyOptional.empty();
return super.provideHandler();
}
@Override
public FluidStack provideFluid() {
if (!provideHandler().isPresent())
return FluidStack.EMPTY;
Couple<Pair<BlockFace, WeakReference<PumpTileEntity>>> pumps = getPumps();
for (boolean current : Iterate.trueAndFalse) {
if (isPulling(current))
continue;
Pair<BlockFace, WeakReference<PumpTileEntity>> pair = pumps.get(current);
BlockFace blockFace = pair.getFirst();
PumpTileEntity pumpTileEntity = pair.getSecond()
.get();
if (pumpTileEntity == null)
continue;
if (pumpTileEntity.networks == null)
continue;
FluidNetwork fluidNetwork = pumpTileEntity.networks.get(pumpTileEntity.isFront(blockFace.getFace()));
for (FluidNetworkFlow fluidNetworkFlow : fluidNetwork.flows) {
for (FluidNetworkEndpoint fne : fluidNetworkFlow.outputEndpoints) {
if (!(fne instanceof InterPumpEndpoint))
continue;
InterPumpEndpoint ipe = (InterPumpEndpoint) fne;
if (!ipe.location.isEquivalent(location))
continue;
FluidStack heldFluid = fluidNetworkFlow.fluidStack;
if (heldFluid.isEmpty())
return heldFluid;
FluidStack copy = heldFluid.copy();
copy.setAmount(1);
return heldFluid;
}
}
}
return FluidStack.EMPTY;
}
}

View file

@ -1,44 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.templates.FluidTank;
public class InterPumpFluidHandler extends FluidTank {
InterPumpEndpoint endpoint;
public InterPumpFluidHandler(InterPumpEndpoint endpoint) {
super(Integer.MAX_VALUE);
this.endpoint = endpoint;
}
@Override
public int fill(FluidStack resource, FluidAction action) {
if (resource.isEmpty())
return 0;
int maxInput = Math.min(resource.getAmount(), Math.max(getTransferCapacity() - getFluidAmount(), 0));
FluidStack toInsert = resource.copy();
toInsert.setAmount(maxInput);
FluidPropagator.showBlockFace(endpoint.location).colored(0x77d196).lineWidth(1/4f);
return super.fill(toInsert, action);
}
@Override
public FluidStack drain(int maxDrain, FluidAction action) {
return super.drain(maxDrain, action);
}
public FluidStack provide() {
FluidStack heldFluid = getFluid();
if (heldFluid.isEmpty())
return heldFluid;
FluidStack copy = heldFluid.copy();
copy.setAmount(1);
return copy;
}
private int getTransferCapacity() {
return Math.min(endpoint.getTransferSpeed(true), endpoint.getTransferSpeed(false));
}
}

View file

@ -9,12 +9,10 @@ import com.simibubi.create.content.contraptions.fluids.potion.PotionFluidHandler
import com.simibubi.create.foundation.fluid.FluidHelper; import com.simibubi.create.foundation.fluid.FluidHelper;
import com.simibubi.create.foundation.utility.BlockFace; import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.BlockHelper; import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.FlowingFluidBlock; import net.minecraft.block.FlowingFluidBlock;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState; import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids; import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@ -25,7 +23,6 @@ import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.PotionUtils; import net.minecraft.potion.PotionUtils;
import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.SoundCategory; import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents; import net.minecraft.util.SoundEvents;
@ -35,10 +32,9 @@ import net.minecraft.world.World;
import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fluids.capability.templates.FluidTank; import net.minecraftforge.fluids.capability.templates.FluidTank;
public class OpenEndedPipe { public class OpenEndedPipe extends FlowSource {
World world; World world;
BlockPos pos; BlockPos pos;
@ -47,12 +43,12 @@ public class OpenEndedPipe {
private OpenEndFluidHandler fluidHandler; private OpenEndFluidHandler fluidHandler;
private BlockPos outputPos; private BlockPos outputPos;
private boolean wasPulling; private boolean wasPulling;
private boolean stale;
private FluidStack cachedFluid; private FluidStack cachedFluid;
private List<EffectInstance> cachedEffects; private List<EffectInstance> cachedEffects;
public OpenEndedPipe(BlockFace face) { public OpenEndedPipe(BlockFace face) {
super(face);
fluidHandler = new OpenEndFluidHandler(); fluidHandler = new OpenEndFluidHandler();
outputPos = face.getConnectedPos(); outputPos = face.getConnectedPos();
pos = face.getPos(); pos = face.getPos();
@ -61,87 +57,106 @@ public class OpenEndedPipe {
aoe = aoe.expand(0, -1, 0); aoe = aoe.expand(0, -1, 0);
} }
public void tick(World world, boolean pulling) { @Override
public void manageSource(World world) {
this.world = world; this.world = world;
if (!world.isAreaLoaded(outputPos, 0))
return;
if (pulling != wasPulling) {
if (pulling)
fluidHandler.clear();
wasPulling = pulling;
} }
private FluidStack removeFluidFromSpace(boolean simulate) {
FluidStack empty = FluidStack.EMPTY;
if (world == null)
return empty;
if (!world.isAreaLoaded(outputPos, 0))
return empty;
BlockState state = world.getBlockState(outputPos); BlockState state = world.getBlockState(outputPos);
FluidState fluidState = state.getFluidState(); FluidState fluidState = state.getFluidState();
boolean waterlog = BlockHelper.hasBlockStateProperty(state, BlockStateProperties.WATERLOGGED); boolean waterlog = BlockHelper.hasBlockStateProperty(state, BlockStateProperties.WATERLOGGED);
if (!waterlog && !state.getMaterial() if (!waterlog && !state.getMaterial()
.isReplaceable()) .isReplaceable())
return; return empty;
if (pulling) {
if (fluidState.isEmpty() || !fluidState.isSource()) if (fluidState.isEmpty() || !fluidState.isSource())
return; return empty;
if (!fluidHandler.tryCollectFluid(fluidState.getFluid()))
return; FluidStack stack = new FluidStack(fluidState.getFluid(), 1000);
if (simulate)
return stack;
if (waterlog) { if (waterlog) {
world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, false), 3); world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, false), 3);
world.getPendingFluidTicks() world.getPendingFluidTicks()
.scheduleTick(outputPos, Fluids.WATER, 1); .scheduleTick(outputPos, Fluids.WATER, 1);
return; return stack;
} }
world.setBlockState(outputPos, fluidState.getBlockState() world.setBlockState(outputPos, fluidState.getBlockState()
.with(FlowingFluidBlock.LEVEL, 14), 3); .with(FlowingFluidBlock.LEVEL, 14), 3);
return; return stack;
} }
FluidStack fluid = fluidHandler.getFluid(); private boolean provideFluidToSpace(FluidStack fluid, boolean simulate) {
if (world == null)
return false;
if (!world.isAreaLoaded(outputPos, 0))
return false;
BlockState state = world.getBlockState(outputPos);
FluidState fluidState = state.getFluidState();
boolean waterlog = state.contains(BlockStateProperties.WATERLOGGED);
if (!waterlog && !state.getMaterial()
.isReplaceable())
return false;
if (fluid.isEmpty()) if (fluid.isEmpty())
return; return false;
if (!FluidHelper.hasBlockState(fluid.getFluid())) { if (!FluidHelper.hasBlockState(fluid.getFluid())) {
fluidHandler.drain(fluid.getAmount() > 1 ? fluid.getAmount() - 1 : 1, FluidAction.EXECUTE); if (!simulate)
if (fluidHandler.isEmpty()) applyEffects(world, fluid);
updatePumpIfNecessary(); return true;
if (!fluid.getFluid()
.isEquivalentTo(AllFluids.POTION.get()))
return;
applyPotionEffects(world, fluid);
return;
} }
Fluid providedFluid = fluidHandler.tryProvidingFluid(); if (!fluidState.isEmpty() && fluidState.getFluid() != fluid.getFluid()) {
if (providedFluid == null) FluidReactions.handlePipeSpillCollision(world, outputPos, fluid.getFluid(), fluidState);
return; return false;
if (!fluidState.isEmpty() && fluidState.getFluid() != providedFluid) {
FluidReactions.handlePipeSpillCollision(world, outputPos, providedFluid, fluidState);
return;
} }
if (fluidState.isSource()) if (fluidState.isSource())
return; return false;
if (waterlog && fluid.getFluid() != Fluids.WATER)
return false;
if (simulate)
return true;
if (world.getDimension().isUltrawarm() && providedFluid.getFluid() if (world.getDimension().isUltrawarm() && fluid.getFluid()
.isIn(FluidTags.WATER)) { .isIn(FluidTags.WATER)) {
int i = outputPos.getX(); int i = outputPos.getX();
int j = outputPos.getY(); int j = outputPos.getY();
int k = outputPos.getZ(); int k = outputPos.getZ();
world.playSound(null, i, j, k, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F, world.playSound(null, i, j, k, SoundEvents.BLOCK_FIRE_EXTINGUISH, SoundCategory.BLOCKS, 0.5F,
2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F); 2.6F + (world.rand.nextFloat() - world.rand.nextFloat()) * 0.8F);
return; return true;
} }
if (waterlog) { if (waterlog) {
if (providedFluid.getFluid() != Fluids.WATER)
return;
world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, true), 3); world.setBlockState(outputPos, state.with(BlockStateProperties.WATERLOGGED, true), 3);
world.getPendingFluidTicks() world.getPendingFluidTicks()
.scheduleTick(outputPos, Fluids.WATER, 1); .scheduleTick(outputPos, Fluids.WATER, 1);
return; return true;
} }
world.setBlockState(outputPos, providedFluid.getDefaultState() world.setBlockState(outputPos, fluid.getFluid()
.getDefaultState()
.getBlockState(), 3); .getBlockState(), 3);
return true;
}
private void applyEffects(World world, FluidStack fluid) {
if (!fluid.getFluid()
.isEquivalentTo(AllFluids.POTION.get())) {
// other fx
return;
} }
private void applyPotionEffects(World world, FluidStack fluid) {
if (cachedFluid == null || cachedEffects == null || !fluid.isFluidEqual(cachedFluid)) { if (cachedFluid == null || cachedEffects == null || !fluid.isFluidEqual(cachedFluid)) {
FluidStack copy = fluid.copy(); FluidStack copy = fluid.copy();
copy.setAmount(250); copy.setAmount(250);
@ -167,47 +182,30 @@ public class OpenEndedPipe {
} }
public LazyOptional<IFluidHandler> getCapability() { @Override
public LazyOptional<IFluidHandler> provideHandler() {
return LazyOptional.of(() -> fluidHandler); return LazyOptional.of(() -> fluidHandler);
} }
public CompoundNBT writeToNBT(CompoundNBT compound) { public CompoundNBT serializeNBT() {
CompoundNBT compound = new CompoundNBT();
fluidHandler.writeToNBT(compound); fluidHandler.writeToNBT(compound);
compound.putBoolean("Pulling", wasPulling); compound.putBoolean("Pulling", wasPulling);
compound.put("Location", location.serializeNBT());
return compound; return compound;
} }
public void readNBT(CompoundNBT compound) { public static OpenEndedPipe fromNBT(CompoundNBT compound) {
fluidHandler.readFromNBT(compound); OpenEndedPipe oep = new OpenEndedPipe(BlockFace.fromNBT(compound.getCompound("Location")));
wasPulling = compound.getBoolean("Pulling"); oep.fluidHandler.readFromNBT(compound);
} oep.wasPulling = compound.getBoolean("Pulling");
return oep;
public void markStale() {
stale = true;
}
public void unmarkStale() {
stale = false;
}
public boolean isStale() {
return stale;
}
private void updatePumpIfNecessary() {
if (world == null)
return;
if (!PumpBlock.isPump(world.getBlockState(pos)))
return;
TileEntity tileEntity = world.getTileEntity(pos);
if (tileEntity instanceof PumpTileEntity)
((PumpTileEntity) tileEntity).sendData();
} }
private class OpenEndFluidHandler extends FluidTank { private class OpenEndFluidHandler extends FluidTank {
public OpenEndFluidHandler() { public OpenEndFluidHandler() {
super(1500); super(1000);
} }
@Override @Override
@ -219,76 +217,76 @@ public class OpenEndedPipe {
return 0; return 0;
if (resource.isEmpty()) if (resource.isEmpty())
return 0; return 0;
if (!provideFluidToSpace(resource, true))
FluidStack prevFluid = getFluid();
BlockState state = world.getBlockState(outputPos);
FluidState fluidState = state.getFluidState();
if (!fluidState.isEmpty() && fluidState.getFluid() != resource.getFluid()) {
FluidReactions.handlePipeSpillCollision(world, outputPos, resource.getFluid(), fluidState);
return 0;
}
if (fluidState.isSource())
return 0;
if (!(BlockHelper.hasBlockStateProperty(state, BlockStateProperties.WATERLOGGED) && resource.getFluid() == Fluids.WATER)
&& !state.getMaterial()
.isReplaceable())
return 0; return 0;
// Never allow being filled above 1000 if (!getFluid().isEmpty() && !getFluid().isFluidEqual(resource))
FluidStack insertable = resource.copy(); setFluid(FluidStack.EMPTY);
insertable.setAmount(Math.min(insertable.getAmount(), Math.max(1000 - getFluidAmount(), 0))); if (wasPulling)
int fill = super.fill(insertable, action); wasPulling = false;
if (!getFluid().isFluidEqual(prevFluid))
updatePumpIfNecessary();
int fill = super.fill(resource, action);
if (action.execute() && (getFluidAmount() == 1000 || !FluidHelper.hasBlockState(getFluid().getFluid()))
&& provideFluidToSpace(getFluid(), false))
setFluid(FluidStack.EMPTY);
return fill; return fill;
} }
@Override @Override
public FluidStack drain(FluidStack resource, FluidAction action) { public FluidStack drain(FluidStack resource, FluidAction action) {
boolean wasEmpty = isEmpty(); return drainInner(resource.getAmount(), resource, action);
FluidStack drain = super.drain(resource, action);
if (action.execute() && !wasEmpty && isEmpty())
updatePumpIfNecessary();
return drain;
} }
@Override @Override
public FluidStack drain(int maxDrain, FluidAction action) { public FluidStack drain(int maxDrain, FluidAction action) {
boolean wasEmpty = isEmpty(); return drainInner(maxDrain, null, action);
FluidStack drain = super.drain(maxDrain, action);
if (action.execute() && !wasEmpty && isEmpty())
updatePumpIfNecessary();
return drain;
} }
public boolean tryCollectFluid(Fluid fluid) { private FluidStack drainInner(int amount, @Nullable FluidStack filter, FluidAction action) {
for (boolean simulate : Iterate.trueAndFalse) FluidStack empty = FluidStack.EMPTY;
if (super.fill(new FluidStack(fluid, 1000), boolean filterPresent = filter != null;
simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE) != 1000)
return false; if (world == null)
updatePumpIfNecessary(); return empty;
if (!world.isAreaLoaded(outputPos, 0))
return empty;
if (amount == 0)
return empty;
if (amount > 1000) {
amount = 1000;
if (filterPresent)
filter = FluidHelper.copyStackWithAmount(filter, amount);
}
if (!wasPulling)
wasPulling = true;
FluidStack drainedFromInternal = filterPresent ? super.drain(filter, action) : super.drain(amount, action);
if (!drainedFromInternal.isEmpty())
return drainedFromInternal;
FluidStack drainedFromWorld = removeFluidFromSpace(action.simulate());
if (drainedFromWorld.isEmpty())
return FluidStack.EMPTY;
if (filterPresent && !drainedFromWorld.isFluidEqual(filter))
return FluidStack.EMPTY;
int remainder = drainedFromWorld.getAmount() - amount;
drainedFromWorld.setAmount(amount);
if (!action.simulate() && remainder > 0) {
if (!getFluid().isEmpty() && !getFluid().isFluidEqual(drainedFromWorld))
setFluid(FluidStack.EMPTY);
super.fill(FluidHelper.copyStackWithAmount(drainedFromWorld, remainder), FluidAction.EXECUTE);
}
return drainedFromWorld;
}
}
@Override
public boolean isEndpoint() {
return true; return true;
} }
@Nullable
public Fluid tryProvidingFluid() {
Fluid fluid = getFluid().getFluid();
for (boolean simulate : Iterate.trueAndFalse)
if (drain(1000, simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE).getAmount() != 1000)
return null;
updatePumpIfNecessary();
return fluid;
}
public void clear() {
boolean wasEmpty = isEmpty();
setFluid(FluidStack.EMPTY);
if (!wasEmpty)
updatePumpIfNecessary();
}
}
} }

View file

@ -6,8 +6,9 @@ import java.util.List;
import java.util.Random; import java.util.Random;
import com.simibubi.create.AllBlockPartials; import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour.AttachmentTypes; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour.AttachmentTypes;
import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock; import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.foundation.block.connected.BakedModelWrapperWithData; import com.simibubi.create.foundation.block.connected.BakedModelWrapperWithData;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -35,16 +36,16 @@ public class PipeAttachmentModel extends BakedModelWrapperWithData {
@Override @Override
protected Builder gatherModelData(Builder builder, IBlockDisplayReader world, BlockPos pos, BlockState state) { protected Builder gatherModelData(Builder builder, IBlockDisplayReader world, BlockPos pos, BlockState state) {
PipeModelData data = new PipeModelData(); PipeModelData data = new PipeModelData();
FluidPipeAttachmentBehaviour attachmentBehaviour = FluidTransportBehaviour transport = TileEntityBehaviour.get(world, pos, FluidTransportBehaviour.TYPE);
TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (attachmentBehaviour != null) { if (transport != null)
for (Direction d : Iterate.directions) for (Direction d : Iterate.directions)
data.putRim(d, attachmentBehaviour.getAttachment(world, pos, state, d)); data.putRim(d, transport.getRenderedRimAttachment(world, pos, state, d));
data.putBracket(attachmentBehaviour.getBracket()); if (bracket != null)
} data.putBracket(bracket.getBracket());
data.setEncased(FluidPipeBlock.shouldDrawCasing(world, pos, state));
data.setEncased(FluidPipeBlock.shouldDrawCasing(world, pos, state));
return builder.withInitial(PIPE_PROPERTY, data); return builder.withInitial(PIPE_PROPERTY, data);
} }

View file

@ -0,0 +1,476 @@
package com.simibubi.create.content.contraptions.fluids;
import java.util.Optional;
import java.util.Random;
import java.util.function.Predicate;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.LerpedFloat;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.FloatNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.particles.IParticleData;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.DistExecutor;
public class PipeConnection {
Direction side;
// Layer I
Couple<Float> pressure; // [inbound, outward]
Optional<FlowSource> source;
Optional<FlowSource> previousSource;
// Layer II
Optional<Flow> flow;
boolean particleSplashNextTick;
// Layer III
Optional<FluidNetwork> network; // not serialized
public PipeConnection(Direction side) {
this.side = side;
pressure = Couple.create(() -> 0f);
flow = Optional.empty();
previousSource = Optional.empty();
source = Optional.empty();
network = Optional.empty();
particleSplashNextTick = false;
}
public FluidStack getProvidedFluid() {
FluidStack empty = FluidStack.EMPTY;
if (!hasFlow())
return empty;
Flow flow = this.flow.get();
if (!flow.inbound)
return empty;
if (!flow.complete)
return empty;
return flow.fluid;
}
public boolean flipFlowsIfPressureReversed() {
if (!hasFlow())
return false;
boolean singlePressure = comparePressure() != 0 && (getInboundPressure() == 0 || getOutwardPressure() == 0);
Flow flow = this.flow.get();
if (!singlePressure || comparePressure() < 0 == flow.inbound)
return false;
flow.inbound = !flow.inbound;
if (!flow.complete)
this.flow = Optional.empty();
return true;
}
public void manageSource(World world, BlockPos pos) {
if (!source.isPresent() && !determineSource(world, pos))
return;
FlowSource flowSource = source.get();
flowSource.manageSource(world);
}
public boolean manageFlows(World world, BlockPos pos, FluidStack internalFluid,
Predicate<FluidStack> extractionPredicate) {
// Only keep network if still valid
Optional<FluidNetwork> retainedNetwork = network;
network = Optional.empty();
// chunk border
if (!source.isPresent() && !determineSource(world, pos))
return false;
FlowSource flowSource = source.get();
if (!hasFlow()) {
if (!hasPressure())
return false;
// Try starting a new flow
boolean prioritizeInbound = comparePressure() < 0;
for (boolean trueFalse : Iterate.trueAndFalse) {
boolean inbound = prioritizeInbound == trueFalse;
if (pressure.get(inbound) == 0)
continue;
if (tryStartingNewFlow(inbound, inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid))
return true;
}
return false;
}
// Manage existing flow
Flow flow = this.flow.get();
FluidStack provided = flow.inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid;
if (!hasPressure() || provided.isEmpty() || !provided.isFluidEqual(flow.fluid)) {
this.flow = Optional.empty();
return true;
}
// Overwrite existing flow
if (flow.inbound != comparePressure() < 0) {
boolean inbound = !flow.inbound;
if (inbound && !provided.isEmpty() || !inbound && !internalFluid.isEmpty()) {
FluidPropagator.resetAffectedFluidNetworks(world, pos, side);
tryStartingNewFlow(inbound, inbound ? flowSource.provideFluid(extractionPredicate) : internalFluid);
return true;
}
}
flowSource.whileFlowPresent(world, flow.inbound);
if (!flowSource.isEndpoint())
return false;
if (!flow.inbound)
return false;
// Layer III
network = retainedNetwork;
if (!hasNetwork())
network = Optional.of(new FluidNetwork(world, new BlockFace(pos, side), flowSource::provideHandler));
network.get()
.tick();
return false;
}
private boolean tryStartingNewFlow(boolean inbound, FluidStack providedFluid) {
if (providedFluid.isEmpty())
return false;
Flow flow = new Flow(inbound, providedFluid);
this.flow = Optional.of(flow);
return true;
}
private boolean determineSource(World world, BlockPos pos) {
if (!world.isAreaLoaded(pos, 1))
return false;
BlockFace location = new BlockFace(pos, side);
if (FluidPropagator.isOpenEnd(world, pos, side)) {
if (previousSource.orElse(null) instanceof OpenEndedPipe)
source = previousSource;
else
source = Optional.of(new OpenEndedPipe(location));
return true;
}
if (FluidPropagator.hasFluidCapability(world, location.getConnectedPos(), side.getOpposite())) {
source = Optional.of(new FlowSource.FluidHandler(location));
return true;
}
FluidTransportBehaviour behaviour =
TileEntityBehaviour.get(world, pos.offset(side), FluidTransportBehaviour.TYPE);
source = Optional.of(behaviour == null ? new FlowSource.Blocked(location) : new FlowSource.OtherPipe(location));
return true;
}
public void tickFlowProgress(World world, BlockPos pos) {
if (!hasFlow())
return;
Flow flow = this.flow.get();
if (flow.fluid.isEmpty())
return;
if (world.isRemote) {
if (!source.isPresent())
determineSource(world, pos);
spawnParticles(world, pos, flow.fluid);
if (particleSplashNextTick)
spawnSplashOnRim(world, pos, flow.fluid);
particleSplashNextTick = false;
}
float flowSpeed = 1 / 32f + MathHelper.clamp(pressure.get(flow.inbound) / 512f, 0, 1) * 31 / 32f;
flow.progress.setValue(Math.min(flow.progress.getValue() + flowSpeed, 1));
if (flow.progress.getValue() >= 1)
flow.complete = true;
}
public void serializeNBT(CompoundNBT tag, boolean clientPacket) {
CompoundNBT connectionData = new CompoundNBT();
tag.put(side.getName(), connectionData);
if (hasPressure()) {
ListNBT pressureData = new ListNBT();
pressureData.add(FloatNBT.of(getInboundPressure()));
pressureData.add(FloatNBT.of(getOutwardPressure()));
connectionData.put("Pressure", pressureData);
}
if (hasOpenEnd())
connectionData.put("OpenEnd", ((OpenEndedPipe) source.get()).serializeNBT());
if (hasFlow()) {
CompoundNBT flowData = new CompoundNBT();
Flow flow = this.flow.get();
flow.fluid.writeToNBT(flowData);
flowData.putBoolean("In", flow.inbound);
if (!flow.complete)
flowData.put("Progress", flow.progress.writeNBT());
connectionData.put("Flow", flowData);
}
}
private boolean hasOpenEnd() {
return source.orElse(null) instanceof OpenEndedPipe;
}
public void deserializeNBT(CompoundNBT tag, boolean clientPacket) {
CompoundNBT connectionData = tag.getCompound(side.getName());
if (connectionData.contains("Pressure")) {
ListNBT pressureData = connectionData.getList("Pressure", NBT.TAG_FLOAT);
pressure = Couple.create(pressureData.getFloat(0), pressureData.getFloat(1));
} else
pressure.replace(f -> 0f);
source = Optional.empty();
if (connectionData.contains("OpenEnd"))
source = Optional.of(OpenEndedPipe.fromNBT(connectionData.getCompound("OpenEnd")));
if (connectionData.contains("Flow")) {
CompoundNBT flowData = connectionData.getCompound("Flow");
FluidStack fluid = FluidStack.loadFluidStackFromNBT(flowData);
boolean inbound = flowData.getBoolean("In");
if (!flow.isPresent()) {
flow = Optional.of(new Flow(inbound, fluid));
if (clientPacket)
particleSplashNextTick = true;
}
Flow flow = this.flow.get();
flow.fluid = fluid;
flow.inbound = inbound;
flow.complete = !flowData.contains("Progress");
if (!flow.complete)
flow.progress.readNBT(flowData.getCompound("Progress"), clientPacket);
else {
if (flow.progress.getValue() == 0)
flow.progress.startWithValue(1);
flow.progress.setValue(1);
}
} else
flow = Optional.empty();
}
/**
* @return zero if outward == inbound <br>
* positive if outward > inbound <br>
* negative if outward < inbound
*/
public float comparePressure() {
return getOutwardPressure() - getInboundPressure();
}
public void wipePressure() {
this.pressure.replace(f -> 0f);
if (this.source.isPresent())
this.previousSource = this.source;
this.source = Optional.empty();
resetNetwork();
}
public FluidStack provideOutboundFlow() {
if (!hasFlow())
return FluidStack.EMPTY;
Flow flow = this.flow.get();
if (!flow.complete || flow.inbound)
return FluidStack.EMPTY;
return flow.fluid;
}
public void addPressure(boolean inbound, float pressure) {
this.pressure = this.pressure.mapWithContext((f, in) -> in == inbound ? f + pressure : f);
}
public boolean hasPressure() {
return getInboundPressure() != 0 || getOutwardPressure() != 0;
}
private float getOutwardPressure() {
return pressure.getSecond();
}
private float getInboundPressure() {
return pressure.getFirst();
}
public boolean hasFlow() {
return flow.isPresent();
}
public boolean hasNetwork() {
return network.isPresent();
}
public void resetNetwork() {
network.ifPresent(FluidNetwork::reset);
}
public class Flow {
public boolean complete;
public boolean inbound;
public LerpedFloat progress;
public FluidStack fluid;
public Flow(boolean inbound, FluidStack fluid) {
this.inbound = inbound;
this.fluid = fluid;
this.progress = LerpedFloat.linear()
.startWithValue(0);
this.complete = false;
}
}
public static final int MAX_PARTICLE_RENDER_DISTANCE = 20;
public static final int SPLASH_PARTICLE_AMOUNT = 1;
public static final float IDLE_PARTICLE_SPAWN_CHANCE = 1 / 1000f;
public static final float RIM_RADIUS = 1 / 4f + 1 / 64f;
public static final Random r = new Random();
public void spawnSplashOnRim(World world, BlockPos pos, FluidStack fluid) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnSplashOnRimInner(world, pos, fluid));
}
public void spawnParticles(World world, BlockPos pos, FluidStack fluid) {
DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> spawnParticlesInner(world, pos, fluid));
}
@OnlyIn(Dist.CLIENT)
private void spawnParticlesInner(World world, BlockPos pos, FluidStack fluid) {
if (!isRenderEntityWithinDistance(pos))
return;
if (hasOpenEnd())
spawnPouringLiquid(world, pos, fluid, 1);
else if (r.nextFloat() < IDLE_PARTICLE_SPAWN_CHANCE)
spawnRimParticles(world, pos, fluid, 1);
}
@OnlyIn(Dist.CLIENT)
private void spawnSplashOnRimInner(World world, BlockPos pos, FluidStack fluid) {
if (!isRenderEntityWithinDistance(pos))
return;
spawnRimParticles(world, pos, fluid, SPLASH_PARTICLE_AMOUNT);
}
@OnlyIn(Dist.CLIENT)
private void spawnRimParticles(World world, BlockPos pos, FluidStack fluid, int amount) {
if (hasOpenEnd()) {
spawnPouringLiquid(world, pos, fluid, amount);
return;
}
IParticleData particle = FluidFX.getDrippingParticle(fluid);
FluidFX.spawnRimParticles(world, pos, side, amount, particle, RIM_RADIUS);
}
@OnlyIn(Dist.CLIENT)
private void spawnPouringLiquid(World world, BlockPos pos, FluidStack fluid, int amount) {
IParticleData particle = FluidFX.getFluidParticle(fluid);
Vec3d directionVec = new Vec3d(side.getDirectionVec());
if (!hasFlow())
return;
Flow flow = this.flow.get();
FluidFX.spawnPouringLiquid(world, pos, amount, particle, RIM_RADIUS, directionVec, flow.inbound);
}
@OnlyIn(Dist.CLIENT)
public static boolean isRenderEntityWithinDistance(BlockPos pos) {
Entity renderViewEntity = Minecraft.getInstance()
.getRenderViewEntity();
if (renderViewEntity == null)
return false;
Vec3d center = VecHelper.getCenterOf(pos);
if (renderViewEntity.getPositionVec()
.distanceTo(center) > MAX_PARTICLE_RENDER_DISTANCE)
return false;
return true;
}
// void visualizePressure(BlockPos pos) {
// if (!hasPressure())
// return;
//
// pressure.forEachWithContext((pressure, inbound) -> {
// if (inbound)
// return;
//
// Vec3d directionVec = new Vec3d(side.getDirectionVec());
// Vec3d scaleVec = directionVec.scale(-.25f * side.getAxisDirection()
// .getOffset());
// directionVec = directionVec.scale(inbound ? .35f : .45f);
// CreateClient.outliner.chaseAABB("pressure" + pos.toShortString() + side.getName() + String.valueOf(inbound),
// FluidPropagator.smallCenter.offset(directionVec.add(new Vec3d(pos)))
// .grow(scaleVec.x, scaleVec.y, scaleVec.z)
// .expand(0, pressure / 64f, 0)
// .grow(1 / 64f));
// });
// }
//
// void visualizeFlow(BlockPos pos) {
// if (!hasFlow())
// return;
//
// Vec3d directionVec = new Vec3d(side.getDirectionVec());
// float size = 1 / 4f;
// float length = .5f;
// Flow flow = this.flow.get();
// boolean inbound = flow.inbound;
// FluidStack fluid = flow.fluid;
//
// if (flow.progress == null)
// return;
// float value = flow.progress.getValue();
// Vec3d start = directionVec.scale(inbound ? .5 : .5f - length);
// Vec3d offset = directionVec.scale(length * (inbound ? -1 : 1))
// .scale(value);
//
// Vec3d scale = new Vec3d(1, 1, 1).subtract(directionVec.scale(side.getAxisDirection()
// .getOffset()))
// .scale(size);
// AxisAlignedBB bb = new AxisAlignedBB(start, start.add(offset)).offset(VecHelper.getCenterOf(pos))
// .grow(scale.x, scale.y, scale.z);
//
// int color = 0x7fdbda;
// if (!fluid.isEmpty()) {
// Fluid fluid2 = fluid.getFluid();
// if (fluid2 == Fluids.WATER)
// color = 0x1D4D9B;
// else if (fluid2 == Fluids.LAVA)
// color = 0xFF773D;
// else
// color = fluid2.getAttributes()
// .getColor(fluid);
// }
//
// CreateClient.outliner.chaseAABB(this, bb)
// .withFaceTexture(AllSpecialTextures.SELECTION)
// .colored(color)
// .lineWidth(0);
// }
}

View file

@ -1,15 +1,12 @@
package com.simibubi.create.content.contraptions.fluids; package com.simibubi.create.content.contraptions.fluids;
import java.util.Map; import java.util.Random;
import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableBoolean;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock; import com.simibubi.create.content.contraptions.base.DirectionalKineticBlock;
import com.simibubi.create.content.contraptions.fluids.pipes.FluidPipeBlock;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
@ -30,8 +27,9 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld; import net.minecraft.world.IWorld;
import net.minecraft.world.IWorldReader; import net.minecraft.world.IWorldReader;
import net.minecraft.world.TickPriority;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraftforge.fluids.FluidStack; import net.minecraft.world.server.ServerWorld;
public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable { public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable {
@ -67,26 +65,8 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable
if (!(tileEntity instanceof PumpTileEntity)) if (!(tileEntity instanceof PumpTileEntity))
return state; return state;
PumpTileEntity pump = (PumpTileEntity) tileEntity; PumpTileEntity pump = (PumpTileEntity) tileEntity;
if (pump.networks == null) pump.sidesToUpdate.forEach(MutableBoolean::setTrue);
return state;
FluidNetwork apn1 = pump.networks.get(true);
FluidNetwork apn2 = pump.networks.get(false);
// Collect pipes that can be skipped
apn1.clearFlows(world, true);
apn2.clearFlows(world, true);
// Swap skipsets as the networks change sides
Map<BlockFace, FluidStack> skippedConnections = apn1.previousFlow;
apn1.previousFlow = apn2.previousFlow;
apn2.previousFlow = skippedConnections;
// Init networks next tick
pump.networksToUpdate.forEach(MutableBoolean::setTrue);
pump.networks.swap();
pump.reversed = !pump.reversed; pump.reversed = !pump.reversed;
return state; return state;
} }
@ -111,22 +91,29 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable
public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos, public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos,
boolean isMoving) { boolean isMoving) {
DebugPacketSender.func_218806_a(world, pos); DebugPacketSender.func_218806_a(world, pos);
if (world.isRemote) Direction d = FluidPropagator.validateNeighbourChange(state, world, pos, otherBlock, neighborPos, isMoving);
if (d == null)
return; return;
if (otherBlock instanceof FluidPipeBlock) if (!isOpenAt(state, d))
return; return;
TileEntity tileEntity = world.getTileEntity(pos); world.getPendingBlockTicks()
if (!(tileEntity instanceof PumpTileEntity)) .scheduleTick(pos, this, 1, TickPriority.HIGH);
return; // if (world.isRemote)
PumpTileEntity pump = (PumpTileEntity) tileEntity; // return;
Direction facing = state.get(FACING); // if (otherBlock instanceof FluidPipeBlock)
for (boolean front : Iterate.trueAndFalse) { // return;
Direction side = front ? facing : facing.getOpposite(); // TileEntity tileEntity = world.getTileEntity(pos);
if (!pos.offset(side) // if (!(tileEntity instanceof PumpTileEntity))
.equals(neighborPos)) // return;
continue; // PumpTileEntity pump = (PumpTileEntity) tileEntity;
pump.updatePipesOnSide(side); // Direction facing = state.get(FACING);
} // for (boolean front : Iterate.trueAndFalse) {
// Direction side = front ? facing : facing.getOpposite();
// if (!pos.offset(side)
// .equals(neighborPos))
// continue;
// pump.updatePipesOnSide(side);
// }
} }
@Override @Override
@ -163,4 +150,32 @@ public class PumpBlock extends DirectionalKineticBlock implements IWaterLoggable
return state.getBlock() instanceof PumpBlock; return state.getBlock() instanceof PumpBlock;
} }
@Override
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean isMoving) {
if (world.isRemote)
return;
if (state != oldState)
world.getPendingBlockTicks()
.scheduleTick(pos, this, 1, TickPriority.HIGH);
}
public static boolean isOpenAt(BlockState state, Direction d) {
return d.getAxis() == state.get(FACING)
.getAxis();
}
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) {
FluidPropagator.propagateChangedPipe(world, pos, state);
}
@Override
public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) {
boolean blockTypeChanged = state.getBlock() != newState.getBlock();
if (blockTypeChanged && !world.isRemote)
FluidPropagator.propagateChangedPipe(world, pos, state);
if (state.hasTileEntity() && (blockTypeChanged || !newState.hasTileEntity()))
world.removeTileEntity(pos);
}
} }

View file

@ -1,26 +0,0 @@
package com.simibubi.create.content.contraptions.fluids;
import com.simibubi.create.foundation.utility.BlockFace;
import net.minecraft.world.IWorld;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.fluids.FluidStack;
public class PumpEndpoint extends FluidNetworkEndpoint {
PumpTileEntity pumpTE;
public PumpEndpoint(BlockFace location, PumpTileEntity pumpTE) {
super(pumpTE.getWorld(), location, LazyOptional.empty());
this.pumpTE = pumpTE;
}
@Override
protected void onHandlerInvalidated(IWorld world) {}
@Override
public FluidStack provideFluid() {
return pumpTE.providedFluid;
}
}

View file

@ -1,11 +1,13 @@
package com.simibubi.create.content.contraptions.fluids; package com.simibubi.create.content.contraptions.fluids;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -19,48 +21,37 @@ import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.LerpedFloat; import com.simibubi.create.foundation.utility.LerpedFloat;
import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; import com.simibubi.create.foundation.utility.LerpedFloat.Chaser;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT; import net.minecraft.tileentity.TileEntity;
import net.minecraft.particles.IParticleData;
import net.minecraft.tileentity.TileEntityType; import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.IBlockDisplayReader; import net.minecraft.world.IBlockDisplayReader;
import net.minecraftforge.api.distmarker.Dist; import net.minecraft.world.IWorld;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.common.util.Constants.NBT; import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler; import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.fml.DistExecutor;
public class PumpTileEntity extends KineticTileEntity { public class PumpTileEntity extends KineticTileEntity {
LerpedFloat arrowDirection; LerpedFloat arrowDirection;
Couple<FluidNetwork> networks; Couple<MutableBoolean> sidesToUpdate;
Couple<Map<BlockFace, OpenEndedPipe>> openEnds;
Couple<MutableBoolean> networksToUpdate;
boolean reversed; boolean reversed;
FluidStack providedFluid;
public PumpTileEntity(TileEntityType<?> typeIn) { public PumpTileEntity(TileEntityType<?> typeIn) {
super(typeIn); super(typeIn);
arrowDirection = LerpedFloat.linear() arrowDirection = LerpedFloat.linear()
.startWithValue(1); .startWithValue(1);
networksToUpdate = Couple.create(MutableBoolean::new); sidesToUpdate = Couple.create(MutableBoolean::new);
openEnds = Couple.create(HashMap::new);
setProvidedFluid(FluidStack.EMPTY);
} }
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
super.addBehaviours(behaviours); super.addBehaviours(behaviours);
behaviours.add(new PumpAttachmentBehaviour(this)); behaviours.add(new PumpFluidTransferBehaviour(this));
} }
@Override @Override
@ -77,196 +68,235 @@ public class PumpTileEntity extends KineticTileEntity {
if (world.isRemote) { if (world.isRemote) {
if (speed == 0) if (speed == 0)
return; return;
spawnParticles();
arrowDirection.chase(speed >= 0 ? 1 : -1, .5f, Chaser.EXP); arrowDirection.chase(speed >= 0 ? 1 : -1, .5f, Chaser.EXP);
arrowDirection.tickChaser(); arrowDirection.tickChaser();
return; return;
} }
BlockState blockState = getBlockState(); sidesToUpdate.forEachWithContext((update, isFront) -> {
if (!(blockState.getBlock() instanceof PumpBlock))
return;
Direction face = blockState.get(PumpBlock.FACING);
MutableBoolean networkUpdated = new MutableBoolean(false);
if (networks == null) {
networks = Couple.create(new FluidNetwork(), new FluidNetwork());
networks.forEachWithContext((fn, front) -> {
BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite());
fn.assemble(world, this, blockFace);
FluidPropagator.showBlockFace(blockFace)
.lineWidth(1 / 8f);
});
networkUpdated.setTrue();
}
networksToUpdate.forEachWithContext((update, front) -> {
if (update.isFalse()) if (update.isFalse())
return; return;
FluidNetwork activePipeNetwork = networks.get(front);
if (activePipeNetwork == null)
return;
BlockFace blockFace = new BlockFace(pos, front ? face : face.getOpposite());
activePipeNetwork.reAssemble(world, this, blockFace);
FluidPropagator.showBlockFace(blockFace)
.lineWidth(1 / 8f);
update.setFalse(); update.setFalse();
networkUpdated.setTrue(); distributePressureTo(isFront ? getFront() : getFront().getOpposite());
}); });
if (networkUpdated.isTrue())
return;
networks.forEach(fn -> fn.tick(world, this));
if (speed == 0) if (speed == 0)
return; return;
if (speed < 0 != reversed) { if (speed < 0 != reversed) {
networks.forEachWithContext((fn, current) -> fn.clearFlows(world, true));
reversed = speed < 0; reversed = speed < 0;
return; return;
} }
boolean pullingSide = isPullingOnSide(true);
float flowSpeed = Math.abs(speed) / 256f;
networks.forEachWithContext((fn, front) -> {
boolean pulling = isPullingOnSide(front);
fn.tickFlows(world, this, pulling, flowSpeed);
openEnds.get(front)
.values()
.forEach(oep -> oep.tick(world, pulling));
});
if (!networks.get(pullingSide)
.hasEndpoints()) {
setProvidedFluid(FluidStack.EMPTY);
return;
}
if (networks.getFirst()
.hasEndpoints()
&& networks.getSecond()
.hasEndpoints()) {
performTransfer();
}
} }
@Override @Override
public void remove() { public void onSpeedChanged(float previousSpeed) {
super.remove(); super.onSpeedChanged(previousSpeed);
if (networks != null)
networks.forEachWithContext((fn, current) -> fn.clearFlows(world, false)); if (previousSpeed == getSpeed())
return;
if (speed != 0)
reversed = speed < 0;
if (world.isRemote)
return;
BlockPos frontPos = pos.offset(getFront());
BlockPos backPos = pos.offset(getFront().getOpposite());
FluidPropagator.propagateChangedPipe(world, frontPos, world.getBlockState(frontPos));
FluidPropagator.propagateChangedPipe(world, backPos, world.getBlockState(backPos));
} }
private void performTransfer() { protected void distributePressureTo(Direction side) {
boolean input = isPullingOnSide(true); if (getSpeed() == 0)
Collection<FluidNetworkEndpoint> inputs = networks.get(input) return;
.getEndpoints(true);
Collection<FluidNetworkEndpoint> outputs = networks.get(!input)
.getEndpoints(false);
int flowSpeed = getFluidTransferSpeed(); BlockFace start = new BlockFace(pos, side);
FluidStack transfer = FluidStack.EMPTY; boolean pull = isPullingOnSide(isFront(side));
for (boolean simulate : Iterate.trueAndFalse) { Set<BlockFace> targets = new HashSet<>();
FluidAction action = simulate ? FluidAction.SIMULATE : FluidAction.EXECUTE; Map<BlockPos, Pair<Integer, Map<Direction, Boolean>>> pipeGraph = new HashMap<>();
List<FluidNetworkEndpoint> availableInputs = new ArrayList<>(inputs); if (!pull)
while (!availableInputs.isEmpty() && transfer.getAmount() < flowSpeed) { FluidPropagator.resetAffectedFluidNetworks(world, pos, side.getOpposite());
int diff = flowSpeed - transfer.getAmount();
int dividedTransfer = diff / availableInputs.size();
int remainder = diff % availableInputs.size();
for (Iterator<FluidNetworkEndpoint> iterator = availableInputs.iterator(); iterator.hasNext();) { if (!hasReachedValidEndpoint(world, start, pull)) {
int toTransfer = dividedTransfer;
if (remainder > 0) {
toTransfer++;
remainder--;
}
FluidNetworkEndpoint ne = iterator.next(); pipeGraph.computeIfAbsent(pos, $ -> Pair.of(0, new IdentityHashMap<>()))
IFluidHandler handler = ne.provideHandler() .getSecond()
.orElse(null); .put(side, pull);
if (handler == null) { pipeGraph.computeIfAbsent(start.getConnectedPos(), $ -> Pair.of(1, new IdentityHashMap<>()))
iterator.remove(); .getSecond()
.put(side.getOpposite(), !pull);
List<Pair<Integer, BlockPos>> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>();
int maxDistance = FluidPropagator.getPumpRange();
frontier.add(Pair.of(1, start.getConnectedPos()));
while (!frontier.isEmpty()) {
Pair<Integer, BlockPos> entry = frontier.remove(0);
int distance = entry.getFirst();
BlockPos currentPos = entry.getSecond();
if (!world.isAreaLoaded(currentPos, 0))
continue; continue;
} if (visited.contains(currentPos))
FluidStack drained = handler.drain(toTransfer, action);
if (drained.isEmpty()) {
iterator.remove();
continue; continue;
} visited.add(currentPos);
if (transfer.isFluidEqual(drained) || transfer.isEmpty()) { BlockState currentState = world.getBlockState(currentPos);
if (drained.getAmount() < toTransfer) FluidTransportBehaviour pipe = FluidPropagator.getPipe(world, currentPos);
iterator.remove(); if (pipe == null)
FluidStack copy = drained.copy();
copy.setAmount(drained.getAmount() + transfer.getAmount());
transfer = copy;
continue; continue;
}
iterator.remove(); for (Direction face : FluidPropagator.getPipeConnections(currentState, pipe)) {
BlockFace blockFace = new BlockFace(currentPos, face);
BlockPos connectedPos = blockFace.getConnectedPos();
if (!world.isAreaLoaded(connectedPos, 0))
continue;
if (blockFace.isEquivalent(start))
continue;
if (hasReachedValidEndpoint(world, blockFace, pull)) {
pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>()))
.getSecond()
.put(face, pull);
targets.add(blockFace);
continue; continue;
} }
} FluidTransportBehaviour pipeBehaviour = FluidPropagator.getPipe(world, connectedPos);
if (pipeBehaviour == null)
List<FluidNetworkEndpoint> availableOutputs = new ArrayList<>(outputs); continue;
while (!availableOutputs.isEmpty() && transfer.getAmount() > 0) { if (pipeBehaviour instanceof PumpFluidTransferBehaviour)
int dividedTransfer = transfer.getAmount() / availableOutputs.size(); continue;
int remainder = transfer.getAmount() % availableOutputs.size(); if (visited.contains(connectedPos))
continue;
for (Iterator<FluidNetworkEndpoint> iterator = availableOutputs.iterator(); iterator.hasNext();) { if (distance + 1 >= maxDistance) {
FluidNetworkEndpoint ne = iterator.next(); pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>()))
int toTransfer = dividedTransfer; .getSecond()
if (remainder > 0) { .put(face, pull);
toTransfer++; targets.add(blockFace);
remainder--;
}
if (transfer.isEmpty())
break;
IFluidHandler handler = ne.provideHandler()
.orElse(null);
if (handler == null) {
iterator.remove();
continue; continue;
} }
FluidStack divided = transfer.copy(); pipeGraph.computeIfAbsent(currentPos, $ -> Pair.of(distance, new IdentityHashMap<>()))
divided.setAmount(toTransfer); .getSecond()
int fill = handler.fill(divided, action); .put(face, pull);
transfer.setAmount(transfer.getAmount() - fill); pipeGraph.computeIfAbsent(connectedPos, $ -> Pair.of(distance + 1, new IdentityHashMap<>()))
if (fill < toTransfer) .getSecond()
iterator.remove(); .put(face.getOpposite(), !pull);
frontier.add(Pair.of(distance + 1, connectedPos));
}
}
}
// DFS
Map<Integer, Set<BlockFace>> validFaces = new HashMap<>();
searchForEndpointRecursively(pipeGraph, targets, validFaces,
new BlockFace(start.getPos(), start.getOppositeFace()), pull);
float pressure = Math.abs(getSpeed());
for (Set<BlockFace> set : validFaces.values()) {
int parallelBranches = set.size();
for (BlockFace face : set) {
BlockPos pipePos = face.getPos();
Direction pipeSide = face.getFace();
if (pipePos.equals(pos))
continue;
boolean inbound = pipeGraph.get(pipePos)
.getSecond()
.get(pipeSide);
FluidTransportBehaviour pipeBehaviour = FluidPropagator.getPipe(world, pipePos);
if (pipeBehaviour == null)
continue;
pipeBehaviour.addPressure(pipeSide, inbound, pressure / parallelBranches);
}
} }
} }
flowSpeed -= transfer.getAmount(); protected boolean searchForEndpointRecursively(Map<BlockPos, Pair<Integer, Map<Direction, Boolean>>> pipeGraph,
transfer = FluidStack.EMPTY; Set<BlockFace> targets, Map<Integer, Set<BlockFace>> validFaces, BlockFace currentFace, boolean pull) {
} BlockPos currentPos = currentFace.getPos();
if (!pipeGraph.containsKey(currentPos))
return false;
Pair<Integer, Map<Direction, Boolean>> pair = pipeGraph.get(currentPos);
int distance = pair.getFirst();
boolean atLeastOneBranchSuccessful = false;
for (Direction nextFacing : Iterate.directions) {
if (nextFacing == currentFace.getFace())
continue;
Map<Direction, Boolean> map = pair.getSecond();
if (!map.containsKey(nextFacing))
continue;
BlockFace localTarget = new BlockFace(currentPos, nextFacing);
if (targets.contains(localTarget)) {
validFaces.computeIfAbsent(distance, $ -> new HashSet<>())
.add(localTarget);
atLeastOneBranchSuccessful = true;
continue;
} }
public int getFluidTransferSpeed() { if (map.get(nextFacing) != pull)
float rotationSpeed = Math.abs(getSpeed()); continue;
int flowSpeed = (int) (rotationSpeed / 2f); if (!searchForEndpointRecursively(pipeGraph, targets, validFaces,
if (rotationSpeed != 0 && flowSpeed == 0) new BlockFace(currentPos.offset(nextFacing), nextFacing.getOpposite()), pull))
flowSpeed = 1; continue;
return flowSpeed;
validFaces.computeIfAbsent(distance, $ -> new HashSet<>())
.add(localTarget);
atLeastOneBranchSuccessful = true;
}
if (atLeastOneBranchSuccessful)
validFaces.computeIfAbsent(distance, $ -> new HashSet<>())
.add(currentFace);
return atLeastOneBranchSuccessful;
}
private boolean hasReachedValidEndpoint(IWorld world, BlockFace blockFace, boolean pull) {
BlockPos connectedPos = blockFace.getConnectedPos();
BlockState connectedState = world.getBlockState(connectedPos);
TileEntity tileEntity = world.getTileEntity(connectedPos);
Direction face = blockFace.getFace();
// facing a pump
if (PumpBlock.isPump(connectedState) && connectedState.get(PumpBlock.FACING)
.getAxis() == face.getAxis() && tileEntity instanceof PumpTileEntity) {
PumpTileEntity pumpTE = (PumpTileEntity) tileEntity;
return pumpTE.isPullingOnSide(pumpTE.isFront(blockFace.getOppositeFace())) != pull;
}
// other pipe, no endpoint
FluidTransportBehaviour pipe = FluidPropagator.getPipe(world, connectedPos);
if (pipe != null && pipe.canHaveFlowToward(connectedState, blockFace.getOppositeFace()))
return false;
// fluid handler endpoint
if (tileEntity != null) {
LazyOptional<IFluidHandler> capability =
tileEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, face.getOpposite());
if (capability.isPresent())
return true;
}
// open endpoint
return FluidPropagator.isOpenEnd(world, blockFace.getPos(), face);
} }
@Override @Override
public void write(CompoundNBT compound, boolean clientPacket) { public void write(CompoundNBT compound, boolean clientPacket) {
compound.putBoolean("Reversed", reversed); compound.putBoolean("Reversed", reversed);
serializeOpenEnds(compound);
super.write(compound, clientPacket); super.write(compound, clientPacket);
} }
@Override @Override
protected void fromTag(BlockState state, CompoundNBT compound, boolean clientPacket) { protected void fromTag(BlockState state, CompoundNBT compound, boolean clientPacket) {
reversed = compound.getBoolean("Reversed"); reversed = compound.getBoolean("Reversed");
deserializeOpenEnds(compound);
super.fromTag(state, compound, clientPacket); super.fromTag(state, compound, clientPacket);
} }
@ -274,11 +304,10 @@ public class PumpTileEntity extends KineticTileEntity {
if (!isSideAccessible(side)) if (!isSideAccessible(side))
return; return;
updatePipeNetwork(isFront(side)); updatePipeNetwork(isFront(side));
getBehaviour(FluidTransportBehaviour.TYPE).wipePressure();
} }
protected boolean isFront(Direction side) { protected boolean isFront(Direction side) {
if (networks == null)
return false;
BlockState blockState = getBlockState(); BlockState blockState = getBlockState();
if (!(blockState.getBlock() instanceof PumpBlock)) if (!(blockState.getBlock() instanceof PumpBlock))
return false; return false;
@ -296,13 +325,8 @@ public class PumpTileEntity extends KineticTileEntity {
} }
protected void updatePipeNetwork(boolean front) { protected void updatePipeNetwork(boolean front) {
if (networks != null) sidesToUpdate.get(front)
networks.get(front)
.clearFlows(world, true);
networksToUpdate.get(front)
.setTrue(); .setTrue();
if (getSpeed() == 0 || (isPullingOnSide(front)) && networks != null)
setProvidedFluid(FluidStack.EMPTY);
} }
public boolean isSideAccessible(Direction side) { public boolean isSideAccessible(Direction side) {
@ -317,113 +341,32 @@ public class PumpTileEntity extends KineticTileEntity {
return front == reversed; return front == reversed;
} }
public void spawnParticles() { class PumpFluidTransferBehaviour extends FluidTransportBehaviour {
DistExecutor.runWhenOn(Dist.CLIENT, () -> this::spawnParticlesInner);
}
@OnlyIn(Dist.CLIENT) public PumpFluidTransferBehaviour(SmartTileEntity te) {
private void spawnParticlesInner() {
if (!FluidPipeBehaviour.isRenderEntityWithinDistance(pos))
return;
for (boolean front : Iterate.trueAndFalse) {
Direction side = getFront();
if (side == null)
return;
if (!front)
side = side.getOpposite();
if (!FluidPropagator.isOpenEnd(world, pos, side))
continue;
BlockFace key = new BlockFace(pos, side);
Map<BlockFace, OpenEndedPipe> map = openEnds.get(front);
if (map.containsKey(key)) {
FluidStack fluidStack = map.get(key)
.getCapability()
.map(fh -> fh.getFluidInTank(0))
.orElse(FluidStack.EMPTY);
if (!fluidStack.isEmpty())
spawnPouringLiquid(fluidStack, side, 1);
}
}
}
@OnlyIn(Dist.CLIENT)
private void spawnPouringLiquid(FluidStack fluid, Direction side, int amount) {
IParticleData particle = FluidFX.getFluidParticle(fluid);
float rimRadius = 1 / 4f + 1 / 64f;
boolean inbound = isPullingOnSide(getFront() == side);
Vector3d directionVec = Vector3d.of(side.getDirectionVec());
FluidFX.spawnPouringLiquid(world, pos, amount, particle, rimRadius, directionVec, inbound);
}
public Map<BlockFace, OpenEndedPipe> getOpenEnds(Direction side) {
return openEnds.get(isFront(side));
}
private void serializeOpenEnds(CompoundNBT compound) {
compound.put("OpenEnds", openEnds.serializeEach(m -> {
CompoundNBT compoundNBT = new CompoundNBT();
ListNBT entries = new ListNBT();
m.entrySet()
.forEach(e -> {
CompoundNBT innerCompound = new CompoundNBT();
innerCompound.put("Pos", e.getKey()
.serializeNBT());
e.getValue()
.writeToNBT(innerCompound);
entries.add(innerCompound);
});
compoundNBT.put("Entries", entries);
return compoundNBT;
}));
}
private void deserializeOpenEnds(CompoundNBT compound) {
openEnds = Couple.deserializeEach(compound.getList("OpenEnds", NBT.TAG_COMPOUND), c -> {
Map<BlockFace, OpenEndedPipe> map = new HashMap<>();
NBTHelper.iterateCompoundList(c.getList("Entries", NBT.TAG_COMPOUND), innerCompound -> {
BlockFace key = BlockFace.fromNBT(innerCompound.getCompound("Pos"));
OpenEndedPipe value = new OpenEndedPipe(key);
value.readNBT(innerCompound);
map.put(key, value);
});
return map;
});
compound.put("OpenEnds", openEnds.serializeEach(m -> {
CompoundNBT compoundNBT = new CompoundNBT();
ListNBT entries = new ListNBT();
m.entrySet()
.forEach(e -> {
CompoundNBT innerCompound = new CompoundNBT();
innerCompound.put("Pos", e.getKey()
.serializeNBT());
e.getValue()
.writeToNBT(innerCompound);
entries.add(innerCompound);
});
compoundNBT.put("Entries", entries);
return compoundNBT;
}));
}
public void setProvidedFluid(FluidStack providedFluid) {
this.providedFluid = providedFluid;
}
class PumpAttachmentBehaviour extends FluidPipeAttachmentBehaviour {
public PumpAttachmentBehaviour(SmartTileEntity te) {
super(te); super(te);
} }
@Override @Override
public boolean isPipeConnectedTowards(BlockState state, Direction direction) { public void tick() {
super.tick();
for (Entry<Direction, PipeConnection> entry : interfaces.entrySet()) {
boolean pull = isPullingOnSide(isFront(entry.getKey()));
Couple<Float> pressure = entry.getValue().pressure;
pressure.set(pull, Math.abs(getSpeed()));
pressure.set(!pull, 0f);
}
}
@Override
public boolean canHaveFlowToward(BlockState state, Direction direction) {
return isSideAccessible(direction); return isSideAccessible(direction);
} }
@Override @Override
public AttachmentTypes getAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state, Direction direction) { public AttachmentTypes getRenderedRimAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state,
AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); Direction direction) {
AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction);
if (attachment == AttachmentTypes.RIM) if (attachment == AttachmentTypes.RIM)
return AttachmentTypes.NONE; return AttachmentTypes.NONE;
return attachment; return attachment;

View file

@ -6,8 +6,8 @@ import java.util.Random;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket; import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -22,9 +22,9 @@ import net.minecraft.network.DebugPacketSender;
import net.minecraft.state.BooleanProperty; import net.minecraft.state.BooleanProperty;
import net.minecraft.util.ActionResultType; import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.Axis;
import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.Direction.AxisDirection;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult;
@ -126,7 +126,7 @@ public class AxisPipeBlock extends RotatedPillarBlock implements IWrenchableWith
@Override @Override
public Optional<ItemStack> removeBracket(IBlockReader world, BlockPos pos) { public Optional<ItemStack> removeBracket(IBlockReader world, BlockPos pos) {
FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour == null) if (behaviour == null)
return Optional.empty(); return Optional.empty();
BlockState bracket = behaviour.getBracket(); BlockState bracket = behaviour.getBracket();

View file

@ -2,7 +2,6 @@ package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.Optional; import java.util.Optional;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -32,11 +31,12 @@ public class BracketBlockItem extends BlockItem {
BracketBlock bracketBlock = getBracketBlock(); BracketBlock bracketBlock = getBracketBlock();
PlayerEntity player = context.getPlayer(); PlayerEntity player = context.getPlayer();
BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour == null)
behaviour = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour != null && behaviour.canHaveBracket()) { if (behaviour == null)
return ActionResultType.FAIL;
if (!behaviour.canHaveBracket())
return ActionResultType.FAIL;
if (world.isRemote) if (world.isRemote)
return ActionResultType.SUCCESS; return ActionResultType.SUCCESS;
@ -63,9 +63,6 @@ public class BracketBlockItem extends BlockItem {
return ActionResultType.SUCCESS; return ActionResultType.SUCCESS;
} }
return ActionResultType.FAIL;
}
private BracketBlock getBracketBlock() { private BracketBlock getBracketBlock() {
return (BracketBlock) getBlock(); return (BracketBlock) getBlock();
} }

View file

@ -7,8 +7,9 @@ import javax.annotation.Nullable;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket; import com.simibubi.create.content.contraptions.wrench.IWrenchableWithBracket;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -136,18 +137,17 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren
return state.getBlock() instanceof FluidPipeBlock; return state.getBlock() instanceof FluidPipeBlock;
} }
public static boolean canConnectTo(IBlockDisplayReader world, BlockPos pos, BlockState neighbour, public static boolean canConnectTo(IBlockDisplayReader world, BlockPos neighbourPos, BlockState neighbour, Direction direction) {
Direction blockFace) { if (FluidPropagator.hasFluidCapability(world, neighbourPos, direction.getOpposite()))
if (FluidPropagator.hasFluidCapability(neighbour, world, pos, blockFace))
return true; return true;
FluidPipeAttachmentBehaviour attachmentBehaviour = FluidTransportBehaviour transport = TileEntityBehaviour.get(world, neighbourPos, FluidTransportBehaviour.TYPE);
TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, neighbourPos, BracketedTileEntityBehaviour.TYPE);
if (isPipe(neighbour)) if (isPipe(neighbour))
return attachmentBehaviour == null || attachmentBehaviour.getBracket() == Blocks.AIR.getDefaultState() return bracket == null || !bracket.isBacketPresent()
|| FluidPropagator.getStraightPipeAxis(neighbour) == blockFace.getAxis(); || FluidPropagator.getStraightPipeAxis(neighbour) == direction.getAxis();
if (attachmentBehaviour == null) if (transport == null)
return false; return false;
return attachmentBehaviour.isPipeConnectedTowards(neighbour, blockFace.getOpposite()); return transport.canHaveFlowToward(neighbour, direction.getOpposite());
} }
public static boolean shouldDrawRim(IBlockDisplayReader world, BlockPos pos, BlockState state, public static boolean shouldDrawRim(IBlockDisplayReader world, BlockPos pos, BlockState state,
@ -222,8 +222,8 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren
public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore, public BlockState updateBlockState(BlockState state, Direction preferredDirection, @Nullable Direction ignore,
IBlockDisplayReader world, BlockPos pos) { IBlockDisplayReader world, BlockPos pos) {
FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour bracket = TileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour != null && behaviour.getBracket() != Blocks.AIR.getDefaultState()) if (bracket != null && bracket.isBacketPresent())
return state; return state;
// Update sides that are not ignored // Update sides that are not ignored
@ -260,7 +260,8 @@ public class FluidPipeBlock extends SixWayBlock implements IWaterLoggable, IWren
@Override @Override
public Optional<ItemStack> removeBracket(IBlockReader world, BlockPos pos) { public Optional<ItemStack> removeBracket(IBlockReader world, BlockPos pos) {
FluidPipeAttachmentBehaviour behaviour = TileEntityBehaviour.get(world, pos, FluidPipeAttachmentBehaviour.TYPE); BracketedTileEntityBehaviour behaviour =
BracketedTileEntityBehaviour.get(world, pos, BracketedTileEntityBehaviour.TYPE);
if (behaviour == null) if (behaviour == null)
return Optional.empty(); return Optional.empty();
BlockState bracket = behaviour.getBracket(); BlockState bracket = behaviour.getBracket();

View file

@ -3,8 +3,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.List; import java.util.List;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -22,43 +22,40 @@ public class FluidPipeTileEntity extends SmartTileEntity {
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new StandardPipeBehaviour(this)); behaviours.add(new StandardPipeFluidTransportBehaviour(this));
behaviours.add(new StandardPipeAttachmentBehaviour(this)); behaviours.add(new BracketedTileEntityBehaviour(this, this::canHaveBracket));
} }
class StandardPipeBehaviour extends FluidPipeBehaviour { private boolean canHaveBracket(BlockState state) {
return !(state.getBlock() instanceof EncasedPipeBlock);
}
public StandardPipeBehaviour(SmartTileEntity te) { class StandardPipeFluidTransportBehaviour extends FluidTransportBehaviour {
public StandardPipeFluidTransportBehaviour(SmartTileEntity te) {
super(te); super(te);
} }
@Override @Override
public boolean isConnectedTo(BlockState state, Direction direction) { public boolean canHaveFlowToward(BlockState state, Direction direction) {
return (FluidPipeBlock.isPipe(state) || state.getBlock() instanceof EncasedPipeBlock) return (FluidPipeBlock.isPipe(state) || state.getBlock() instanceof EncasedPipeBlock)
&& state.get(FluidPipeBlock.FACING_TO_PROPERTY_MAP.get(direction)); && state.get(FluidPipeBlock.FACING_TO_PROPERTY_MAP.get(direction));
} }
}
class StandardPipeAttachmentBehaviour extends FluidPipeAttachmentBehaviour {
public StandardPipeAttachmentBehaviour(SmartTileEntity te) {
super(te);
}
@Override @Override
public AttachmentTypes getAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state, Direction direction) { public AttachmentTypes getRenderedRimAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state,
AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); Direction direction) {
AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction);
if (attachment == AttachmentTypes.RIM && AllBlocks.ENCASED_FLUID_PIPE.has(state)) if (attachment == AttachmentTypes.RIM && AllBlocks.ENCASED_FLUID_PIPE.has(state))
return AttachmentTypes.RIM; return AttachmentTypes.RIM;
BlockPos offsetPos = pos.offset(direction); BlockPos offsetPos = pos.offset(direction);
if (!FluidPipeBlock.isPipe(world.getBlockState(offsetPos))) { if (!FluidPipeBlock.isPipe(world.getBlockState(offsetPos))) {
FluidPipeAttachmentBehaviour attachmentBehaviour = FluidTransportBehaviour pipeBehaviour =
TileEntityBehaviour.get(world, offsetPos, FluidPipeAttachmentBehaviour.TYPE); TileEntityBehaviour.get(world, offsetPos, FluidTransportBehaviour.TYPE);
if (attachmentBehaviour != null && attachmentBehaviour if (pipeBehaviour != null
.isPipeConnectedTowards(world.getBlockState(offsetPos), direction.getOpposite())) && pipeBehaviour.canHaveFlowToward(world.getBlockState(offsetPos), direction.getOpposite()))
return AttachmentTypes.NONE; return AttachmentTypes.NONE;
} }

View file

@ -1,12 +1,16 @@
package com.simibubi.create.content.contraptions.fluids.pipes; package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.Random;
import com.simibubi.create.AllShapes; import com.simibubi.create.AllShapes;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.base.DirectionalAxisKineticBlock; import com.simibubi.create.content.contraptions.base.DirectionalAxisKineticBlock;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.network.DebugPacketSender;
import net.minecraft.state.BooleanProperty; import net.minecraft.state.BooleanProperty;
import net.minecraft.state.StateContainer.Builder; import net.minecraft.state.StateContainer.Builder;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
@ -17,6 +21,9 @@ import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader; import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorldReader; import net.minecraft.world.IWorldReader;
import net.minecraft.world.TickPriority;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
public class FluidValveBlock extends DirectionalAxisKineticBlock implements IAxisPipe { public class FluidValveBlock extends DirectionalAxisKineticBlock implements IAxisPipe {
@ -75,4 +82,49 @@ public class FluidValveBlock extends DirectionalAxisKineticBlock implements IAxi
return getPipeAxis(state); return getPipeAxis(state);
} }
@Override
public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) {
boolean blockTypeChanged = state.getBlock() != newState.getBlock();
if (blockTypeChanged && !world.isRemote)
FluidPropagator.propagateChangedPipe(world, pos, state);
if (state.hasTileEntity() && (blockTypeChanged || !newState.hasTileEntity()))
world.removeTileEntity(pos);
}
@Override
public boolean isValidPosition(BlockState p_196260_1_, IWorldReader p_196260_2_, BlockPos p_196260_3_) {
return true;
}
@Override
public void onBlockAdded(BlockState state, World world, BlockPos pos, BlockState oldState, boolean isMoving) {
if (world.isRemote)
return;
if (state != oldState)
world.getPendingBlockTicks()
.scheduleTick(pos, this, 1, TickPriority.HIGH);
}
@Override
public void neighborChanged(BlockState state, World world, BlockPos pos, Block otherBlock, BlockPos neighborPos,
boolean isMoving) {
DebugPacketSender.func_218806_a(world, pos);
Direction d = FluidPropagator.validateNeighbourChange(state, world, pos, otherBlock, neighborPos, isMoving);
if (d == null)
return;
if (!isOpenAt(state, d))
return;
world.getPendingBlockTicks()
.scheduleTick(pos, this, 1, TickPriority.HIGH);
}
public static boolean isOpenAt(BlockState state, Direction d) {
return d.getAxis() == getPipeAxis(state);
}
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) {
FluidPropagator.propagateChangedPipe(world, pos, state);
}
} }

View file

@ -3,11 +3,9 @@ package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.List; import java.util.List;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeFluidTransportBehaviour;
import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeAttachmentBehaviour;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BlockHelper;
import com.simibubi.create.foundation.utility.LerpedFloat; import com.simibubi.create.foundation.utility.LerpedFloat;
import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; import com.simibubi.create.foundation.utility.LerpedFloat.Chaser;
@ -79,24 +77,23 @@ public class FluidValveTileEntity extends KineticTileEntity {
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new ValvePipeBehaviour(this)); behaviours.add(new ValvePipeBehaviour(this));
behaviours.add(new StraightPipeAttachmentBehaviour(this));
} }
class ValvePipeBehaviour extends FluidPipeBehaviour { class ValvePipeBehaviour extends StraightPipeFluidTransportBehaviour {
public ValvePipeBehaviour(SmartTileEntity te) { public ValvePipeBehaviour(SmartTileEntity te) {
super(te); super(te);
} }
@Override @Override
public boolean isConnectedTo(BlockState state, Direction direction) { public boolean canHaveFlowToward(BlockState state, Direction direction) {
return FluidValveBlock.getPipeAxis(state) == direction.getAxis(); return FluidValveBlock.getPipeAxis(state) == direction.getAxis();
} }
@Override @Override
public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) { public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) {
if (BlockHelper.hasBlockStateProperty(state, FluidValveBlock.ENABLED) && state.get(FluidValveBlock.ENABLED)) if (state.contains(FluidValveBlock.ENABLED) && state.get(FluidValveBlock.ENABLED))
return super.canTransferToward(fluid, state, direction, inbound); return super.canPullFluidFrom(fluid, state, direction);
return false; return false;
} }

View file

@ -118,6 +118,11 @@ public class SmartFluidPipeBlock extends HorizontalFaceBlock implements IAxisPip
return d.getAxis() == getPipeAxis(state); return d.getAxis() == getPipeAxis(state);
} }
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) {
FluidPropagator.propagateChangedPipe(world, pos, state);
}
protected static Axis getPipeAxis(BlockState state) { protected static Axis getPipeAxis(BlockState state) {
return state.get(FACE) == AttachFace.WALL ? Axis.Y return state.get(FACE) == AttachFace.WALL ? Axis.Y
: state.get(HORIZONTAL_FACING) : state.get(HORIZONTAL_FACING)
@ -134,11 +139,6 @@ public class SmartFluidPipeBlock extends HorizontalFaceBlock implements IAxisPip
return AllTileEntities.SMART_FLUID_PIPE.create(); return AllTileEntities.SMART_FLUID_PIPE.create();
} }
@Override
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random r) {
FluidPropagator.propagateChangedPipe(world, pos, state);
}
@Override @Override
public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_, public VoxelShape getShape(BlockState state, IBlockReader p_220053_2_, BlockPos p_220053_3_,
ISelectionContext p_220053_4_) { ISelectionContext p_220053_4_) {

View file

@ -3,9 +3,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.List; import java.util.List;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour;
import com.simibubi.create.content.contraptions.fluids.FluidPropagator; import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.fluids.pipes.StraightPipeTileEntity.StraightPipeFluidTransportBehaviour;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform; import com.simibubi.create.foundation.tileEntity.behaviour.ValueBoxTransform;
@ -34,7 +33,6 @@ public class SmartFluidPipeTileEntity extends SmartTileEntity {
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new SmartPipeBehaviour(this)); behaviours.add(new SmartPipeBehaviour(this));
behaviours.add(new StraightPipeAttachmentBehaviour(this));
behaviours.add(filter = new FilteringBehaviour(this, new SmartPipeFilterSlot()).forFluids() behaviours.add(filter = new FilteringBehaviour(this, new SmartPipeFilterSlot()).forFluids()
.withCallback(this::onFilterChanged)); .withCallback(this::onFilterChanged));
} }
@ -45,21 +43,21 @@ public class SmartFluidPipeTileEntity extends SmartTileEntity {
FluidPropagator.propagateChangedPipe(world, pos, getBlockState()); FluidPropagator.propagateChangedPipe(world, pos, getBlockState());
} }
class SmartPipeBehaviour extends FluidPipeBehaviour { class SmartPipeBehaviour extends StraightPipeFluidTransportBehaviour {
public SmartPipeBehaviour(SmartTileEntity te) { public SmartPipeBehaviour(SmartTileEntity te) {
super(te); super(te);
} }
@Override @Override
public boolean canTransferToward(FluidStack fluid, BlockState state, Direction direction, boolean inbound) { public boolean canPullFluidFrom(FluidStack fluid, BlockState state, Direction direction) {
if (fluid.isEmpty() || filter != null && filter.test(fluid)) if (fluid.isEmpty() || filter != null && filter.test(fluid))
return super.canTransferToward(fluid, state, direction, inbound); return super.canPullFluidFrom(fluid, state, direction);
return false; return false;
} }
@Override @Override
public boolean isConnectedTo(BlockState state, Direction direction) { public boolean canHaveFlowToward(BlockState state, Direction direction) {
return state.getBlock() instanceof SmartFluidPipeBlock return state.getBlock() instanceof SmartFluidPipeBlock
&& SmartFluidPipeBlock.getPipeAxis(state) == direction.getAxis(); && SmartFluidPipeBlock.getPipeAxis(state) == direction.getAxis();
} }

View file

@ -2,8 +2,8 @@ package com.simibubi.create.content.contraptions.fluids.pipes;
import java.util.List; import java.util.List;
import com.simibubi.create.content.contraptions.fluids.FluidPipeAttachmentBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; import com.simibubi.create.content.contraptions.relays.elementary.BracketedTileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
@ -23,32 +23,25 @@ public class StraightPipeTileEntity extends SmartTileEntity {
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new StraightPipeBehaviour(this)); behaviours.add(new StraightPipeFluidTransportBehaviour(this));
behaviours.add(new StraightPipeAttachmentBehaviour(this)); behaviours.add(new BracketedTileEntityBehaviour(this));
} }
class StraightPipeBehaviour extends FluidPipeBehaviour { static class StraightPipeFluidTransportBehaviour extends FluidTransportBehaviour {
public StraightPipeBehaviour(SmartTileEntity te) { public StraightPipeFluidTransportBehaviour(SmartTileEntity te) {
super(te); super(te);
} }
@Override @Override
public boolean isConnectedTo(BlockState state, Direction direction) { public boolean canHaveFlowToward(BlockState state, Direction direction) {
return state.get(AxisPipeBlock.AXIS) == direction.getAxis(); return state.get(AxisPipeBlock.AXIS) == direction.getAxis();
} }
}
static class StraightPipeAttachmentBehaviour extends FluidPipeAttachmentBehaviour {
public StraightPipeAttachmentBehaviour(SmartTileEntity te) {
super(te);
}
@Override @Override
public AttachmentTypes getAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state, Direction direction) { public AttachmentTypes getRenderedRimAttachment(IBlockDisplayReader world, BlockPos pos, BlockState state,
AttachmentTypes attachment = super.getAttachment(world, pos, state, direction); Direction direction) {
AttachmentTypes attachment = super.getRenderedRimAttachment(world, pos, state, direction);
BlockState otherState = world.getBlockState(pos.offset(direction)); BlockState otherState = world.getBlockState(pos.offset(direction));
Axis axis = IAxisPipe.getAxisOf(state); Axis axis = IAxisPipe.getAxisOf(state);

View file

@ -1,13 +1,13 @@
package com.simibubi.create.content.contraptions.fluids.pipes; package com.simibubi.create.content.contraptions.fluids.pipes;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.content.contraptions.fluids.FluidPipeBehaviour; import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
import com.simibubi.create.content.contraptions.fluids.PipeConnection.Flow;
import com.simibubi.create.foundation.fluid.FluidRenderer; import com.simibubi.create.foundation.fluid.FluidRenderer;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.LerpedFloat; import com.simibubi.create.foundation.utility.LerpedFloat;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.client.renderer.IRenderTypeBuffer; import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
@ -23,32 +23,40 @@ public class TransparentStraightPipeRenderer extends SafeTileEntityRenderer<Stra
@Override @Override
protected void renderSafe(StraightPipeTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, protected void renderSafe(StraightPipeTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer,
int light, int overlay) { int light, int overlay) {
FluidPipeBehaviour pipe = te.getBehaviour(FluidPipeBehaviour.TYPE); FluidTransportBehaviour pipe = te.getBehaviour(FluidTransportBehaviour.TYPE);
if (pipe == null) if (pipe == null)
return; return;
FluidStack fluidStack = pipe.getFluid();
if (fluidStack.isEmpty())
return;
for (Direction side : Iterate.directions) { for (Direction side : Iterate.directions) {
if (!pipe.isConnectedTo(te.getBlockState(), side))
Flow flow = pipe.getFlow(side);
if (flow == null)
continue; continue;
Pair<Boolean, LerpedFloat> strogestFlow = pipe.getStrogestFlow(side); FluidStack fluidStack = flow.fluid;
if (strogestFlow == null) if (fluidStack.isEmpty())
continue; continue;
LerpedFloat second = strogestFlow.getSecond(); LerpedFloat progress = flow.progress;
if (second == null) if (progress == null)
continue; continue;
float value = second.getValue(partialTicks); float value = progress.getValue(partialTicks);
Boolean inbound = strogestFlow.getFirst(); boolean inbound = flow.inbound;
if (value == 1 && !inbound) { if (value == 1) {
FluidPipeBehaviour adjacent = TileEntityBehaviour.get(te.getWorld(), te.getPos() if (inbound) {
.offset(side), FluidPipeBehaviour.TYPE); Flow opposite = pipe.getFlow(side.getOpposite());
if (opposite == null)
if (adjacent != null && adjacent.getFluid()
.isEmpty())
value -= 1e-6f; value -= 1e-6f;
} else {
FluidTransportBehaviour adjacent = TileEntityBehaviour.get(te.getWorld(), te.getPos()
.offset(side), FluidTransportBehaviour.TYPE);
if (adjacent == null)
value -= 1e-6f;
else {
Flow other = adjacent.getFlow(side.getOpposite());
if (other == null || !other.inbound && !other.complete)
value -= 1e-6f;
}
}
} }
FluidRenderer.renderFluidStream(fluidStack, side, 3 / 16f, value, inbound, buffer, ms, light); FluidRenderer.renderFluidStream(fluidStack, side, 3 / 16f, value, inbound, buffer, ms, light);

View file

@ -182,6 +182,12 @@ public class BasinBlock extends Block implements ITE<BasinTileEntity>, IWrenchab
} }
public static boolean canOutputTo(IBlockReader world, BlockPos basinPos, Direction direction) { public static boolean canOutputTo(IBlockReader world, BlockPos basinPos, Direction direction) {
BlockPos neighbour = basinPos.offset(direction);
if (!world.getBlockState(neighbour)
.getCollisionShape(world, neighbour)
.isEmpty())
return false;
BlockPos offset = basinPos.down() BlockPos offset = basinPos.down()
.offset(direction); .offset(direction);
DirectBeltInputBehaviour directBeltInputBehaviour = DirectBeltInputBehaviour directBeltInputBehaviour =

View file

@ -1,16 +1,20 @@
package com.simibubi.create.content.contraptions.relays.elementary; package com.simibubi.create.content.contraptions.relays.elementary;
import java.util.Optional; import java.util.Optional;
import java.util.function.Predicate;
import com.google.common.base.Predicates;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil; import net.minecraft.nbt.NBTUtil;
import net.minecraft.world.World;
public class BracketedTileEntityBehaviour extends TileEntityBehaviour { public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
@ -19,8 +23,15 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
private Optional<BlockState> bracket; private Optional<BlockState> bracket;
private boolean reRender; private boolean reRender;
private Predicate<BlockState> pred;
public BracketedTileEntityBehaviour(SmartTileEntity te) { public BracketedTileEntityBehaviour(SmartTileEntity te) {
this(te, Predicates.alwaysTrue());
}
public BracketedTileEntityBehaviour(SmartTileEntity te, Predicate<BlockState> pred) {
super(te); super(te);
this.pred = pred;
bracket = Optional.empty(); bracket = Optional.empty();
} }
@ -36,11 +47,18 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
} }
public void removeBracket() { public void removeBracket() {
World world = getWorld();
if (!world.isRemote)
world.playEvent(2001, getPos(), Block.getStateId(getBracket()));
this.bracket = Optional.empty(); this.bracket = Optional.empty();
reRender = true; reRender = true;
tileEntity.notifyUpdate(); tileEntity.notifyUpdate();
} }
public boolean isBacketPresent() {
return getBracket() != Blocks.AIR.getDefaultState();
}
public BlockState getBracket() { public BlockState getBracket() {
return bracket.orElse(Blocks.AIR.getDefaultState()); return bracket.orElse(Blocks.AIR.getDefaultState());
} }
@ -66,10 +84,7 @@ public class BracketedTileEntityBehaviour extends TileEntityBehaviour {
} }
public boolean canHaveBracket() { public boolean canHaveBracket() {
BlockState blockState = tileEntity.getBlockState(); return pred.test(tileEntity.getBlockState());
if (blockState.getBlock() instanceof AbstractShaftBlock)
return true;
return false;
} }
} }

View file

@ -16,7 +16,7 @@ public class SimpleKineticTileEntity extends KineticTileEntity {
@Override @Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { public void addBehaviours(List<TileEntityBehaviour> behaviours) {
behaviours.add(new BracketedTileEntityBehaviour(this)); behaviours.add(new BracketedTileEntityBehaviour(this, state -> state.getBlock() instanceof AbstractShaftBlock));
super.addBehaviours(behaviours); super.addBehaviours(behaviours);
} }

View file

@ -51,6 +51,14 @@ public class FluidHelper {
return blockState != null && blockState != Blocks.AIR.getDefaultState(); return blockState != null && blockState != Blocks.AIR.getDefaultState();
} }
public static FluidStack copyStackWithAmount(FluidStack fs, int amount) {
if (fs.isEmpty())
return FluidStack.EMPTY;
FluidStack copy = fs.copy();
copy.setAmount(amount);
return copy;
}
public static Fluid convertToFlowing(Fluid fluid) { public static Fluid convertToFlowing(Fluid fluid) {
if (fluid == Fluids.WATER) if (fluid == Fluids.WATER)
return Fluids.FLOWING_WATER; return Fluids.FLOWING_WATER;

View file

@ -48,6 +48,10 @@ public abstract class Outline {
} }
public void renderAACuboidLine(MatrixStack ms, SuperRenderTypeBuffer buffer, Vector3d start, Vector3d end) { public void renderAACuboidLine(MatrixStack ms, SuperRenderTypeBuffer buffer, Vector3d start, Vector3d end) {
float lineWidth = params.getLineWidth();
if (lineWidth == 0)
return;
IVertexBuilder builder = buffer.getBuffer(RenderTypes.getOutlineSolid()); IVertexBuilder builder = buffer.getBuffer(RenderTypes.getOutlineSolid());
Vector3d diff = end.subtract(start); Vector3d diff = end.subtract(start);
@ -58,7 +62,6 @@ public abstract class Outline {
diff = diff.scale(-1); diff = diff.scale(-1);
} }
float lineWidth = params.getLineWidth();
Vector3d extension = diff.normalize() Vector3d extension = diff.normalize()
.scale(lineWidth / 2); .scale(lineWidth / 2);
Vector3d plane = VecHelper.axisAlingedPlaneOf(diff); Vector3d plane = VecHelper.axisAlingedPlaneOf(diff);

View file

@ -455,6 +455,8 @@
"create.hint.upward_funnel": "can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.", "create.hint.upward_funnel": "can only transfer items inserted by _Arms_, fan-powered _Chutes_, or items _thrown_ at them. Try building some _Chutes_ if you are looking to move your items _vertically_.",
"create.hint.empty_bearing.title": "Update Bearing", "create.hint.empty_bearing.title": "Update Bearing",
"create.hint.empty_bearing": "_Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.", "create.hint.empty_bearing": "_Right-click_ the bearing with an _empty_ _hand_ to _attach_ the structure you just built in front of it.",
"create.hint.full_deployer.title": "Deployer Item Overflow",
"create.hint.full_deployer": "It appears this _Deployer_ contains _excess_ _items_ that need to be _extracted._ Use a _hopper,_ _funnel_ or other means to free it from its overflow.",
"create.gui.config.overlay1": "Hi :)", "create.gui.config.overlay1": "Hi :)",
"create.gui.config.overlay2": "This is a sample overlay", "create.gui.config.overlay2": "This is a sample overlay",