Add the dual P2P interface (#139)

* Add the dual P2P interface

- Can be disabled in the config
- Updated dependencies
- We now require a hard inheritance dependency on beta-238+.
- CANNOT BE ATTUNED.

* Dependency cleanup (don't use pre)

* Fix dual interface background

* Fix null icon for p2p dual interface

BetterP2P needs this to properly display the icon.

* spotlessApply

---------

Co-authored-by: GitHub GTNH Actions <unknown>
This commit is contained in:
firenooandGitHub GTNH Actions authored and GitHub committed 2023-07-22 17:00:43 +02:00
1 parent e8f069375f
commit c94876b02e
12 files changed
+609 -4

No files matched your search

@@ -27,7 +27,7 @@ import cpw.mods.fml.common.network.NetworkRegistry;
modid = FluidCraft.MODID,
version = FluidCraft.VERSION,
name = FluidCraft.MODNAME,
dependencies = "required-after:appliedenergistics2;required-after:CoFHCore;required-after:Baubles;after:waila;after:thaumicenergistics;after:ae2wct")
dependencies = "required-after:appliedenergistics2@[rv3-beta-238,);required-after:CoFHCore;required-after:Baubles;after:waila;after:thaumicenergistics;after:ae2wct")
public class FluidCraft {
public static final String MODID = "GRADLETOKEN_MODID";
@@ -129,6 +129,7 @@ public class GuiDualInterface extends GuiUpgradeable {
protected String getBackground() {
if (!ModAndClassUtil.isBigInterface) return "guis/interface.png";
return switch (((ContainerInterface) this.cvb).getPatternCapacityCardsInstalled()) {
case -1 -> "guis/interfacenone.png";
case 1 -> "guis/interface2.png";
case 2 -> "guis/interface3.png";
case 3 -> "guis/interface4.png";
@@ -16,6 +16,7 @@ public class Config {
public static boolean fluidIOBus;
public static boolean removeRecipe;
public static double portableCellBattery;
public static boolean fluidP2PInterface;
public static int packetSize;
public static int packetRate;
public static boolean replaceEC2;
@@ -41,6 +42,11 @@ public class Config {
"Fluid Craft for AE2",
true,
"Enable this to add Fluid Import/Export Bus like EC2's one.");
fluidP2PInterface = Config.getBoolean(
"fluidP2PInterface",
"Fluid Craft for AE2",
true,
"Enable the P2P Tunnel - ME Dual Interface feature.");
removeRecipe = Config.getBoolean(
"Disable all recipes",
"Fluid Craft for AE2",
@@ -0,0 +1,71 @@
package com.glodblock.github.common.item;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import com.glodblock.github.FluidCraft;
import com.glodblock.github.common.Config;
import com.glodblock.github.common.parts.PartFluidP2PInterface;
import com.glodblock.github.common.tabs.FluidCraftingTabs;
import com.glodblock.github.util.NameConst;
import appeng.api.AEApi;
import appeng.api.parts.IPart;
import appeng.api.parts.IPartItem;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class ItemPartFluidP2PInterface extends FCBaseItem implements IPartItem {
private IIcon icon;
public ItemPartFluidP2PInterface() {
this.setMaxStackSize(64);
this.setUnlocalizedName(NameConst.ITEM_PART_FLUID_P2P_INTERFACE);
AEApi.instance().partHelper().setItemBusRenderer(this);
}
@Nullable
@Override
public IPart createPartFromItemStack(ItemStack is) {
return new PartFluidP2PInterface(is);
}
@Override
public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side,
float xOffset, float yOffset, float zOffset) {
return AEApi.instance().partHelper().placeBus(player.getHeldItem(), x, y, z, side, player, world);
}
@Override
public ItemPartFluidP2PInterface register() {
if (!Config.fluidP2PInterface) return null;
GameRegistry.registerItem(this, NameConst.ITEM_PART_FLUID_P2P_INTERFACE, FluidCraft.MODID);
setCreativeTab(FluidCraftingTabs.INSTANCE);
return this;
}
@Override
public IIcon getIconFromDamage(int dmg) {
return icon;
}
@Override
public void registerIcons(IIconRegister register) {
if (Config.fluidP2PInterface) {
this.icon = register.registerIcon("appliedenergistics2:ItemPart.P2PTunnel");
}
}
@Override
@SideOnly(Side.CLIENT)
public int getSpriteNumber() {
return 0;
}
}
@@ -0,0 +1,516 @@
package com.glodblock.github.common.parts;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.util.Vec3;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import com.glodblock.github.common.item.ItemFluidPacket;
import com.glodblock.github.inventory.AEFluidInventory;
import com.glodblock.github.inventory.IAEFluidTank;
import com.glodblock.github.inventory.IDualHost;
import com.glodblock.github.inventory.InventoryHandler;
import com.glodblock.github.inventory.gui.GuiType;
import com.glodblock.github.loader.ItemAndBlockHolder;
import com.glodblock.github.util.BlockPos;
import com.glodblock.github.util.DualityFluidInterface;
import com.glodblock.github.util.Util;
import com.google.common.collect.ImmutableSet;
import appeng.api.config.Actionable;
import appeng.api.config.Upgrades;
import appeng.api.implementations.tiles.ITileStorageMonitorable;
import appeng.api.networking.IGridNode;
import appeng.api.networking.crafting.ICraftingLink;
import appeng.api.networking.crafting.ICraftingPatternDetails;
import appeng.api.networking.crafting.ICraftingProviderHelper;
import appeng.api.networking.energy.IEnergyGrid;
import appeng.api.networking.events.MENetworkChannelsChanged;
import appeng.api.networking.events.MENetworkCraftingPatternChange;
import appeng.api.networking.events.MENetworkEventSubscribe;
import appeng.api.networking.events.MENetworkPowerStatusChange;
import appeng.api.networking.security.BaseActionSource;
import appeng.api.networking.security.MachineSource;
import appeng.api.networking.storage.IStorageGrid;
import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPart;
import appeng.api.storage.IMEMonitor;
import appeng.api.storage.IStorageMonitorable;
import appeng.api.storage.data.IAEFluidStack;
import appeng.api.storage.data.IAEItemStack;
import appeng.api.util.IConfigManager;
import appeng.helpers.DualityInterface;
import appeng.helpers.IInterfaceHost;
import appeng.helpers.IPriorityHost;
import appeng.me.GridAccessException;
import appeng.parts.automation.UpgradeInventory;
import appeng.parts.p2p.PartP2PTunnelStatic;
import appeng.tile.inventory.AppEngInternalAEInventory;
import appeng.tile.inventory.AppEngInternalInventory;
import appeng.tile.inventory.IAEAppEngInventory;
import appeng.tile.inventory.InvOperation;
import appeng.util.Platform;
import appeng.util.inv.IInventoryDestination;
import appeng.util.item.AEFluidStack;
public class PartFluidP2PInterface extends PartP2PTunnelStatic<PartFluidP2PInterface>
implements IGridTickable, IStorageMonitorable, IInventoryDestination, IDualHost, ISidedInventory,
IAEAppEngInventory, ITileStorageMonitorable, IPriorityHost, IInterfaceHost {
private final DualityInterface duality = new DualityInterface(this.getProxy(), this) {
@Override
public void updateCraftingList() {
if (!isOutput()) {
super.updateCraftingList();
try {
for (PartFluidP2PInterface p2p : getOutputs()) p2p.duality.updateCraftingList();
} catch (GridAccessException e) {
// ?
}
} else {
PartFluidP2PInterface p2p = getInput();
if (p2p != null) {
this.craftingList = p2p.duality.craftingList;
try {
this.gridProxy.getGrid()
.postEvent(new MENetworkCraftingPatternChange(this, this.gridProxy.getNode()));
} catch (final GridAccessException e) {
// :P
}
}
}
}
@Override
public int getInstalledUpgrades(Upgrades u) {
if (isOutput() && u == Upgrades.PATTERN_CAPACITY) return -1;
return super.getInstalledUpgrades(u);
}
};
private final DualityFluidInterface dualityFluid = new DualityFluidInterface(this.getProxy(), this);
private final AppEngInternalAEInventory config = new AppEngInternalAEInventory(this, 6);
private final BaseActionSource ownActionSource = new MachineSource(this);
public PartFluidP2PInterface(ItemStack is) {
super(is);
}
@MENetworkEventSubscribe
public void stateChange(final MENetworkChannelsChanged c) {
dualityFluid.onChannelStateChange(c);
duality.notifyNeighbors();
}
@MENetworkEventSubscribe
public void stateChange(final MENetworkPowerStatusChange c) {
dualityFluid.onPowerStateChange(c);
duality.notifyNeighbors();
}
@Override
public void gridChanged() {
super.gridChanged();
dualityFluid.gridChanged();
}
@Override
public boolean onPartActivate(final EntityPlayer player, final Vec3 pos) {
AppEngInternalInventory patterns = (AppEngInternalInventory) duality.getPatterns();
if (super.onPartActivate(player, pos)) {
ArrayList<ItemStack> drops = new ArrayList<>();
for (int i = 0; i < patterns.getSizeInventory(); i++) {
if (patterns.getStackInSlot(i) == null) continue;
drops.add(patterns.getStackInSlot(i));
}
final IPart tile = this.getHost().getPart(this.getSide());
if (tile instanceof PartFluidP2PInterface dualTile) {
DualityInterface newDuality = dualTile.duality;
// Copy interface storage, upgrades, and settings over
UpgradeInventory upgrades = (UpgradeInventory) duality.getInventoryByName("upgrades");
dualTile.duality.getStorage();
UpgradeInventory newUpgrade = (UpgradeInventory) newDuality.getInventoryByName("upgrades");
for (int i = 0; i < upgrades.getSizeInventory(); ++i) {
newUpgrade.setInventorySlotContents(i, upgrades.getStackInSlot(i));
}
IInventory storage = duality.getStorage();
IInventory newStorage = newDuality.getStorage();
for (int i = 0; i < storage.getSizeInventory(); ++i) {
newStorage.setInventorySlotContents(i, storage.getStackInSlot(i));
}
IConfigManager config = duality.getConfigManager();
config.getSettings().forEach(
setting -> newDuality.getConfigManager().putSetting(setting, config.getSetting(setting)));
}
TileEntity te = getTileEntity();
Platform.spawnDrops(te.getWorldObj(), te.xCoord, te.yCoord, te.zCoord, drops);
return true;
}
if (player.isSneaking()) {
return false;
}
if (Platform.isServer()) {
InventoryHandler.openGui(
player,
this.getHost().getTile().getWorldObj(),
new BlockPos(this.getHost().getTile()),
Objects.requireNonNull(this.getSide()),
GuiType.DUAL_INTERFACE);
}
return true;
}
@Override
public IIcon getTypeTexture() {
return ItemAndBlockHolder.INTERFACE.getBlockTextureFromSide(0);
}
@Override
public IStorageMonitorable getMonitorable(ForgeDirection side, BaseActionSource src) {
return duality.getMonitorable(side, src, this);
}
@Override
public TickingRequest getTickingRequest(IGridNode node) {
TickingRequest item = duality.getTickingRequest(node);
TickingRequest fluid = dualityFluid.getTickingRequest(node);
return new TickingRequest(
Math.min(item.minTickRate, fluid.minTickRate),
Math.max(item.maxTickRate, fluid.maxTickRate),
item.isSleeping && fluid.isSleeping,
true);
}
@Override
public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) {
TickRateModulation item = duality.tickingRequest(node, ticksSinceLastCall);
TickRateModulation fluid = dualityFluid.tickingRequest(node, ticksSinceLastCall);
if (item.ordinal() >= fluid.ordinal()) {
return item;
} else {
return fluid;
}
}
@Override
public int getInstalledUpgrades(Upgrades u) {
return duality.getInstalledUpgrades(u);
}
@Override
public IMEMonitor<IAEItemStack> getItemInventory() {
return duality.getItemInventory();
}
@Override
public IMEMonitor<IAEFluidStack> getFluidInventory() {
return duality.getFluidInventory();
}
@Override
public int getPriority() {
return duality.getPriority();
}
@Override
public void setPriority(int newValue) {
duality.setPriority(newValue);
}
@Override
public void onTunnelNetworkChange() {
duality.updateCraftingList();
}
@Override
public void onChangeInventory(IInventory inv, int slot, InvOperation op, ItemStack removedStack,
ItemStack newStack) {
duality.onChangeInventory(inv, slot, op, removedStack, newStack);
}
@Override
public boolean canInsert(ItemStack stack) {
return duality.canInsert(stack);
}
@Override
public int getSizeInventory() {
return duality.getStorage().getSizeInventory();
}
@Override
public ItemStack getStackInSlot(int slotIn) {
return duality.getStorage().getStackInSlot(slotIn);
}
@Override
public ItemStack decrStackSize(int index, int count) {
return duality.getStorage().decrStackSize(index, count);
}
@Override
public ItemStack getStackInSlotOnClosing(int index) {
return duality.getStorage().getStackInSlotOnClosing(index);
}
@Override
public void setInventorySlotContents(int index, ItemStack stack) {
duality.getStorage().setInventorySlotContents(index, stack);
}
@Override
public IInventory getInventoryByName(String name) {
return duality.getInventoryByName(name);
}
@Override
public IConfigManager getConfigManager() {
return duality.getConfigManager();
}
@Override
public IIcon getBreakingTexture() {
return getItemStack().getIconIndex();
}
@Override
public String getInventoryName() {
return duality.getStorage().getInventoryName();
}
@Override
public boolean hasCustomInventoryName() {
return duality.getStorage().hasCustomInventoryName();
}
@Override
public int getInventoryStackLimit() {
return duality.getStorage().getInventoryStackLimit();
}
@Override
public void markDirty() {
duality.markDirty();
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return duality.getStorage().isUseableByPlayer(player);
}
@Override
public void openInventory() {
duality.getStorage().openInventory();
}
@Override
public void closeInventory() {
duality.getStorage().closeInventory();
}
@Override
public boolean isItemValidForSlot(int index, ItemStack stack) {
return duality.getStorage().isItemValidForSlot(index, stack);
}
@Override
public int[] getAccessibleSlotsFromSide(int side) {
return duality.getAccessibleSlotsFromSide(side);
}
@Override
public boolean canInsertItem(int p_102007_1_, ItemStack itemStack, int p_102007_3_) {
return true;
}
@Override
public boolean canExtractItem(int p_102008_1_, ItemStack p_102008_2_, int p_102008_3_) {
return true;
}
private IMEMonitor<IAEFluidStack> getFluidGrid() {
try {
return getProxy().getGrid().<IStorageGrid>getCache(IStorageGrid.class).getFluidInventory();
} catch (GridAccessException e) {
return null;
}
}
private IEnergyGrid getEnergyGrid() {
try {
return getProxy().getGrid().getCache(IEnergyGrid.class);
} catch (GridAccessException e) {
return null;
}
}
@Override
public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
IMEMonitor<IAEFluidStack> fluidGrid = getFluidGrid();
IEnergyGrid energyGrid = getEnergyGrid();
if (energyGrid == null || fluidGrid == null || resource == null) return 0;
int ori = resource.amount;
IAEFluidStack remove;
if (doFill) {
remove = fluidGrid.injectItems(AEFluidStack.create(resource), Actionable.MODULATE, ownActionSource);
} else {
remove = fluidGrid.injectItems(AEFluidStack.create(resource), Actionable.SIMULATE, ownActionSource);
}
return remove == null ? ori : (int) (ori - remove.getStackSize());
}
@Override
public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) {
return dualityFluid.drain(from, resource, doDrain);
}
@Override
public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
return dualityFluid.drain(from, maxDrain, doDrain);
}
@Override
public boolean canFill(ForgeDirection from, Fluid fluid) {
return true;
}
@Override
public boolean canDrain(ForgeDirection from, Fluid fluid) {
return true;
}
@Override
public FluidTankInfo[] getTankInfo(ForgeDirection from) {
return dualityFluid.getTankInfo(from);
}
@Override
public void onFluidInventoryChanged(IAEFluidTank inv, int slot) {
saveChanges();
getTileEntity().markDirty();
dualityFluid.onFluidInventoryChanged(inv, slot);
}
@Override
public AEFluidInventory getInternalFluid() {
return dualityFluid.getInternalFluid();
}
@Override
public DualityFluidInterface getDualityFluid() {
return dualityFluid;
}
@Override
public AppEngInternalAEInventory getConfig() {
Util.mirrorFluidToPacket(config, dualityFluid.getConfig());
return config;
}
@Override
public void setConfig(int id, IAEFluidStack fluid) {
if (id >= 0 && id < 6) {
config.setInventorySlotContents(
id,
ItemFluidPacket.newDisplayStack(fluid == null ? null : fluid.getFluidStack()));
dualityFluid.getConfig().setFluidInSlot(id, dualityFluid.getStandardFluid(fluid));
}
}
@Override
public void setFluidInv(int id, IAEFluidStack fluid) {
if (id >= 0 && id < 6) {
dualityFluid.getInternalFluid().setFluidInSlot(id, fluid);
}
}
@Override
public DualityInterface getInterfaceDuality() {
return duality;
}
@Override
public EnumSet<ForgeDirection> getTargets() {
return EnumSet.of(this.getSide());
}
@Override
public TileEntity getTileEntity() {
return super.getHost().getTile();
}
@Override
public void provideCrafting(ICraftingProviderHelper craftingTracker) {
duality.provideCrafting(craftingTracker);
}
@Override
public boolean pushPattern(ICraftingPatternDetails patternDetails, InventoryCrafting table) {
return duality.pushPattern(patternDetails, table);
}
@Override
public boolean isBusy() {
return duality.isBusy();
}
@Override
public ImmutableSet<ICraftingLink> getRequestedJobs() {
return duality.getRequestedJobs();
}
@Override
public IAEItemStack injectCraftedItems(ICraftingLink link, IAEItemStack items, Actionable mode) {
return duality.injectCraftedItems(link, items, mode);
}
@Override
public void jobStateChange(ICraftingLink link) {
duality.jobStateChange(link);
}
@Override
public void readFromNBT(NBTTagCompound data) {
super.readFromNBT(data);
duality.readFromNBT(data);
}
@Override
public void writeToNBT(NBTTagCompound data) {
super.writeToNBT(data);
duality.writeToNBT(data);
}
@Override
public void addToWorld() {
super.addToWorld();
this.duality.initialize();
}
@Override
public void getDrops(List<ItemStack> drops, boolean wrenched) {
super.getDrops(drops, wrenched);
duality.addDrops(drops);
}
}
@@ -17,6 +17,7 @@ import com.glodblock.github.common.Config;
import com.glodblock.github.common.item.ItemFluidPacket;
import com.glodblock.github.common.parts.PartFluidExportBus;
import com.glodblock.github.common.parts.PartFluidInterface;
import com.glodblock.github.common.parts.PartFluidP2PInterface;
import com.glodblock.github.common.tile.TileFluidInterface;
import com.glodblock.github.util.Ae2Reflect;
import com.glodblock.github.util.BlockPos;
@@ -88,7 +89,8 @@ public class FluidConvertingInventoryAdaptor extends InventoryAdaptor {
capProvider.zCoord + face.offsetZ);
if (!Config.noFluidPacket && !(inter instanceof TileFluidInterface
|| Util.getPart(inter, face.getOpposite()) instanceof PartFluidInterface
|| Util.getPart(inter, face.getOpposite()) instanceof PartFluidExportBus))
|| Util.getPart(inter, face.getOpposite()) instanceof PartFluidExportBus
|| Util.getPart(inter, face.getOpposite()) instanceof PartFluidP2PInterface))
return InventoryAdaptor.getAdaptor(capProvider, face);
InventoryAdaptor item = InventoryAdaptor.getAdaptor(capProvider, face);
IFluidHandler fluid = capProvider instanceof IFluidHandler ? (IFluidHandler) capProvider : null;
@@ -28,6 +28,7 @@ public class ItemAndBlockHolder {
public static ItemPartFluidPatternTerminal FLUID_TERMINAL = new ItemPartFluidPatternTerminal().register();
public static ItemPartFluidPatternTerminalEx FLUID_TERMINAL_EX = new ItemPartFluidPatternTerminalEx().register();
public static ItemPartFluidInterface FLUID_INTERFACE = new ItemPartFluidInterface().register();
public static ItemPartFluidP2PInterface FLUID_INTERFACE_P2P = new ItemPartFluidP2PInterface().register();
public static ItemFluidImportBus FLUID_IMPORT_BUS = new ItemFluidImportBus().register();
public static ItemFluidExportBus FLUID_EXPORT_BUS = new ItemFluidExportBus().register();
public static ItemPartFluidStorageBus FLUID_STORAGE_BUS = new ItemPartFluidStorageBus().register();
@@ -21,6 +21,7 @@ import com.glodblock.github.common.storage.CellType;
import com.glodblock.github.loader.recipe.WirelessTerminalRecipe;
import com.glodblock.github.util.ModAndClassUtil;
import appeng.api.AEApi;
import cpw.mods.fml.common.registry.GameRegistry;
import extracells.registries.ItemEnum;
@@ -102,6 +103,7 @@ public class RecipeLoader implements Runnable {
54);
public static final ItemStack AE2_WIRELESS_TERMINAL = GameRegistry
.findItemStack("appliedenergistics2", "item.ToolWirelessTerminal", 1);
public static final ItemStack AE2_P2P_ME = AEApi.instance().definitions().parts().p2PTunnelME().maybeStack(1).get();
public static final ItemStack AE2_ME_CHEST = GameRegistry
.findItemStack("appliedenergistics2", "tile.BlockChest", 1);
public static final ItemStack AE2_ENERGY_CELL = GameRegistry
@@ -146,6 +148,10 @@ public class RecipeLoader implements Runnable {
'E',
AE2_INTERFACE));
GameRegistry.addShapelessRecipe(FLUID_INTERFACE.stack(), INTERFACE.stack());
if (Config.fluidP2PInterface) {
GameRegistry.addShapelessRecipe(FLUID_INTERFACE_P2P.stack(), AE2_P2P_ME, INTERFACE.stack());
GameRegistry.addShapelessRecipe(FLUID_INTERFACE_P2P.stack(), AE2_P2P_ME, FLUID_INTERFACE.stack());
}
GameRegistry.addShapelessRecipe(INTERFACE.stack(), FLUID_INTERFACE.stack());
GameRegistry.addShapelessRecipe(WIRELESS_FLUID_TERM.stack(), AE2_WIRELESS_TERMINAL, BUCKET);
GameRegistry.addShapelessRecipe(WIRELESS_PATTERN_TERM.stack(), WIRELESS_FLUID_TERM, FLUID_TERMINAL);
@@ -165,7 +165,7 @@ public class DualityFluidInterface implements IGridTickable, IStorageMonitorable
@Override
public IInventory getInventoryByName(String name) {
return null;
return iHost.getInventoryByName(name);
}
public IFluidHandler getFluidInventoryByName(String name) {
@@ -29,6 +29,7 @@ public class NameConst {
public static final String ITEM_PART_FLUID_PATTERN_TERMINAL = "part_fluid_pattern_terminal";
public static final String ITEM_PART_FLUID_PATTERN_TERMINAL_EX = "part_fluid_pattern_terminal_ex";
public static final String ITEM_PART_FLUID_INTERFACE = "part_fluid_interface";
public static final String ITEM_PART_FLUID_P2P_INTERFACE = "part_fluid_p2p_interface";
public static final String ITEM_PART_FLUID_IMPORT = "part_fluid_import";
public static final String ITEM_PART_FLUID_EXPORT = "part_fluid_export";
public static final String ITEM_PART_FLUID_STORAGE_BUS = "part_fluid_storage_bus";