diff --git a/src/main/java/gregtech/api/mui/GTGuiTextures.java b/src/main/java/gregtech/api/mui/GTGuiTextures.java
index f17affee609..4f0345f49fc 100644
--- a/src/main/java/gregtech/api/mui/GTGuiTextures.java
+++ b/src/main/java/gregtech/api/mui/GTGuiTextures.java
@@ -206,6 +206,8 @@ public static class IDs {
"textures/gui/widget/button_public_private.png",
18, 36, 18, 18, true);
+ public static final UITexture MENU_OVERLAY = fullImage("textures/gui/overlay/menu_overlay.png");
+
// todo bronze/steel/primitive fluid slots?
// SLOT OVERLAYS
diff --git a/src/main/java/gregtech/api/mui/GTGuis.java b/src/main/java/gregtech/api/mui/GTGuis.java
index b5599dd9cfd..614f6a08f1b 100644
--- a/src/main/java/gregtech/api/mui/GTGuis.java
+++ b/src/main/java/gregtech/api/mui/GTGuis.java
@@ -9,6 +9,7 @@
import net.minecraft.item.ItemStack;
+import com.cleanroommc.modularui.api.widget.Interactable;
import com.cleanroommc.modularui.factory.GuiManager;
import com.cleanroommc.modularui.screen.ModularPanel;
import com.cleanroommc.modularui.utils.Alignment;
@@ -62,7 +63,15 @@ public PopupPanel(@NotNull String name, int width, int height, boolean disableBe
super(name);
size(width, height).align(Alignment.Center);
background(GTGuiTextures.BACKGROUND_POPUP);
- child(ButtonWidget.panelCloseButton().top(5).right(5));
+ child(ButtonWidget.panelCloseButton().top(5).right(5)
+ .onMousePressed(mouseButton -> {
+ if (mouseButton == 0 || mouseButton == 1) {
+ this.closeIfOpen(true);
+ Interactable.playButtonClickSound();
+ return true;
+ }
+ return false;
+ }));
this.disableBelow = disableBelow;
this.closeOnOutsideClick = closeOnOutsideClick;
}
diff --git a/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java
new file mode 100644
index 00000000000..b5be013da18
--- /dev/null
+++ b/src/main/java/gregtech/api/mui/sync/GTFluidSyncHandler.java
@@ -0,0 +1,224 @@
+package gregtech.api.mui.sync;
+
+import gregtech.api.util.GTUtility;
+
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.util.SoundEvent;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidTank;
+import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
+import net.minecraftforge.fluids.capability.IFluidHandlerItem;
+
+import com.cleanroommc.modularui.network.NetworkUtils;
+import com.cleanroommc.modularui.value.sync.SyncHandler;
+import org.jetbrains.annotations.NotNull;
+
+public class GTFluidSyncHandler extends SyncHandler {
+
+ private static final int TRY_CLICK_CONTAINER = 1;
+
+ private final IFluidTank tank;
+ private boolean canDrainSlot = true;
+ private boolean canFillSlot = true;
+
+ public GTFluidSyncHandler(IFluidTank tank) {
+ this.tank = tank;
+ }
+
+ public FluidStack getFluid() {
+ return this.tank.getFluid();
+ }
+
+ public int getCapacity() {
+ return this.tank.getCapacity();
+ }
+
+ public GTFluidSyncHandler canDrainSlot(boolean canDrainSlot) {
+ this.canDrainSlot = canDrainSlot;
+ return this;
+ }
+
+ public boolean canDrainSlot() {
+ return this.canDrainSlot;
+ }
+
+ public GTFluidSyncHandler canFillSlot(boolean canFillSlot) {
+ this.canFillSlot = canFillSlot;
+ return this;
+ }
+
+ public boolean canFillSlot() {
+ return this.canFillSlot;
+ }
+
+ @Override
+ public void readOnClient(int id, PacketBuffer buf) {
+ if (id == TRY_CLICK_CONTAINER) {
+ replaceCursorItemStack(NetworkUtils.readItemStack(buf));
+ }
+ }
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {
+ if (id == TRY_CLICK_CONTAINER) {
+ var stack = tryClickContainer(buf.readBoolean());
+ if (!stack.isEmpty())
+ syncToClient(TRY_CLICK_CONTAINER, buffer -> NetworkUtils.writeItemStack(buffer, stack));
+ }
+ }
+
+ public ItemStack tryClickContainer(boolean tryFillAll) {
+ ItemStack playerHeldStack = getSyncManager().getCursorItem();
+ if (playerHeldStack.isEmpty())
+ return ItemStack.EMPTY;
+
+ ItemStack useStack = GTUtility.copy(1, playerHeldStack);
+ IFluidHandlerItem fluidHandlerItem = useStack
+ .getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null);
+ if (fluidHandlerItem == null) return ItemStack.EMPTY;
+
+ FluidStack tankFluid = tank.getFluid();
+ FluidStack heldFluid = fluidHandlerItem.drain(Integer.MAX_VALUE, false);
+
+ // nothing to do, return
+ if (tankFluid == null && heldFluid == null)
+ return ItemStack.EMPTY;
+
+ // tank is empty, try to fill tank
+ if (canFillSlot && tankFluid == null) {
+ return fillTankFromStack(fluidHandlerItem, heldFluid, tryFillAll);
+
+ // hand is empty, try to drain tank
+ } else if (canDrainSlot && heldFluid == null) {
+ return drainTankFromStack(fluidHandlerItem, tankFluid, tryFillAll);
+
+ // neither is empty but tank is not full, try to fill tank
+ } else if (canFillSlot && tank.getFluidAmount() < tank.getCapacity() && heldFluid != null) {
+ return fillTankFromStack(fluidHandlerItem, heldFluid, tryFillAll);
+ }
+
+ return ItemStack.EMPTY;
+ }
+
+ private ItemStack fillTankFromStack(IFluidHandlerItem fluidHandler, @NotNull FluidStack heldFluid,
+ boolean tryFillAll) {
+ ItemStack heldItem = getSyncManager().getCursorItem();
+ if (heldItem.isEmpty()) return ItemStack.EMPTY;
+
+ FluidStack currentFluid = tank.getFluid();
+ // Fluid type does not match
+ if (currentFluid != null && !currentFluid.isFluidEqual(heldFluid)) return ItemStack.EMPTY;
+
+ int freeSpace = tank.getCapacity() - tank.getFluidAmount();
+ if (freeSpace <= 0) return ItemStack.EMPTY;
+
+ ItemStack itemStackEmptied = ItemStack.EMPTY;
+ int fluidAmountTaken = 0;
+
+ FluidStack drained = fluidHandler.drain(freeSpace, true);
+ if (drained != null && drained.amount > 0) {
+ itemStackEmptied = fluidHandler.getContainer();
+ fluidAmountTaken = drained.amount;
+ }
+ if (itemStackEmptied == ItemStack.EMPTY) {
+ return ItemStack.EMPTY;
+ }
+
+ // find out how many fills we can do
+ // same round down behavior as drain
+ int additional = tryFillAll ? Math.min(freeSpace / fluidAmountTaken, heldItem.getCount()) : 1;
+ FluidStack copiedFluidStack = heldFluid.copy();
+ copiedFluidStack.amount = fluidAmountTaken * additional;
+ tank.fill(copiedFluidStack, true);
+
+ itemStackEmptied.setCount(additional);
+ replaceCursorItemStack(itemStackEmptied);
+ playSound(heldFluid, true);
+ return itemStackEmptied;
+ }
+
+ private ItemStack drainTankFromStack(IFluidHandlerItem fluidHandler, FluidStack tankFluid, boolean tryFillAll) {
+ ItemStack heldItem = getSyncManager().getCursorItem();
+ if (heldItem.isEmpty()) return ItemStack.EMPTY;
+
+ ItemStack fluidContainer = fluidHandler.getContainer();
+ int filled = fluidHandler.fill(tankFluid, false);
+ if (filled > 0) {
+ tank.drain(filled, true);
+ fluidHandler.fill(tankFluid, true);
+ if (tryFillAll) {
+ // Determine how many more items we can fill. One item is already filled.
+ // Integer division means it will round down, so it will only fill equivalent fluid amounts.
+ // For example:
+ // Click with 3 cells, with 2500L of fluid in the tank.
+ // 2 cells will be filled, and 500L will be left behind in the tank.
+ int additional = Math.min(heldItem.getCount(), tankFluid.amount / filled) - 1;
+ tank.drain(filled * additional, true);
+ fluidContainer.grow(additional);
+ }
+ replaceCursorItemStack(fluidContainer);
+ playSound(tankFluid, false);
+ return fluidContainer;
+ }
+ return ItemStack.EMPTY;
+ }
+
+ /**
+ * Replace the ItemStack on the player's cursor with the passed stack. Use to replace empty cells with filled, or
+ * filled cells with empty. If it is not fully emptied/filled, it will place the new items into the player inventory
+ * instead, and shrink the held stack by the appropriate amount.
+ */
+ private void replaceCursorItemStack(ItemStack resultStack) {
+ int resultStackSize = resultStack.getMaxStackSize();
+ ItemStack playerStack = getSyncManager().getCursorItem();
+
+ if (!getSyncManager().isClient())
+ syncToClient(TRY_CLICK_CONTAINER, buffer -> NetworkUtils.writeItemStack(buffer, resultStack));
+
+ while (resultStack.getCount() > resultStackSize) {
+ playerStack.shrink(resultStackSize);
+ addItemToPlayerInventory(resultStack.splitStack(resultStackSize));
+ }
+ if (playerStack.getCount() == resultStack.getCount()) {
+ // every item on the cursor is mutated, so leave it there
+ getSyncManager().setCursorItem(resultStack);
+ } else {
+ // some items not mutated. Mutated items go into the inventory/world.
+ playerStack.shrink(resultStack.getCount());
+ getSyncManager().setCursorItem(playerStack);
+ addItemToPlayerInventory(resultStack);
+ }
+ }
+
+ /** Place an item into the player's inventory, or drop it in-world as an item entity if it cannot fit. */
+ private void addItemToPlayerInventory(ItemStack stack) {
+ if (stack == null) return;
+ var player = getSyncManager().getPlayer();
+
+ if (!player.inventory.addItemStackToInventory(stack) && !player.world.isRemote) {
+ EntityItem dropItem = player.entityDropItem(stack, 0);
+ if (dropItem != null) dropItem.setPickupDelay(0);
+ }
+ }
+
+ /**
+ * Play the appropriate fluid interaction sound for the fluid.
+ * Must be called on server to work correctly
+ **/
+ private void playSound(FluidStack fluid, boolean fill) {
+ if (fluid == null) return;
+ SoundEvent soundEvent;
+ if (fill) {
+ soundEvent = fluid.getFluid().getFillSound(fluid);
+ } else {
+ soundEvent = fluid.getFluid().getEmptySound(fluid);
+ }
+ EntityPlayer player = getSyncManager().getPlayer();
+ player.world.playSound(null, player.posX, player.posY + 0.5, player.posZ,
+ soundEvent, SoundCategory.PLAYERS, 1.0F, 1.0F);
+ }
+}
diff --git a/src/main/java/gregtech/api/util/FluidTankSwitchShim.java b/src/main/java/gregtech/api/util/FluidTankSwitchShim.java
index e0210eee145..0e4c556da3a 100644
--- a/src/main/java/gregtech/api/util/FluidTankSwitchShim.java
+++ b/src/main/java/gregtech/api/util/FluidTankSwitchShim.java
@@ -11,14 +11,17 @@
// probably causes problems
public class FluidTankSwitchShim implements IFluidTank, IFluidHandler {
- IFluidTank tank;
+ @Nullable
+ private IFluidTank tank;
+ private static final FluidTankInfo NO_INFO = new FluidTankInfo(null, 0);
+ private static final IFluidTankProperties[] NO_PROPS = new IFluidTankProperties[0];
public FluidTankSwitchShim(IFluidTank tank) {
changeTank(tank);
}
public void changeTank(IFluidTank tank) {
- if (!(tank instanceof IFluidHandler)) {
+ if (tank != null && !(tank instanceof IFluidHandler)) {
throw new IllegalArgumentException("Shim tank must be both IFluidTank and IFluidHandler!");
}
this.tank = tank;
@@ -27,43 +30,49 @@ public void changeTank(IFluidTank tank) {
@Nullable
@Override
public FluidStack getFluid() {
- return tank.getFluid();
+ return tank == null ? null : tank.getFluid();
}
@Override
public int getFluidAmount() {
- return tank.getFluidAmount();
+ return tank == null ? 0 : tank.getFluidAmount();
}
@Override
public int getCapacity() {
- return tank.getCapacity();
+ return tank == null ? 0 : tank.getCapacity();
}
@Override
public FluidTankInfo getInfo() {
- return tank.getInfo();
+ return tank == null ? NO_INFO : tank.getInfo();
}
@Override
public IFluidTankProperties[] getTankProperties() {
+ if (tank == null)
+ return NO_PROPS;
+
return ((IFluidHandler) tank).getTankProperties();
}
@Override
public int fill(FluidStack resource, boolean doFill) {
+ if (tank == null) return 0;
return ((IFluidHandler) tank).fill(resource, doFill);
}
@Nullable
@Override
public FluidStack drain(FluidStack resource, boolean doDrain) {
+ if (tank == null) return null;
return ((IFluidHandler) tank).drain(resource, doDrain);
}
@Nullable
@Override
public FluidStack drain(int maxDrain, boolean doDrain) {
+ if (tank == null) return null;
return tank.drain(maxDrain, doDrain);
}
}
diff --git a/src/main/java/gregtech/api/util/VirtualTankRegistry.java b/src/main/java/gregtech/api/util/VirtualTankRegistry.java
deleted file mode 100644
index f43809d7eb3..00000000000
--- a/src/main/java/gregtech/api/util/VirtualTankRegistry.java
+++ /dev/null
@@ -1,338 +0,0 @@
-package gregtech.api.util;
-
-import gregtech.api.GTValues;
-
-import net.minecraft.nbt.NBTTagCompound;
-import net.minecraft.world.World;
-import net.minecraft.world.storage.MapStorage;
-import net.minecraft.world.storage.WorldSavedData;
-import net.minecraftforge.fluids.FluidStack;
-import net.minecraftforge.fluids.FluidTankInfo;
-import net.minecraftforge.fluids.IFluidTank;
-import net.minecraftforge.fluids.capability.IFluidHandler;
-import net.minecraftforge.fluids.capability.IFluidTankProperties;
-
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-public class VirtualTankRegistry extends WorldSavedData {
-
- private static final int DEFAULT_CAPACITY = 64000; // 64B
- private static final String DATA_ID = GTValues.MODID + ".vtank_data";
-
- protected static Map> tankMap = new HashMap<>();
-
- public VirtualTankRegistry() {
- super(DATA_ID);
- }
-
- // for some reason, MapStorage throws an error if this constructor is not present
- @SuppressWarnings("unused")
- public VirtualTankRegistry(String name) {
- super(name);
- }
-
- /**
- * Retrieves a tank from the registry
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- * @return The tank object
- */
- public static IFluidTank getTank(String key, UUID uuid) {
- return tankMap.get(uuid).get(key);
- }
-
- /**
- * @return the internal Map of tanks.
- * Do not use to modify the map!
- */
- public static Map> getTankMap() {
- return tankMap;
- }
-
- /**
- * Retrieves a tank from the registry, creating it if it does not exist
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- * @param capacity The initial capacity of the tank
- * @return The tank object
- */
- public static IFluidTank getTankCreate(String key, UUID uuid, int capacity) {
- if (!tankMap.containsKey(uuid) || !tankMap.get(uuid).containsKey(key)) {
- addTank(key, uuid, capacity);
- }
- return getTank(key, uuid);
- }
-
- /**
- * Retrieves a tank from the registry, creating it with {@link #DEFAULT_CAPACITY the default capacity} if it does
- * not exist
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- * @return The tank object
- */
- public static IFluidTank getTankCreate(String key, UUID uuid) {
- return getTankCreate(key, uuid, DEFAULT_CAPACITY);
- }
-
- /**
- * Adds a tank to the registry
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- * @param capacity The initial capacity of the tank
- */
- public static void addTank(String key, UUID uuid, int capacity) {
- if (tankMap.containsKey(uuid) && tankMap.get(uuid).containsKey(key)) {
- GTLog.logger.warn("Overwriting virtual tank " + key + "/" + (uuid == null ? "null" : uuid.toString()) +
- ", this might cause fluid loss!");
- } else if (!tankMap.containsKey(uuid)) {
- tankMap.put(uuid, new HashMap<>());
- }
- tankMap.get(uuid).put(key, new VirtualTank(capacity));
- }
-
- /**
- * Adds a tank to the registry with {@link #DEFAULT_CAPACITY the default capacity}
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- */
- public static void addTank(String key, UUID uuid) {
- addTank(key, uuid, DEFAULT_CAPACITY);
- }
-
- /**
- * Removes a tank from the registry. Use with caution!
- *
- * @param key The name of the tank
- * @param uuid The uuid of the player the tank is private to, or null if the tank is public
- * @param removeFluid Whether to remove the tank if it has fluid in it
- */
- public static void delTank(String key, UUID uuid, boolean removeFluid) {
- if (tankMap.containsKey(uuid) && tankMap.get(uuid).containsKey(key)) {
- if (removeFluid || tankMap.get(uuid).get(key).getFluidAmount() <= 0) {
- tankMap.get(uuid).remove(key);
- if (tankMap.get(uuid).size() == 0) {
- tankMap.remove(uuid);
- }
- }
- } else {
- GTLog.logger.warn("Attempted to delete tank " + key + "/" + (uuid == null ? "null" : uuid.toString()) +
- ", which does not exist!");
- }
- }
-
- /**
- * To be called on server stopped event
- */
- public static void clearMaps() {
- tankMap.clear();
- }
-
- @Override
- public void readFromNBT(NBTTagCompound nbt) {
- if (nbt.hasKey("Public")) {
- NBTTagCompound publicTanks = nbt.getCompoundTag("Public");
- for (String key : publicTanks.getKeySet()) {
- NBTTagCompound tankCompound = publicTanks.getCompoundTag(key);
- VirtualTankRegistry.addTank(key, null, tankCompound.getInteger("Capacity"));
- if (!tankCompound.hasKey("Empty")) {
- VirtualTankRegistry.getTank(key, null).fill(FluidStack.loadFluidStackFromNBT(tankCompound), true);
- }
- }
- }
- if (nbt.hasKey("Private")) {
- NBTTagCompound privateTankUUIDs = nbt.getCompoundTag("Private");
- for (String uuidStr : privateTankUUIDs.getKeySet()) {
- UUID uuid = UUID.fromString(uuidStr);
- NBTTagCompound privateTanks = privateTankUUIDs.getCompoundTag(uuidStr);
- for (String key : privateTanks.getKeySet()) {
- NBTTagCompound tankCompound = privateTanks.getCompoundTag(key);
- VirtualTankRegistry.addTank(key, uuid, tankCompound.getInteger("Capacity"));
- if (!tankCompound.hasKey("Empty")) {
- VirtualTankRegistry.getTank(key, uuid).fill(FluidStack.loadFluidStackFromNBT(tankCompound),
- true);
- }
- }
- }
- }
- }
-
- @NotNull
- @Override
- public NBTTagCompound writeToNBT(NBTTagCompound compound) {
- compound.setTag("Private", new NBTTagCompound());
- tankMap.forEach((uuid, map) -> {
- NBTTagCompound mapCompound = new NBTTagCompound();
- map.forEach((key, tank) -> {
- if (tank.getFluid() != null || tank.getCapacity() != DEFAULT_CAPACITY) {
- NBTTagCompound tankCompound = new NBTTagCompound();
- tankCompound.setInteger("Capacity", tank.getCapacity());
- if (tank.getFluid() != null) {
- tank.getFluid().writeToNBT(tankCompound);
- } else {
- tankCompound.setString("Empty", "");
- }
- mapCompound.setTag(key, tankCompound);
- }
- });
- if (mapCompound.getSize() > 0) {
- if (uuid == null) {
- compound.setTag("Public", mapCompound);
- } else {
- compound.getCompoundTag("Private").setTag(uuid.toString(), mapCompound);
- }
- }
- });
- return compound;
- }
-
- @Override
- public boolean isDirty() {
- // can't think of a good way to mark dirty other than always
- return true;
- }
-
- /**
- * To be called on world load event
- */
- public static void initializeStorage(World world) {
- MapStorage storage = world.getMapStorage();
- VirtualTankRegistry instance = (VirtualTankRegistry) storage.getOrLoadData(VirtualTankRegistry.class, DATA_ID);
-
- if (instance == null) {
- instance = new VirtualTankRegistry();
- storage.setData(DATA_ID, instance);
- }
- }
-
- private static class VirtualTank implements IFluidTank, IFluidHandler {
-
- @Nullable
- protected FluidStack fluid;
- protected int capacity;
- protected IFluidTankProperties[] tankProperties;
-
- public VirtualTank(int capacity) {
- this.capacity = capacity;
- }
-
- @Nullable
- @Override
- public FluidStack getFluid() {
- return this.fluid;
- }
-
- @Override
- public int getFluidAmount() {
- return this.fluid == null ? 0 : this.fluid.amount;
- }
-
- @Override
- public int getCapacity() {
- return this.capacity;
- }
-
- @Override
- public FluidTankInfo getInfo() {
- return new FluidTankInfo(this);
- }
-
- @Override
- public IFluidTankProperties[] getTankProperties() {
- if (this.tankProperties == null) {
- this.tankProperties = new IFluidTankProperties[] { new VirtualTankProperties(this) };
- }
- return this.tankProperties;
- }
-
- @Override
- public int fill(FluidStack fluidStack, boolean doFill) {
- if (fluidStack == null || fluidStack.amount <= 0 ||
- (this.fluid != null && !fluidStack.isFluidEqual(this.fluid)))
- return 0;
-
- int fillAmt = Math.min(fluidStack.amount, this.capacity - this.getFluidAmount());
- if (doFill) {
- if (this.fluid == null) {
- this.fluid = new FluidStack(fluidStack, fillAmt);
- } else {
- this.fluid.amount += fillAmt;
- }
- }
- return fillAmt;
- }
-
- @Nullable
- @Override
- public FluidStack drain(FluidStack resource, boolean doDrain) {
- return resource == null || !resource.isFluidEqual(this.fluid) ? null : drain(resource.amount, doDrain);
- }
-
- @Nullable
- @Override
- public FluidStack drain(int amount, boolean doDrain) {
- if (this.fluid == null || amount <= 0)
- return null;
-
- int drainAmt = Math.min(this.getFluidAmount(), amount);
- FluidStack drainedFluid = new FluidStack(fluid, drainAmt);
- if (doDrain) {
- this.fluid.amount -= drainAmt;
- if (this.fluid.amount <= 0) {
- this.fluid = null;
- }
- }
- return drainedFluid;
- }
-
- private static class VirtualTankProperties implements IFluidTankProperties {
-
- protected final VirtualTank tank;
-
- private VirtualTankProperties(VirtualTank tank) {
- this.tank = tank;
- }
-
- @Nullable
- @Override
- public FluidStack getContents() {
- FluidStack contents = tank.getFluid();
- return contents == null ? null : contents.copy();
- }
-
- @Override
- public int getCapacity() {
- return tank.getCapacity();
- }
-
- @Override
- public boolean canFill() {
- return true;
- }
-
- @Override
- public boolean canDrain() {
- return true;
- }
-
- @Override
- public boolean canFillFluidType(FluidStack fluidStack) {
- return true;
- }
-
- @Override
- public boolean canDrainFluidType(FluidStack fluidStack) {
- return true;
- }
- }
- }
-}
diff --git a/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java b/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java
new file mode 100644
index 00000000000..6324958b8c7
--- /dev/null
+++ b/src/main/java/gregtech/api/util/virtualregistry/EntryTypes.java
@@ -0,0 +1,66 @@
+package gregtech.api.util.virtualregistry;
+
+import gregtech.api.util.GTLog;
+import gregtech.api.util.virtualregistry.entries.VirtualTank;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.ResourceLocation;
+
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+import java.util.function.Supplier;
+
+import static gregtech.api.util.GTUtility.gregtechId;
+
+public final class EntryTypes {
+
+ private static final Map> TYPES_MAP = new Object2ObjectOpenHashMap<>();
+ public static final EntryTypes ENDER_FLUID = addEntryType(gregtechId("ender_fluid"), VirtualTank::new);
+ // ENDER_ITEM("ender_item", null),
+ // ENDER_ENERGY("ender_energy", null),
+ // ENDER_REDSTONE("ender_redstone", null);
+ private final ResourceLocation location;
+ private final Supplier factory;
+
+ private EntryTypes(ResourceLocation location, Supplier supplier) {
+ this.location = location;
+ this.factory = supplier;
+ }
+
+ public T createInstance(NBTTagCompound nbt) {
+ var entry = createInstance();
+ entry.deserializeNBT(nbt);
+ return entry;
+ }
+
+ public T createInstance() {
+ return factory.get();
+ }
+
+ @Override
+ public String toString() {
+ return this.location.toString();
+ }
+
+ @Nullable
+ public static EntryTypes extends VirtualEntry> fromString(String name) {
+ return TYPES_MAP.getOrDefault(gregtechId(name), null);
+ }
+
+ @Nullable
+ public static EntryTypes extends VirtualEntry> fromLocation(String location) {
+ return TYPES_MAP.getOrDefault(new ResourceLocation(location), null);
+ }
+
+ public static EntryTypes addEntryType(ResourceLocation location, Supplier supplier) {
+ var type = new EntryTypes<>(location, supplier);
+ if (!TYPES_MAP.containsKey(location)) {
+ TYPES_MAP.put(location, type);
+ } else {
+ GTLog.logger.warn("Entry \"{}\" is already registered!", location);
+ }
+ return type;
+ }
+}
diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java
new file mode 100644
index 00000000000..3d8ccd78224
--- /dev/null
+++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualEnderRegistry.java
@@ -0,0 +1,175 @@
+package gregtech.api.util.virtualregistry;
+
+import gregtech.api.GTValues;
+import gregtech.api.util.GTLog;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.world.World;
+import net.minecraft.world.storage.MapStorage;
+import net.minecraft.world.storage.WorldSavedData;
+import net.minecraftforge.fluids.IFluidTank;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Predicate;
+
+@SuppressWarnings("SameParameterValue")
+public class VirtualEnderRegistry extends WorldSavedData {
+
+ private static final String DATA_ID = GTValues.MODID + ".virtual_entry_data";
+ private static final String OLD_DATA_ID = GTValues.MODID + ".vtank_data";
+ private static final String PUBLIC_KEY = "Public";
+ private static final String PRIVATE_KEY = "Private";
+ private static final Map VIRTUAL_REGISTRIES = new HashMap<>();
+
+ public VirtualEnderRegistry(String name) {
+ super(name);
+ }
+
+ public static T getEntry(@Nullable UUID owner, EntryTypes type, String name) {
+ return getRegistry(owner).getEntry(type, name);
+ }
+
+ public static void addEntry(@Nullable UUID owner, String name, VirtualEntry entry) {
+ getRegistry(owner).addEntry(name, entry);
+ }
+
+ public static boolean hasEntry(@Nullable UUID owner, EntryTypes> type, String name) {
+ return getRegistry(owner).contains(type, name);
+ }
+
+ public static @NotNull T getOrCreateEntry(@Nullable UUID owner, EntryTypes type,
+ String name) {
+ if (!hasEntry(owner, type, name))
+ addEntry(owner, name, type.createInstance());
+
+ return getEntry(owner, type, name);
+ }
+
+ /**
+ * Removes an entry from the registry. Use with caution!
+ *
+ * @param owner The uuid of the player the entry is private to, or null if the entry is public
+ * @param type Type of the registry to remove from
+ * @param name The name of the entry
+ */
+ public static void deleteEntry(@Nullable UUID owner, EntryTypes> type, String name) {
+ var registry = getRegistry(owner);
+ if (registry.contains(type, name)) {
+ registry.deleteEntry(type, name);
+ return;
+ }
+ GTLog.logger.warn("Attempted to delete {} entry {} of type {}, which does not exist",
+ owner == null ? "public" : String.format("private [%s]", owner), name, type);
+ }
+
+ public static void deleteEntry(@Nullable UUID owner, EntryTypes type, String name,
+ Predicate shouldDelete) {
+ T entry = getEntry(owner, type, name);
+ if (entry != null && shouldDelete.test(entry))
+ deleteEntry(owner, type, name);
+ }
+
+ public static Set getEntryNames(UUID owner, EntryTypes> type) {
+ return getRegistry(owner).getEntryNames(type);
+ }
+
+ /**
+ * To be called on server stopped event
+ */
+ public static void clearMaps() {
+ VIRTUAL_REGISTRIES.clear();
+ }
+
+ private static VirtualRegistryMap getRegistry(UUID owner) {
+ return VIRTUAL_REGISTRIES.computeIfAbsent(owner, key -> new VirtualRegistryMap());
+ }
+
+ // remove if tank app is removed
+ public static Map> createTankMap() {
+ Map> map = new HashMap<>();
+ for (var uuid : VIRTUAL_REGISTRIES.keySet()) {
+ map.put(uuid, new HashMap<>());
+ for (var name : getEntryNames(uuid, EntryTypes.ENDER_FLUID)) {
+ map.get(uuid).put(name, getEntry(uuid, EntryTypes.ENDER_FLUID, name));
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public final void readFromNBT(NBTTagCompound nbt) {
+ if (nbt.hasKey(PUBLIC_KEY)) {
+ VIRTUAL_REGISTRIES.put(null, new VirtualRegistryMap(nbt.getCompoundTag(PUBLIC_KEY)));
+ }
+ if (nbt.hasKey(PRIVATE_KEY)) {
+ NBTTagCompound privateEntries = nbt.getCompoundTag(PRIVATE_KEY);
+ for (String owner : privateEntries.getKeySet()) {
+ var privateMap = privateEntries.getCompoundTag(owner);
+ VIRTUAL_REGISTRIES.put(UUID.fromString(owner), new VirtualRegistryMap(privateMap));
+ }
+ }
+ }
+
+ @NotNull
+ @Override
+ public final NBTTagCompound writeToNBT(@NotNull NBTTagCompound tag) {
+ var privateTag = new NBTTagCompound();
+ for (var owner : VIRTUAL_REGISTRIES.keySet()) {
+ var mapTag = VIRTUAL_REGISTRIES.get(owner).serializeNBT();
+ if (owner != null) {
+ privateTag.setTag(owner.toString(), mapTag);
+ } else {
+ tag.setTag(PUBLIC_KEY, mapTag);
+ }
+ }
+ tag.setTag(PRIVATE_KEY, privateTag);
+ return tag;
+ }
+
+ @Override
+ public boolean isDirty() {
+ // can't think of a good way to mark dirty other than always
+ return true;
+ }
+
+ /**
+ * To be called on world load event
+ */
+ @SuppressWarnings("DataFlowIssue")
+ public static void initializeStorage(World world) {
+ MapStorage storage = world.getMapStorage();
+
+ VirtualEnderRegistry instance = (VirtualEnderRegistry) storage.getOrLoadData(VirtualEnderRegistry.class,
+ DATA_ID);
+ VirtualEnderRegistry old = (VirtualEnderRegistry) storage.getOrLoadData(VirtualEnderRegistry.class,
+ OLD_DATA_ID);
+
+ if (instance == null) {
+ instance = new VirtualEnderRegistry(DATA_ID);
+ storage.setData(DATA_ID, instance);
+ }
+
+ if (old != null) {
+ instance.readFromNBT(old.serializeNBT());
+ var file = world.getSaveHandler().getMapFileFromName(OLD_DATA_ID);
+ var split = file.getName().split("\\.");
+ var stringBuilder = new StringBuilder(split[0])
+ .append('.')
+ .append(split[1])
+ .append(".backup")
+ .append('.')
+ .append(split[2]);
+ if (file.renameTo(new File(file.getParent(), stringBuilder.toString()))) {
+ file.deleteOnExit();
+ GTLog.logger.warn("Moved Virtual Tank Data to new format, created backup!");
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java
new file mode 100644
index 00000000000..2775069f0e3
--- /dev/null
+++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualEntry.java
@@ -0,0 +1,79 @@
+package gregtech.api.util.virtualregistry;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.INBTSerializable;
+
+import org.jetbrains.annotations.NotNull;
+
+public abstract class VirtualEntry implements INBTSerializable {
+
+ public static final String DEFAULT_COLOR = "FFFFFFFF";
+ protected static final String COLOR_KEY = "color";
+ protected static final String DESC_KEY = "description";
+
+ private int color = 0xFFFFFFFF;
+ private String colorStr = DEFAULT_COLOR;
+ private @NotNull String description = "";
+
+ public abstract EntryTypes extends VirtualEntry> getType();
+
+ public String getColorStr() {
+ return colorStr;
+ }
+
+ public int getColor() {
+ return this.color;
+ }
+
+ public void setColor(String color) {
+ this.color = parseColor(color);
+ this.colorStr = color.toUpperCase();
+ }
+
+ public void setColor(int color) {
+ setColor(Integer.toHexString(color));
+ }
+
+ private int parseColor(String s) {
+ // stupid java not having actual unsigned ints
+ long tmp = Long.parseLong(s, 16);
+ if (tmp > 0x7FFFFFFF) {
+ tmp -= 0x100000000L;
+ }
+ return (int) tmp;
+ }
+
+ public @NotNull String getDescription() {
+ return this.description;
+ }
+
+ public void setDescription(@NotNull String desc) {
+ this.description = desc;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VirtualEntry other)) return false;
+ return this.getType() == other.getType() &&
+ this.color == other.color;
+ }
+
+ @Override
+ public NBTTagCompound serializeNBT() {
+ var tag = new NBTTagCompound();
+ tag.setString(COLOR_KEY, this.colorStr);
+
+ if (description != null && !description.isEmpty())
+ tag.setString(DESC_KEY, this.description);
+
+ return tag;
+ }
+
+ @Override
+ public void deserializeNBT(NBTTagCompound nbt) {
+ setColor(nbt.getString(COLOR_KEY));
+
+ if (nbt.hasKey(DESC_KEY))
+ setDescription(nbt.getString(DESC_KEY));
+ }
+}
diff --git a/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java b/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java
new file mode 100644
index 00000000000..18a2b9a81e8
--- /dev/null
+++ b/src/main/java/gregtech/api/util/virtualregistry/VirtualRegistryMap.java
@@ -0,0 +1,87 @@
+package gregtech.api.util.virtualregistry;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.common.util.INBTSerializable;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class VirtualRegistryMap implements INBTSerializable {
+
+ private final Map, Map> registryMap = new HashMap<>();
+
+ public VirtualRegistryMap(NBTTagCompound tag) {
+ deserializeNBT(tag);
+ }
+
+ public VirtualRegistryMap() {}
+
+ @SuppressWarnings("unchecked")
+ public @Nullable T getEntry(EntryTypes type, String name) {
+ if (!contains(type, name))
+ return null;
+
+ return (T) registryMap.get(type).get(name);
+ }
+
+ public void addEntry(String name, VirtualEntry entry) {
+ registryMap.computeIfAbsent(entry.getType(), k -> new HashMap<>())
+ .put(name, entry);
+ }
+
+ public boolean contains(EntryTypes> type, String name) {
+ if (!registryMap.containsKey(type))
+ return false;
+
+ return registryMap.get(type).containsKey(name);
+ }
+
+ public void deleteEntry(EntryTypes> type, String name) {
+ registryMap.get(type).remove(name);
+ }
+
+ public void clear() {
+ registryMap.clear();
+ }
+
+ public Set getEntryNames(EntryTypes> type) {
+ return registryMap.get(type).keySet();
+ }
+
+ @Override
+ public @NotNull NBTTagCompound serializeNBT() {
+ var tag = new NBTTagCompound();
+ for (var type : registryMap.keySet()) {
+ var entriesTag = new NBTTagCompound();
+ var entries = registryMap.get(type);
+ for (var name : entries.keySet()) {
+ entriesTag.setTag(name, entries.get(name).serializeNBT());
+ }
+ tag.setTag(type.toString(), entriesTag);
+ }
+ return tag;
+ }
+
+ @Override
+ public void deserializeNBT(NBTTagCompound nbt) {
+ for (var entryType : nbt.getKeySet()) {
+ EntryTypes> type;
+ if (entryType.contains(":")) {
+ type = EntryTypes.fromLocation(entryType);
+ } else {
+ type = EntryTypes.fromString(entryType);
+ }
+ if (type == null) continue;
+
+ var virtualEntries = nbt.getCompoundTag(entryType);
+ for (var name : virtualEntries.getKeySet()) {
+ var entry = virtualEntries.getCompoundTag(name);
+ addEntry(name, type.createInstance(entry));
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java b/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java
new file mode 100644
index 00000000000..3ad02e5969c
--- /dev/null
+++ b/src/main/java/gregtech/api/util/virtualregistry/entries/VirtualTank.java
@@ -0,0 +1,180 @@
+package gregtech.api.util.virtualregistry.entries;
+
+import gregtech.api.util.virtualregistry.EntryTypes;
+import gregtech.api.util.virtualregistry.VirtualEntry;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidTank;
+import net.minecraftforge.fluids.capability.IFluidHandler;
+import net.minecraftforge.fluids.capability.IFluidTankProperties;
+
+import org.jetbrains.annotations.Nullable;
+
+public class VirtualTank extends VirtualEntry implements IFluidTank, IFluidHandler {
+
+ protected static final String CAPACITY_KEY = "capacity";
+ protected static final String FLUID_KEY = "fluid";
+ private static final int DEFAULT_CAPACITY = 64000; // 64B
+
+ @Nullable
+ private FluidStack fluidStack = null;
+ private int capacity;
+ private final IFluidTankProperties[] props = new IFluidTankProperties[] {
+ createProperty(this)
+ };
+
+ public VirtualTank(int capacity) {
+ this.capacity = capacity;
+ }
+
+ public VirtualTank() {
+ this(DEFAULT_CAPACITY);
+ }
+
+ @Override
+ public EntryTypes getType() {
+ return EntryTypes.ENDER_FLUID;
+ }
+
+ @Override
+ public FluidStack getFluid() {
+ return this.fluidStack;
+ }
+
+ public void setFluid(FluidStack fluid) {
+ this.fluidStack = fluid;
+ }
+
+ @Override
+ public int getFluidAmount() {
+ return fluidStack == null ? 0 : fluidStack.amount;
+ }
+
+ @Override
+ public int getCapacity() {
+ return this.capacity;
+ }
+
+ @Override
+ public FluidTankInfo getInfo() {
+ return new FluidTankInfo(this);
+ }
+
+ @Override
+ public IFluidTankProperties[] getTankProperties() {
+ return this.props;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof VirtualTank other)) return false;
+ if (this.fluidStack == null && other.fluidStack == null)
+ return super.equals(o);
+ if (this.fluidStack == null || other.fluidStack == null)
+ return false;
+ if (this.fluidStack.isFluidStackIdentical(other.fluidStack))
+ return super.equals(o);
+
+ return false;
+ }
+
+ @Override
+ public NBTTagCompound serializeNBT() {
+ var tag = super.serializeNBT();
+ tag.setInteger(CAPACITY_KEY, this.capacity);
+
+ if (this.fluidStack != null)
+ tag.setTag(FLUID_KEY, this.fluidStack.writeToNBT(new NBTTagCompound()));
+
+ return tag;
+ }
+
+ @Override
+ public void deserializeNBT(NBTTagCompound nbt) {
+ super.deserializeNBT(nbt);
+ this.capacity = nbt.getInteger(CAPACITY_KEY);
+
+ if (nbt.hasKey(FLUID_KEY))
+ setFluid(FluidStack.loadFluidStackFromNBT(nbt.getCompoundTag(FLUID_KEY)));
+ }
+
+ @Override
+ public int fill(FluidStack fluidStack, boolean doFill) {
+ if (fluidStack == null || fluidStack.amount <= 0 ||
+ (this.fluidStack != null && !fluidStack.isFluidEqual(this.fluidStack)))
+ return 0;
+
+ int fillAmt = Math.min(fluidStack.amount, getCapacity() - this.getFluidAmount());
+
+ if (doFill) {
+ if (this.fluidStack == null) {
+ this.fluidStack = new FluidStack(fluidStack, fillAmt);
+ } else {
+ this.fluidStack.amount += fillAmt;
+ }
+ }
+ return fillAmt;
+ }
+
+ @Nullable
+ @Override
+ public FluidStack drain(FluidStack resource, boolean doDrain) {
+ return resource == null || !resource.isFluidEqual(this.fluidStack) ? null : drain(resource.amount, doDrain);
+ }
+
+ @Nullable
+ @Override
+ public FluidStack drain(int amount, boolean doDrain) {
+ if (this.fluidStack == null || amount <= 0)
+ return null;
+
+ int drainAmt = Math.min(this.getFluidAmount(), amount);
+ FluidStack drainedFluid = new FluidStack(this.fluidStack, drainAmt);
+ if (doDrain) {
+ this.fluidStack.amount -= drainAmt;
+ if (this.fluidStack.amount <= 0) {
+ this.fluidStack = null;
+ }
+ }
+ return drainedFluid;
+ }
+
+ private static IFluidTankProperties createProperty(VirtualTank tank) {
+ return new IFluidTankProperties() {
+
+ @Nullable
+ @Override
+ public FluidStack getContents() {
+ FluidStack contents = tank.getFluid();
+ return contents == null ? null : contents.copy();
+ }
+
+ @Override
+ public int getCapacity() {
+ return tank.getCapacity();
+ }
+
+ @Override
+ public boolean canFill() {
+ return true;
+ }
+
+ @Override
+ public boolean canDrain() {
+ return true;
+ }
+
+ @Override
+ public boolean canFillFluidType(FluidStack fluidStack) {
+ return true;
+ }
+
+ @Override
+ public boolean canDrainFluidType(FluidStack fluidStack) {
+ return true;
+ }
+ };
+ }
+}
diff --git a/src/main/java/gregtech/common/EventHandlers.java b/src/main/java/gregtech/common/EventHandlers.java
index 8fa23091b99..ecbe6b16097 100644
--- a/src/main/java/gregtech/common/EventHandlers.java
+++ b/src/main/java/gregtech/common/EventHandlers.java
@@ -13,7 +13,7 @@
import gregtech.api.util.CapesRegistry;
import gregtech.api.util.GTUtility;
import gregtech.api.util.Mods;
-import gregtech.api.util.VirtualTankRegistry;
+import gregtech.api.util.virtualregistry.VirtualEnderRegistry;
import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinSaveData;
import gregtech.common.entities.EntityGTExplosive;
import gregtech.common.items.MetaItems;
@@ -327,7 +327,7 @@ public static void onPlayerTickClient(TickEvent.PlayerTickEvent event) {
@SubscribeEvent
public static void onWorldLoadEvent(WorldEvent.Load event) {
- VirtualTankRegistry.initializeStorage(event.getWorld());
+ VirtualEnderRegistry.initializeStorage(event.getWorld());
CapesRegistry.checkAdvancements(event.getWorld());
}
diff --git a/src/main/java/gregtech/common/covers/CoverBehaviors.java b/src/main/java/gregtech/common/covers/CoverBehaviors.java
index 78413327163..355fe3b0dff 100644
--- a/src/main/java/gregtech/common/covers/CoverBehaviors.java
+++ b/src/main/java/gregtech/common/covers/CoverBehaviors.java
@@ -8,6 +8,7 @@
import gregtech.api.util.GTLog;
import gregtech.client.renderer.texture.Textures;
import gregtech.common.covers.detector.*;
+import gregtech.common.covers.ender.CoverEnderFluidLink;
import gregtech.common.items.MetaItems;
import gregtech.common.items.behaviors.CoverDigitalInterfaceWirelessPlaceBehaviour;
diff --git a/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java b/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java
deleted file mode 100644
index a674b6fbd54..00000000000
--- a/src/main/java/gregtech/common/covers/CoverEnderFluidLink.java
+++ /dev/null
@@ -1,348 +0,0 @@
-package gregtech.common.covers;
-
-import gregtech.api.capability.GregtechTileCapabilities;
-import gregtech.api.capability.IControllable;
-import gregtech.api.cover.CoverBase;
-import gregtech.api.cover.CoverDefinition;
-import gregtech.api.cover.CoverWithUI;
-import gregtech.api.cover.CoverableView;
-import gregtech.api.mui.GTGuiTextures;
-import gregtech.api.mui.GTGuis;
-import gregtech.api.util.FluidTankSwitchShim;
-import gregtech.api.util.GTTransferUtils;
-import gregtech.api.util.VirtualTankRegistry;
-import gregtech.client.renderer.texture.Textures;
-import gregtech.common.covers.filter.FluidFilterContainer;
-
-import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.entity.player.EntityPlayerMP;
-import net.minecraft.item.ItemStack;
-import net.minecraft.nbt.NBTTagCompound;
-import net.minecraft.network.PacketBuffer;
-import net.minecraft.util.BlockRenderLayer;
-import net.minecraft.util.EnumActionResult;
-import net.minecraft.util.EnumFacing;
-import net.minecraft.util.EnumHand;
-import net.minecraft.util.ITickable;
-import net.minecraftforge.common.capabilities.Capability;
-import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
-import net.minecraftforge.fluids.capability.IFluidHandler;
-
-import codechicken.lib.raytracer.CuboidRayTraceResult;
-import codechicken.lib.render.CCRenderState;
-import codechicken.lib.render.pipeline.IVertexOperation;
-import codechicken.lib.vec.Cuboid6;
-import codechicken.lib.vec.Matrix4;
-import com.cleanroommc.modularui.api.drawable.IKey;
-import com.cleanroommc.modularui.drawable.DynamicDrawable;
-import com.cleanroommc.modularui.drawable.Rectangle;
-import com.cleanroommc.modularui.factory.SidedPosGuiData;
-import com.cleanroommc.modularui.screen.ModularPanel;
-import com.cleanroommc.modularui.utils.Color;
-import com.cleanroommc.modularui.value.sync.BooleanSyncValue;
-import com.cleanroommc.modularui.value.sync.EnumSyncValue;
-import com.cleanroommc.modularui.value.sync.FluidSlotSyncHandler;
-import com.cleanroommc.modularui.value.sync.PanelSyncManager;
-import com.cleanroommc.modularui.value.sync.StringSyncValue;
-import com.cleanroommc.modularui.widgets.FluidSlot;
-import com.cleanroommc.modularui.widgets.ToggleButton;
-import com.cleanroommc.modularui.widgets.layout.Column;
-import com.cleanroommc.modularui.widgets.layout.Row;
-import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.UUID;
-import java.util.regex.Pattern;
-
-public class CoverEnderFluidLink extends CoverBase implements CoverWithUI, ITickable, IControllable {
-
- public static final int TRANSFER_RATE = 8000; // mB/t
- private static final Pattern COLOR_INPUT_PATTERN = Pattern.compile("[0-9a-fA-F]*");
-
- protected CoverPump.PumpMode pumpMode = CoverPump.PumpMode.IMPORT;
- private int color = 0xFFFFFFFF;
- private UUID playerUUID = null;
- private boolean isPrivate = false;
- private boolean workingEnabled = true;
- private boolean ioEnabled = false;
- private String tempColorStr;
- private boolean isColorTemp;
- private final FluidTankSwitchShim linkedTank;
- protected final FluidFilterContainer fluidFilter;
-
- protected CoverEnderFluidLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView,
- @NotNull EnumFacing attachedSide) {
- super(definition, coverableView, attachedSide);
- this.linkedTank = new FluidTankSwitchShim(VirtualTankRegistry.getTankCreate(makeTankName(), null));
- this.fluidFilter = new FluidFilterContainer(this);
- }
-
- private String makeTankName() {
- return "EFLink#" + Integer.toHexString(this.color).toUpperCase();
- }
-
- private UUID getTankUUID() {
- return isPrivate ? playerUUID : null;
- }
-
- public FluidFilterContainer getFluidFilterContainer() {
- return this.fluidFilter;
- }
-
- public boolean isIOEnabled() {
- return this.ioEnabled;
- }
-
- @Override
- public boolean canAttach(@NotNull CoverableView coverable, @NotNull EnumFacing side) {
- return coverable.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side);
- }
-
- @Override
- public void renderCover(@NotNull CCRenderState renderState, @NotNull Matrix4 translation,
- IVertexOperation[] pipeline, @NotNull Cuboid6 plateBox, @NotNull BlockRenderLayer layer) {
- Textures.ENDER_FLUID_LINK.renderSided(getAttachedSide(), plateBox, renderState, pipeline, translation);
- }
-
- @Override
- public @NotNull EnumActionResult onScrewdriverClick(@NotNull EntityPlayer playerIn, @NotNull EnumHand hand,
- @NotNull CuboidRayTraceResult hitResult) {
- if (!getWorld().isRemote) {
- openUI((EntityPlayerMP) playerIn);
- }
- return EnumActionResult.SUCCESS;
- }
-
- @Override
- public void onAttachment(@NotNull CoverableView coverableView, @NotNull EnumFacing side,
- @Nullable EntityPlayer player, @NotNull ItemStack itemStack) {
- super.onAttachment(coverableView, side, player, itemStack);
- if (player != null) {
- this.playerUUID = player.getUniqueID();
- }
- }
-
- @Override
- public void onRemoval() {
- dropInventoryContents(fluidFilter);
- }
-
- @Override
- public void update() {
- if (workingEnabled && ioEnabled) {
- transferFluids();
- }
- }
-
- protected void transferFluids() {
- IFluidHandler fluidHandler = getCoverableView().getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
- getAttachedSide());
- if (fluidHandler == null) return;
- if (pumpMode == CoverPump.PumpMode.IMPORT) {
- GTTransferUtils.transferFluids(fluidHandler, linkedTank, TRANSFER_RATE, fluidFilter::test);
- } else if (pumpMode == CoverPump.PumpMode.EXPORT) {
- GTTransferUtils.transferFluids(linkedTank, fluidHandler, TRANSFER_RATE, fluidFilter::test);
- }
- }
-
- public void setPumpMode(CoverPump.PumpMode pumpMode) {
- this.pumpMode = pumpMode;
- markDirty();
- }
-
- public CoverPump.PumpMode getPumpMode() {
- return pumpMode;
- }
-
- @Override
- public void openUI(EntityPlayerMP player) {
- CoverWithUI.super.openUI(player);
- isColorTemp = false;
- }
-
- @Override
- public boolean usesMui2() {
- return true;
- }
-
- @Override
- public ModularPanel buildUI(SidedPosGuiData guiData, PanelSyncManager guiSyncManager) {
- var panel = GTGuis.createPanel(this, 176, 192);
-
- getFluidFilterContainer().setMaxTransferSize(1);
-
- return panel.child(CoverWithUI.createTitleRow(getPickItem()))
- .child(createWidgets(panel, guiSyncManager))
- .bindPlayerInventory();
- }
-
- protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) {
- var isPrivate = new BooleanSyncValue(this::isPrivate, this::setPrivate);
- isPrivate.updateCacheFromSource(true);
-
- var color = new StringSyncValue(this::getColorStr, this::updateColor);
- color.updateCacheFromSource(true);
-
- var pumpMode = new EnumSyncValue<>(CoverPump.PumpMode.class, this::getPumpMode, this::setPumpMode);
- syncManager.syncValue("pump_mode", pumpMode);
- pumpMode.updateCacheFromSource(true);
-
- var ioEnabled = new BooleanSyncValue(this::isIOEnabled, this::setIoEnabled);
-
- var fluidTank = new FluidSlotSyncHandler(this.linkedTank);
- fluidTank.updateCacheFromSource(true);
-
- return new Column().coverChildrenHeight().top(24)
- .margin(7, 0).widthRel(1f)
- .child(new Row().marginBottom(2)
- .coverChildrenHeight()
- .child(new ToggleButton()
- .tooltip(tooltip -> tooltip.setAutoUpdate(true))
- .background(GTGuiTextures.PRIVATE_MODE_BUTTON[0])
- .hoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[0])
- .selectedBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1])
- .selectedHoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1])
- .tooltipBuilder(tooltip -> tooltip.addLine(IKey.lang(this.isPrivate ?
- "cover.ender_fluid_link.private.tooltip.enabled" :
- "cover.ender_fluid_link.private.tooltip.disabled")))
- .marginRight(2)
- .value(isPrivate))
- .child(new DynamicDrawable(() -> new Rectangle()
- .setColor(this.color)
- .asIcon().size(16))
- .asWidget()
- .background(GTGuiTextures.SLOT)
- .size(18).marginRight(2))
- .child(new TextFieldWidget().height(18)
- .value(color)
- .setValidator(s -> {
- if (s.length() != 8) {
- return color.getStringValue();
- }
- return s;
- })
- .setPattern(COLOR_INPUT_PATTERN)
- .widthRel(0.5f).marginRight(2))
- .child(new FluidSlot().size(18)
- .syncHandler(fluidTank)))
- .child(new Row().marginBottom(2)
- .coverChildrenHeight()
- .child(new ToggleButton()
- .value(ioEnabled)
- .overlay(IKey.dynamic(() -> IKey.lang(this.ioEnabled ?
- "behaviour.soft_hammer.enabled" :
- "behaviour.soft_hammer.disabled").get())
- .color(Color.WHITE.darker(1)))
- .widthRel(0.6f)
- .left(0)))
- .child(getFluidFilterContainer().initUI(panel, syncManager))
- .child(new EnumRowBuilder<>(CoverPump.PumpMode.class)
- .value(pumpMode)
- .overlay(GTGuiTextures.CONVEYOR_MODE_OVERLAY)
- .lang("cover.pump.mode")
- .build());
- }
-
- public void updateColor(String str) {
- if (str.length() == 8) {
- isColorTemp = false;
- // stupid java not having actual unsigned ints
- long tmp = Long.parseLong(str, 16);
- if (tmp > 0x7FFFFFFF) {
- tmp -= 0x100000000L;
- }
- this.color = (int) tmp;
- updateTankLink();
- } else {
- tempColorStr = str;
- isColorTemp = true;
- }
- }
-
- public String getColorStr() {
- return isColorTemp ? tempColorStr : Integer.toHexString(this.color).toUpperCase();
- }
-
- public void updateTankLink() {
- this.linkedTank.changeTank(VirtualTankRegistry.getTankCreate(makeTankName(), getTankUUID()));
- markDirty();
- }
-
- @Override
- public void writeToNBT(NBTTagCompound tagCompound) {
- super.writeToNBT(tagCompound);
- tagCompound.setInteger("Frequency", color);
- tagCompound.setInteger("PumpMode", pumpMode.ordinal());
- tagCompound.setBoolean("WorkingAllowed", workingEnabled);
- tagCompound.setBoolean("IOAllowed", ioEnabled);
- tagCompound.setBoolean("Private", isPrivate);
- tagCompound.setString("PlacedUUID", playerUUID.toString());
- tagCompound.setTag("Filter", fluidFilter.serializeNBT());
- }
-
- @Override
- public void readFromNBT(NBTTagCompound tagCompound) {
- super.readFromNBT(tagCompound);
- this.color = tagCompound.getInteger("Frequency");
- this.pumpMode = CoverPump.PumpMode.values()[tagCompound.getInteger("PumpMode")];
- this.workingEnabled = tagCompound.getBoolean("WorkingAllowed");
- this.ioEnabled = tagCompound.getBoolean("IOAllowed");
- this.isPrivate = tagCompound.getBoolean("Private");
- this.playerUUID = UUID.fromString(tagCompound.getString("PlacedUUID"));
- this.fluidFilter.deserializeNBT(tagCompound.getCompoundTag("Filter"));
- updateTankLink();
- }
-
- @Override
- public void writeInitialSyncData(PacketBuffer packetBuffer) {
- packetBuffer.writeInt(this.color);
- packetBuffer.writeString(this.playerUUID == null ? "null" : this.playerUUID.toString());
- }
-
- @Override
- public void readInitialSyncData(PacketBuffer packetBuffer) {
- this.color = packetBuffer.readInt();
- // does client even need uuid info? just in case
- String uuidStr = packetBuffer.readString(36);
- this.playerUUID = uuidStr.equals("null") ? null : UUID.fromString(uuidStr);
- // client does not need the actual tank reference, the default one will do just fine
- }
-
- @Override
- public boolean isWorkingEnabled() {
- return workingEnabled;
- }
-
- @Override
- public void setWorkingEnabled(boolean isActivationAllowed) {
- this.workingEnabled = isActivationAllowed;
- }
-
- public T getCapability(Capability capability, T defaultValue) {
- if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
- return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(linkedTank);
- }
- if (capability == GregtechTileCapabilities.CAPABILITY_CONTROLLABLE) {
- return GregtechTileCapabilities.CAPABILITY_CONTROLLABLE.cast(this);
- }
- return defaultValue;
- }
-
- private boolean isIoEnabled() {
- return ioEnabled;
- }
-
- private void setIoEnabled(boolean ioEnabled) {
- this.ioEnabled = ioEnabled;
- }
-
- private boolean isPrivate() {
- return isPrivate;
- }
-
- private void setPrivate(boolean isPrivate) {
- this.isPrivate = isPrivate;
- updateTankLink();
- }
-}
diff --git a/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java b/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java
new file mode 100644
index 00000000000..eb5c6a81d9d
--- /dev/null
+++ b/src/main/java/gregtech/common/covers/ender/CoverAbstractEnderLink.java
@@ -0,0 +1,513 @@
+package gregtech.common.covers.ender;
+
+import gregtech.api.capability.GregtechDataCodes;
+import gregtech.api.capability.IControllable;
+import gregtech.api.cover.CoverBase;
+import gregtech.api.cover.CoverDefinition;
+import gregtech.api.cover.CoverWithUI;
+import gregtech.api.cover.CoverableView;
+import gregtech.api.mui.GTGuiTextures;
+import gregtech.api.mui.GTGuis;
+import gregtech.api.util.virtualregistry.EntryTypes;
+import gregtech.api.util.virtualregistry.VirtualEnderRegistry;
+import gregtech.api.util.virtualregistry.VirtualEntry;
+import gregtech.common.mui.widget.InteractableText;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.ITickable;
+
+import codechicken.lib.raytracer.CuboidRayTraceResult;
+import com.cleanroommc.modularui.api.drawable.IKey;
+import com.cleanroommc.modularui.api.widget.IWidget;
+import com.cleanroommc.modularui.api.widget.Interactable;
+import com.cleanroommc.modularui.drawable.DynamicDrawable;
+import com.cleanroommc.modularui.drawable.GuiTextures;
+import com.cleanroommc.modularui.drawable.Rectangle;
+import com.cleanroommc.modularui.factory.SidedPosGuiData;
+import com.cleanroommc.modularui.network.NetworkUtils;
+import com.cleanroommc.modularui.screen.ModularPanel;
+import com.cleanroommc.modularui.utils.Color;
+import com.cleanroommc.modularui.value.sync.BooleanSyncValue;
+import com.cleanroommc.modularui.value.sync.PanelSyncHandler;
+import com.cleanroommc.modularui.value.sync.PanelSyncManager;
+import com.cleanroommc.modularui.value.sync.StringSyncValue;
+import com.cleanroommc.modularui.widgets.ButtonWidget;
+import com.cleanroommc.modularui.widgets.ListWidget;
+import com.cleanroommc.modularui.widgets.ToggleButton;
+import com.cleanroommc.modularui.widgets.layout.Column;
+import com.cleanroommc.modularui.widgets.layout.Row;
+import com.cleanroommc.modularui.widgets.textfield.TextFieldWidget;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Pattern;
+
+@SuppressWarnings("SameParameterValue")
+public abstract class CoverAbstractEnderLink extends CoverBase
+ implements CoverWithUI, ITickable, IControllable {
+
+ protected static final Pattern COLOR_INPUT_PATTERN = Pattern.compile("[0-9a-fA-F]*");
+ public static final int UPDATE_PRIVATE = GregtechDataCodes.assignId();
+
+ protected T activeEntry = null;
+ protected String color = VirtualEntry.DEFAULT_COLOR;
+ protected UUID playerUUID = null;
+ private boolean isPrivate = false;
+ private boolean workingEnabled = true;
+ private boolean ioEnabled = false;
+
+ public CoverAbstractEnderLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView,
+ @NotNull EnumFacing attachedSide) {
+ super(definition, coverableView, attachedSide);
+ updateLink();
+ }
+
+ protected void updateLink() {
+ this.activeEntry = VirtualEnderRegistry.getOrCreateEntry(getOwner(), getType(), createName());
+ this.activeEntry.setColor(this.color);
+ markDirty();
+ }
+
+ protected abstract EntryTypes getType();
+
+ public String getColorStr() {
+ return this.color;
+ }
+
+ protected final String createName() {
+ return identifier() + this.color;
+ }
+
+ protected abstract String identifier();
+
+ protected final UUID getOwner() {
+ return isPrivate ? playerUUID : null;
+ }
+
+ @Override
+ public void readCustomData(int discriminator, @NotNull PacketBuffer buf) {
+ super.readCustomData(discriminator, buf);
+ if (discriminator == UPDATE_PRIVATE) {
+ setPrivate(buf.readBoolean());
+ updateLink();
+ }
+ }
+
+ @Override
+ public @NotNull EnumActionResult onScrewdriverClick(@NotNull EntityPlayer playerIn, @NotNull EnumHand hand,
+ @NotNull CuboidRayTraceResult hitResult) {
+ if (!getWorld().isRemote) {
+ openUI((EntityPlayerMP) playerIn);
+ }
+ return EnumActionResult.SUCCESS;
+ }
+
+ @Override
+ public void onAttachment(@NotNull CoverableView coverableView, @NotNull EnumFacing side,
+ @Nullable EntityPlayer player, @NotNull ItemStack itemStack) {
+ super.onAttachment(coverableView, side, player, itemStack);
+ if (player != null) {
+ this.playerUUID = player.getUniqueID();
+ }
+ }
+
+ public void updateColor(String str) {
+ if (str.length() == 8) {
+ this.color = str.toUpperCase();
+ updateLink();
+ }
+ }
+
+ @Override
+ public boolean usesMui2() {
+ return true;
+ }
+
+ @Override
+ public ModularPanel buildUI(SidedPosGuiData guiData, PanelSyncManager guiSyncManager) {
+ var panel = GTGuis.createPanel(this, 176, 192);
+
+ return panel.child(CoverWithUI.createTitleRow(getPickItem()))
+ .child(createWidgets(panel, guiSyncManager))
+ .bindPlayerInventory();
+ }
+
+ protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) {
+ var name = new StringSyncValue(this::getColorStr, this::updateColor);
+
+ var entrySelectorSH = createEntrySelector(panel);
+ syncManager.syncValue("entry_selector", entrySelectorSH);
+
+ return new Column().coverChildrenHeight().top(24)
+ .margin(7, 0).widthRel(1f)
+ .child(new Row().marginBottom(2)
+ .coverChildrenHeight()
+ .child(createPrivateButton())
+ .child(createColorIcon())
+ .child(new TextFieldWidget()
+ .height(18)
+ .value(name)
+ .setPattern(COLOR_INPUT_PATTERN)
+ .widthRel(0.5f)
+ .marginRight(2))
+ .child(createEntrySlot())
+ .child(new ButtonWidget<>()
+ .overlay(GTGuiTextures.MENU_OVERLAY)
+ .background(GTGuiTextures.MC_BUTTON)
+ .disableHoverBackground()
+ .addTooltipLine(IKey.lang("cover.generic.ender.open_selector"))
+ .onMousePressed(i -> {
+ if (entrySelectorSH.isPanelOpen()) {
+ entrySelectorSH.closePanel();
+ } else {
+ entrySelectorSH.openPanel();
+ }
+ Interactable.playButtonClickSound();
+ return true;
+ })))
+ .child(createIoRow());
+ }
+
+ protected abstract PanelSyncHandler createEntrySelector(ModularPanel panel);
+
+ protected abstract IWidget createEntrySlot();
+
+ protected IWidget createColorIcon() {
+ // todo color selector popup panel
+ return new DynamicDrawable(() -> new Rectangle()
+ .setColor(this.activeEntry.getColor())
+ .asIcon().size(16))
+ .asWidget()
+ .background(GTGuiTextures.SLOT)
+ .size(18)
+ .marginRight(2);
+ }
+
+ protected IWidget createPrivateButton() {
+ var isPrivate = new BooleanSyncValue(this::isPrivate, this::setPrivate);
+ isPrivate.updateCacheFromSource(true);
+
+ return new ToggleButton()
+ .tooltip(tooltip -> tooltip.setAutoUpdate(true))
+ .background(GTGuiTextures.PRIVATE_MODE_BUTTON[0])
+ .hoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[0])
+ .selectedBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1])
+ .selectedHoverBackground(GTGuiTextures.PRIVATE_MODE_BUTTON[1])
+ .tooltipBuilder(tooltip -> tooltip.addLine(IKey.lang(this.isPrivate ?
+ "cover.ender_fluid_link.private.tooltip.enabled" :
+ "cover.ender_fluid_link.private.tooltip.disabled")))
+ .marginRight(2)
+ .value(isPrivate);
+ }
+
+ protected IWidget createIoRow() {
+ var ioEnabled = new BooleanSyncValue(this::isIoEnabled, this::setIoEnabled);
+
+ return new Row().marginBottom(2)
+ .coverChildrenHeight()
+ .child(new ToggleButton()
+ .value(ioEnabled)
+ .overlay(IKey.dynamic(() -> IKey.lang(this.ioEnabled ?
+ "behaviour.soft_hammer.enabled" :
+ "behaviour.soft_hammer.disabled").get())
+ .color(Color.WHITE.darker(1)))
+ .widthRel(0.6f)
+ .left(0));
+ }
+
+ @Override
+ public boolean isWorkingEnabled() {
+ return workingEnabled;
+ }
+
+ @Override
+ public void setWorkingEnabled(boolean isActivationAllowed) {
+ this.workingEnabled = isActivationAllowed;
+ }
+
+ public boolean isIoEnabled() {
+ return ioEnabled;
+ }
+
+ protected void setIoEnabled(boolean ioEnabled) {
+ this.ioEnabled = ioEnabled;
+ }
+
+ private boolean isPrivate() {
+ return isPrivate;
+ }
+
+ private void setPrivate(boolean isPrivate) {
+ this.isPrivate = isPrivate;
+ updateLink();
+ writeCustomData(UPDATE_PRIVATE, buffer -> buffer.writeBoolean(this.isPrivate));
+ }
+
+ @Override
+ public void writeInitialSyncData(PacketBuffer packetBuffer) {
+ packetBuffer.writeString(this.playerUUID == null ? "null" : this.playerUUID.toString());
+ }
+
+ @Override
+ public void readInitialSyncData(PacketBuffer packetBuffer) {
+ // does client even need uuid info? just in case
+ String uuidStr = packetBuffer.readString(36);
+ this.playerUUID = uuidStr.equals("null") ? null : UUID.fromString(uuidStr);
+ }
+
+ @Override
+ public void readFromNBT(@NotNull NBTTagCompound nbt) {
+ super.readFromNBT(nbt);
+ this.ioEnabled = nbt.getBoolean("IOAllowed");
+ this.isPrivate = nbt.getBoolean("Private");
+ this.workingEnabled = nbt.getBoolean("WorkingAllowed");
+ this.playerUUID = UUID.fromString(nbt.getString("PlacedUUID"));
+ int color = nbt.getInteger("Frequency");
+ this.color = Integer.toHexString(color).toUpperCase();
+ updateLink();
+ }
+
+ @Override
+ public void writeToNBT(@NotNull NBTTagCompound nbt) {
+ super.writeToNBT(nbt);
+ nbt.setBoolean("IOAllowed", ioEnabled);
+ nbt.setBoolean("Private", isPrivate);
+ nbt.setBoolean("WorkingAllowed", workingEnabled);
+ nbt.setString("PlacedUUID", playerUUID.toString());
+ nbt.setInteger("Frequency", activeEntry.getColor());
+ }
+
+ protected abstract class EntrySelectorSH extends PanelSyncHandler {
+
+ private static final int TRACK_SUBPANELS = 3;
+ private static final int DELETE_ENTRY = 1;
+ private final EntryTypes type;
+ private final ModularPanel mainPanel;
+ private static final String PANEL_NAME = "entry_selector";
+ private final Set opened = new HashSet<>();
+ protected UUID playerUUID;
+
+ protected EntrySelectorSH(ModularPanel mainPanel, EntryTypes type) {
+ super(mainPanel, EntrySelectorSH::defaultPanel);
+ this.type = type;
+ this.mainPanel = mainPanel;
+ }
+
+ @Override
+ public void init(String key, PanelSyncManager syncManager) {
+ super.init(key, syncManager);
+ this.playerUUID = syncManager.getPlayer().getUniqueID();
+ }
+
+ private static ModularPanel defaultPanel(PanelSyncManager syncManager, PanelSyncHandler syncHandler) {
+ return GTGuis.createPopupPanel(PANEL_NAME, 168, 112);
+ }
+
+ public UUID getPlayerUUID() {
+ return isPrivate ? playerUUID : null;
+ }
+
+ @Override
+ public ModularPanel createUI(PanelSyncManager syncManager) {
+ List names = new ArrayList<>(VirtualEnderRegistry.getEntryNames(getPlayerUUID(), type));
+ return super.createUI(syncManager)
+ .child(IKey.lang("cover.generic.ender.known_channels")
+ .color(UI_TITLE_COLOR).asWidget()
+ .top(6)
+ .left(4))
+ .child(ListWidget.builder(names, name -> createRow(name, this.mainPanel, syncManager))
+ .background(GTGuiTextures.DISPLAY.asIcon()
+ .width(168 - 8)
+ .height(112 - 20))
+ .paddingTop(1)
+ .size(168 - 12, 112 - 24)
+ .left(4)
+ .bottom(6));
+ }
+
+ protected IWidget createRow(String name, ModularPanel mainPanel, PanelSyncManager syncManager) {
+ T entry = VirtualEnderRegistry.getEntry(getPlayerUUID(), this.type, name);
+ String key = String.format("entry#%s_description", entry.getColorStr());
+ var entryDescriptionSH = new EntryDescriptionSH(mainPanel, key, entry);
+ syncManager.syncValue(key, isPrivate ? 1 : 0, entryDescriptionSH);
+
+ return new Row()
+ .left(4)
+ .marginBottom(2)
+ .height(18)
+ .widthRel(0.98f)
+ .setEnabledIf(row -> VirtualEnderRegistry.hasEntry(getOwner(), this.type, name))
+ .child(new Rectangle()
+ .setColor(entry.getColor())
+ .asWidget()
+ .marginRight(4)
+ .size(16)
+ .background(GTGuiTextures.SLOT.asIcon().size(18))
+ .top(1))
+ .child(new InteractableText<>(entry, CoverAbstractEnderLink.this::updateColor)
+ .tooltip(tooltip -> tooltip.setAutoUpdate(true))
+ .tooltipBuilder(tooltip -> {
+ String desc = entry.getDescription();
+ if (!desc.isEmpty())
+ tooltip.addLine(desc);
+ })
+ .width(64)
+ .height(16)
+ .top(1)
+ .marginRight(4))
+ .child(new ButtonWidget<>()
+ .overlay(GuiTextures.GEAR)
+ .addTooltipLine(IKey.lang("cover.generic.ender.set_description.tooltip"))
+ .onMousePressed(i -> {
+ // open entry settings
+ if (entryDescriptionSH.isPanelOpen()) {
+ entryDescriptionSH.closePanel();
+ } else {
+ entryDescriptionSH.openPanel();
+ }
+ Interactable.playButtonClickSound();
+ return true;
+ }))
+ .child(createSlotWidget(entry))
+ .child(new ButtonWidget<>()
+ .overlay(GTGuiTextures.BUTTON_CROSS)
+ .setEnabledIf(w -> !Objects.equals(entry.getColor(), activeEntry.getColor()))
+ .addTooltipLine(IKey.lang("cover.generic.ender.delete_entry"))
+ .onMousePressed(i -> {
+ // todo option to force delete, maybe as a popup?
+ deleteEntry(getPlayerUUID(), name);
+ syncToServer(1, buffer -> {
+ NetworkUtils.writeStringSafe(buffer, getPlayerUUID().toString());
+ NetworkUtils.writeStringSafe(buffer, name);
+ });
+ Interactable.playButtonClickSound();
+ return true;
+ }));
+ }
+
+ @Override
+ public void closePanel() {
+ var manager = getSyncManager().getModularSyncManager().getPanelSyncManager(PANEL_NAME);
+ for (var key : opened) {
+ if (manager.getSyncHandler(key) instanceof PanelSyncHandler psh) {
+ psh.closePanel();
+ }
+ }
+ super.closePanel();
+ }
+
+ @Override
+ @SuppressWarnings("UnstableApiUsage")
+ public void closePanelInternal() {
+ var manager = getSyncManager().getModularSyncManager().getPanelSyncManager(PANEL_NAME);
+ for (var key : opened) {
+ if (manager.getSyncHandler(key) instanceof PanelSyncHandler psh) {
+ psh.closePanel();
+ }
+ }
+ super.closePanelInternal();
+ }
+
+ @Override
+ public void readOnClient(int i, PacketBuffer packetBuffer) throws IOException {
+ if (i == TRACK_SUBPANELS) {
+ handleTracking(packetBuffer);
+ }
+ super.readOnClient(i, packetBuffer);
+ }
+
+ @Override
+ public void readOnServer(int i, PacketBuffer packetBuffer) throws IOException {
+ if (i == TRACK_SUBPANELS) {
+ handleTracking(packetBuffer);
+ }
+ super.readOnServer(i, packetBuffer);
+ if (i == DELETE_ENTRY) {
+ UUID uuid = UUID.fromString(NetworkUtils.readStringSafe(packetBuffer));
+ String name = NetworkUtils.readStringSafe(packetBuffer);
+ deleteEntry(uuid, name);
+ }
+ }
+
+ private void handleTracking(PacketBuffer buffer) {
+ boolean add = buffer.readBoolean();
+ String key = NetworkUtils.readStringSafe(buffer);
+ if (key != null) {
+ if (add) opened.add(key);
+ else opened.remove(key);
+ }
+ }
+
+ private class EntryDescriptionSH extends PanelSyncHandler {
+
+ /**
+ * Creates a PanelSyncHandler
+ *
+ * @param mainPanel the main panel of the current GUI
+ */
+ public EntryDescriptionSH(ModularPanel mainPanel, String key, VirtualEntry entry) {
+ super(mainPanel, (syncManager, syncHandler) -> defaultPanel(syncHandler, key, entry));
+ }
+
+ private static ModularPanel defaultPanel(@NotNull PanelSyncHandler syncHandler, String key,
+ VirtualEntry entry) {
+ return GTGuis.createPopupPanel(key, 168, 36 + 6)
+ .child(IKey.lang("cover.generic.ender.set_description.title", entry.getColorStr())
+ .color(UI_TITLE_COLOR)
+ .asWidget()
+ .left(4)
+ .top(6))
+ .child(new TextFieldWidget()
+ .setTextColor(Color.WHITE.darker(1))
+ .widthRel(0.95f)
+ .height(18)
+ .value(new StringSyncValue(entry::getDescription, string -> {
+ entry.setDescription(string);
+ if (syncHandler.isPanelOpen()) {
+ syncHandler.closePanel();
+ }
+ }))
+ .alignX(0.5f)
+ .bottom(6));
+ }
+
+ @Override
+ public void openPanel() {
+ opened.add(getKey());
+ EntrySelectorSH.this.sync(3, buffer -> {
+ buffer.writeBoolean(true);
+ NetworkUtils.writeStringSafe(buffer, getKey());
+ });
+ super.openPanel();
+ }
+
+ @Override
+ public void closePanel() {
+ opened.remove(getKey());
+ EntrySelectorSH.this.sync(3, buffer -> {
+ buffer.writeBoolean(false);
+ NetworkUtils.writeStringSafe(buffer, getKey());
+ });
+ super.closePanel();
+ }
+ }
+
+ protected abstract IWidget createSlotWidget(T entry);
+
+ protected abstract void deleteEntry(UUID player, String name);
+ }
+}
diff --git a/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java b/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java
new file mode 100644
index 00000000000..27c338154b1
--- /dev/null
+++ b/src/main/java/gregtech/common/covers/ender/CoverEnderFluidLink.java
@@ -0,0 +1,193 @@
+package gregtech.common.covers.ender;
+
+import gregtech.api.capability.GregtechTileCapabilities;
+import gregtech.api.capability.IControllable;
+import gregtech.api.cover.CoverDefinition;
+import gregtech.api.cover.CoverWithUI;
+import gregtech.api.cover.CoverableView;
+import gregtech.api.mui.GTGuiTextures;
+import gregtech.api.util.FluidTankSwitchShim;
+import gregtech.api.util.GTTransferUtils;
+import gregtech.api.util.virtualregistry.EntryTypes;
+import gregtech.api.util.virtualregistry.VirtualEnderRegistry;
+import gregtech.api.util.virtualregistry.entries.VirtualTank;
+import gregtech.client.renderer.texture.Textures;
+import gregtech.common.covers.CoverPump;
+import gregtech.common.covers.filter.FluidFilterContainer;
+import gregtech.common.mui.widget.GTFluidSlot;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.BlockRenderLayer;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ITickable;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
+import net.minecraftforge.fluids.capability.IFluidHandler;
+
+import codechicken.lib.render.CCRenderState;
+import codechicken.lib.render.pipeline.IVertexOperation;
+import codechicken.lib.vec.Cuboid6;
+import codechicken.lib.vec.Matrix4;
+import com.cleanroommc.modularui.api.widget.IWidget;
+import com.cleanroommc.modularui.screen.ModularPanel;
+import com.cleanroommc.modularui.value.sync.EnumSyncValue;
+import com.cleanroommc.modularui.value.sync.PanelSyncHandler;
+import com.cleanroommc.modularui.value.sync.PanelSyncManager;
+import com.cleanroommc.modularui.widgets.layout.Column;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.UUID;
+
+public class CoverEnderFluidLink extends CoverAbstractEnderLink
+ implements CoverWithUI, ITickable, IControllable {
+
+ public static final int TRANSFER_RATE = 8000; // mB/t
+
+ protected CoverPump.PumpMode pumpMode = CoverPump.PumpMode.IMPORT;
+ private final FluidTankSwitchShim linkedTank;
+ protected final FluidFilterContainer fluidFilter;
+
+ public CoverEnderFluidLink(@NotNull CoverDefinition definition, @NotNull CoverableView coverableView,
+ @NotNull EnumFacing attachedSide) {
+ super(definition, coverableView, attachedSide);
+ this.linkedTank = new FluidTankSwitchShim(this.activeEntry);
+ this.fluidFilter = new FluidFilterContainer(this);
+ }
+
+ @Override
+ protected void updateLink() {
+ super.updateLink();
+ if (this.linkedTank != null)
+ this.linkedTank.changeTank(this.activeEntry);
+ }
+
+ @Override
+ protected EntryTypes getType() {
+ return EntryTypes.ENDER_FLUID;
+ }
+
+ @Override
+ protected String identifier() {
+ return "EFLink#";
+ }
+
+ public FluidFilterContainer getFluidFilterContainer() {
+ return this.fluidFilter;
+ }
+
+ @Override
+ public boolean canAttach(@NotNull CoverableView coverable, @NotNull EnumFacing side) {
+ return coverable.hasCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, side);
+ }
+
+ @Override
+ public void renderCover(@NotNull CCRenderState renderState, @NotNull Matrix4 translation,
+ IVertexOperation[] pipeline, @NotNull Cuboid6 plateBox, @NotNull BlockRenderLayer layer) {
+ Textures.ENDER_FLUID_LINK.renderSided(getAttachedSide(), plateBox, renderState, pipeline, translation);
+ }
+
+ @Override
+ public void onRemoval() {
+ dropInventoryContents(fluidFilter);
+ }
+
+ @Override
+ public void update() {
+ if (isWorkingEnabled() && isIoEnabled()) {
+ transferFluids();
+ }
+ }
+
+ protected void transferFluids() {
+ IFluidHandler fluidHandler = getCoverableView().getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
+ getAttachedSide());
+ if (fluidHandler == null) return;
+ if (pumpMode == CoverPump.PumpMode.IMPORT) {
+ GTTransferUtils.transferFluids(fluidHandler, activeEntry, TRANSFER_RATE, fluidFilter::test);
+ } else if (pumpMode == CoverPump.PumpMode.EXPORT) {
+ GTTransferUtils.transferFluids(activeEntry, fluidHandler, TRANSFER_RATE, fluidFilter::test);
+ }
+ }
+
+ public void setPumpMode(CoverPump.PumpMode pumpMode) {
+ this.pumpMode = pumpMode;
+ markDirty();
+ }
+
+ public CoverPump.PumpMode getPumpMode() {
+ return pumpMode;
+ }
+
+ @Override
+ protected PanelSyncHandler createEntrySelector(ModularPanel panel) {
+ return new EntrySelectorSH(panel, EntryTypes.ENDER_FLUID) {
+
+ @Override
+ protected IWidget createSlotWidget(VirtualTank entry) {
+ var fluidTank = GTFluidSlot.sync(entry)
+ .canFillSlot(false)
+ .canDrainSlot(false);
+
+ return new GTFluidSlot()
+ .size(18)
+ .background(GTGuiTextures.FLUID_SLOT)
+ .syncHandler(fluidTank)
+ .marginRight(2);
+ }
+
+ @Override
+ protected void deleteEntry(UUID uuid, String name) {
+ VirtualEnderRegistry.deleteEntry(uuid, getType(), name, tank -> tank.getFluidAmount() == 0);
+ }
+ };
+ }
+
+ @Override
+ protected IWidget createEntrySlot() {
+ return new GTFluidSlot()
+ .size(18)
+ .background(GTGuiTextures.FLUID_SLOT)
+ .syncHandler(this.linkedTank)
+ .marginRight(2);
+ }
+
+ protected Column createWidgets(ModularPanel panel, PanelSyncManager syncManager) {
+ getFluidFilterContainer().setMaxTransferSize(1);
+
+ var pumpMode = new EnumSyncValue<>(CoverPump.PumpMode.class, this::getPumpMode, this::setPumpMode);
+ syncManager.syncValue("pump_mode", pumpMode);
+ pumpMode.updateCacheFromSource(true);
+
+ return super.createWidgets(panel, syncManager)
+ .child(getFluidFilterContainer().initUI(panel, syncManager))
+ .child(new EnumRowBuilder<>(CoverPump.PumpMode.class)
+ .value(pumpMode)
+ .overlay(GTGuiTextures.CONVEYOR_MODE_OVERLAY)
+ .lang("cover.pump.mode")
+ .build());
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound tagCompound) {
+ super.writeToNBT(tagCompound);
+ tagCompound.setInteger("PumpMode", pumpMode.ordinal());
+ tagCompound.setTag("Filter", fluidFilter.serializeNBT());
+ }
+
+ @Override
+ public void readFromNBT(NBTTagCompound tagCompound) {
+ super.readFromNBT(tagCompound);
+ this.pumpMode = CoverPump.PumpMode.values()[tagCompound.getInteger("PumpMode")];
+ this.fluidFilter.deserializeNBT(tagCompound.getCompoundTag("Filter"));
+ }
+
+ public T getCapability(Capability capability, T defaultValue) {
+ if (capability == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) {
+ return CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY.cast(this.activeEntry);
+ }
+ if (capability == GregtechTileCapabilities.CAPABILITY_CONTROLLABLE) {
+ return GregtechTileCapabilities.CAPABILITY_CONTROLLABLE.cast(this);
+ }
+ return defaultValue;
+ }
+}
diff --git a/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java
new file mode 100644
index 00000000000..1b0ccff4ebe
--- /dev/null
+++ b/src/main/java/gregtech/common/mui/widget/GTFluidSlot.java
@@ -0,0 +1,142 @@
+package gregtech.common.mui.widget;
+
+import gregtech.api.GTValues;
+import gregtech.api.mui.sync.GTFluidSyncHandler;
+import gregtech.api.util.FluidTooltipUtil;
+import gregtech.api.util.LocalizationUtils;
+import gregtech.client.utils.TooltipHelper;
+
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.util.text.TextFormatting;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidTank;
+
+import com.cleanroommc.modularui.api.ITheme;
+import com.cleanroommc.modularui.api.drawable.IKey;
+import com.cleanroommc.modularui.api.widget.Interactable;
+import com.cleanroommc.modularui.drawable.GuiDraw;
+import com.cleanroommc.modularui.drawable.TextRenderer;
+import com.cleanroommc.modularui.integration.jei.JeiIngredientProvider;
+import com.cleanroommc.modularui.screen.Tooltip;
+import com.cleanroommc.modularui.screen.viewport.GuiContext;
+import com.cleanroommc.modularui.theme.WidgetSlotTheme;
+import com.cleanroommc.modularui.theme.WidgetTheme;
+import com.cleanroommc.modularui.utils.Alignment;
+import com.cleanroommc.modularui.utils.Color;
+import com.cleanroommc.modularui.utils.NumberFormat;
+import com.cleanroommc.modularui.widget.Widget;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+public class GTFluidSlot extends Widget implements Interactable, JeiIngredientProvider {
+
+ private final TextRenderer textRenderer = new TextRenderer();
+ private GTFluidSyncHandler syncHandler;
+
+ public GTFluidSlot() {
+ tooltip().setAutoUpdate(true).setHasTitleMargin(true);
+ tooltipBuilder(tooltip -> {
+ if (!isSynced()) return;
+ var fluid = this.syncHandler.getFluid();
+ if (fluid == null) return;
+
+ tooltip.addLine(fluid.getLocalizedName());
+ tooltip.addLine(IKey.lang("gregtech.fluid.amount", fluid.amount, this.syncHandler.getCapacity()));
+
+ // Add various tooltips from the material
+ List formula = FluidTooltipUtil.getFluidTooltip(fluid);
+ if (formula != null) {
+ for (String s : formula) {
+ if (s.isEmpty()) continue;
+ tooltip.addLine(s);
+ }
+ }
+
+ addIngotMolFluidTooltip(fluid, tooltip);
+ });
+ }
+
+ public static GTFluidSyncHandler sync(IFluidTank tank) {
+ return new GTFluidSyncHandler(tank);
+ }
+
+ @Override
+ public void onInit() {
+ this.textRenderer.setShadow(true);
+ this.textRenderer.setScale(0.5f);
+ this.textRenderer.setColor(Color.WHITE.main);
+ }
+
+ public GTFluidSlot syncHandler(IFluidTank fluidTank) {
+ return syncHandler(new GTFluidSyncHandler(fluidTank));
+ }
+
+ public GTFluidSlot syncHandler(GTFluidSyncHandler syncHandler) {
+ setSyncHandler(syncHandler);
+ this.syncHandler = syncHandler;
+ return this;
+ }
+
+ @Override
+ public void draw(GuiContext context, WidgetTheme widgetTheme) {
+ FluidStack content = this.syncHandler.getFluid();
+ if (content != null) {
+ GuiDraw.drawFluidTexture(content, 1, 1, getArea().w() - 2, getArea().h() - 2, 0);
+
+ String s = NumberFormat.formatWithMaxDigits(getBaseUnitAmount(content.amount)) + getBaseUnit();
+ this.textRenderer.setAlignment(Alignment.CenterRight, getArea().width - 1f);
+ this.textRenderer.setPos(0, 12);
+ this.textRenderer.draw(s);
+ }
+ if (isHovering()) {
+ GlStateManager.colorMask(true, true, true, false);
+ GuiDraw.drawRect(1, 1, getArea().w() - 2, getArea().h() - 2,
+ getWidgetTheme(context.getTheme()).getSlotHoverColor());
+ GlStateManager.colorMask(true, true, true, true);
+ }
+ }
+
+ protected double getBaseUnitAmount(double amount) {
+ return amount / 1000;
+ }
+
+ protected String getBaseUnit() {
+ return "L";
+ }
+
+ @NotNull
+ @Override
+ public Result onMouseTapped(int mouseButton) {
+ if (this.syncHandler.canFillSlot() || this.syncHandler.canDrainSlot()) {
+ this.syncHandler.syncToServer(1, buffer -> buffer.writeBoolean(mouseButton == 0));
+ Interactable.playButtonClickSound();
+ return Result.SUCCESS;
+ }
+ return Result.IGNORE;
+ }
+
+ @Override
+ public WidgetSlotTheme getWidgetTheme(ITheme theme) {
+ return theme.getFluidSlotTheme();
+ }
+
+ @Override
+ public @Nullable Object getIngredient() {
+ return this.syncHandler.getFluid();
+ }
+
+ public static void addIngotMolFluidTooltip(FluidStack fluidStack, Tooltip tooltip) {
+ // Add tooltip showing how many "ingot moles" (increments of 144) this fluid is if shift is held
+ if (TooltipHelper.isShiftDown() && fluidStack.amount > GTValues.L) {
+ int numIngots = fluidStack.amount / GTValues.L;
+ int extra = fluidStack.amount % GTValues.L;
+ String fluidAmount = String.format(" %,d L = %,d * %d L", fluidStack.amount, numIngots, GTValues.L);
+ if (extra != 0) {
+ fluidAmount += String.format(" + %d L", extra);
+ }
+ tooltip.addLine(TextFormatting.GRAY + LocalizationUtils.format("gregtech.gui.amount_raw") + fluidAmount);
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/mui/widget/InteractableText.java b/src/main/java/gregtech/common/mui/widget/InteractableText.java
new file mode 100644
index 00000000000..eac6936059a
--- /dev/null
+++ b/src/main/java/gregtech/common/mui/widget/InteractableText.java
@@ -0,0 +1,63 @@
+package gregtech.common.mui.widget;
+
+import gregtech.api.util.virtualregistry.VirtualEntry;
+
+import net.minecraft.network.PacketBuffer;
+
+import com.cleanroommc.modularui.api.drawable.IKey;
+import com.cleanroommc.modularui.api.widget.Interactable;
+import com.cleanroommc.modularui.network.NetworkUtils;
+import com.cleanroommc.modularui.utils.Alignment;
+import com.cleanroommc.modularui.utils.Color;
+import com.cleanroommc.modularui.value.sync.SyncHandler;
+import com.cleanroommc.modularui.widgets.TextWidget;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.function.Consumer;
+
+public class InteractableText extends TextWidget implements Interactable {
+
+ private final T entry;
+ private final EntryColorSH syncHandler;
+
+ public InteractableText(T entry, Consumer setter) {
+ super(IKey.str(entry.getColorStr())
+ .alignment(Alignment.CenterLeft)
+ .color(Color.WHITE.darker(1)));
+ this.entry = entry;
+ this.syncHandler = new EntryColorSH(setter);
+ setSyncHandler(this.syncHandler);
+ }
+
+ @NotNull
+ @Override
+ public Result onMousePressed(int mouseButton) {
+ Interactable.playButtonClickSound();
+ this.syncHandler.setColor(this.entry.getColorStr());
+ this.syncHandler.syncToServer(1, buf -> NetworkUtils.writeStringSafe(buf, this.entry.getColorStr()));
+ return Result.SUCCESS;
+ }
+
+ private static class EntryColorSH extends SyncHandler {
+
+ private final Consumer setter;
+
+ private EntryColorSH(Consumer setter) {
+ this.setter = setter;
+ }
+
+ public void setColor(String c) {
+ this.setter.accept(c);
+ }
+
+ @Override
+ public void readOnClient(int id, PacketBuffer buf) {}
+
+ @Override
+ public void readOnServer(int id, PacketBuffer buf) {
+ if (id == 1) {
+ setColor(NetworkUtils.readStringSafe(buf));
+ }
+ }
+ }
+}
diff --git a/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java b/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java
index b6a24a190b9..ed0e63eefac 100644
--- a/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java
+++ b/src/main/java/gregtech/common/terminal/app/VirtualTankApp.java
@@ -12,7 +12,7 @@
import gregtech.api.terminal.os.TerminalTheme;
import gregtech.api.terminal.os.menu.IMenuComponent;
import gregtech.api.util.GTLog;
-import gregtech.api.util.VirtualTankRegistry;
+import gregtech.api.util.virtualregistry.VirtualEnderRegistry;
import gregtech.common.terminal.component.SearchComponent;
import net.minecraft.nbt.NBTTagCompound;
@@ -70,9 +70,8 @@ public AbstractApplication initApp() {
return this;
}
- private List> findVirtualTanks() {
+ private List> findVirtualTanks(Map> tankMap) {
List> result = new LinkedList<>();
- Map> tankMap = VirtualTankRegistry.getTankMap();
for (UUID uuid : tankMap.keySet().stream().sorted(Comparator.nullsLast(UUID::compareTo))
.collect(Collectors.toList())) {
if (uuid == null || uuid.equals(gui.entityPlayer.getUniqueID())) {
@@ -93,9 +92,9 @@ public void detectAndSendChanges() {
}
private void refresh() {
- Map> tankMap = VirtualTankRegistry.getTankMap();
+ Map> tankMap = VirtualEnderRegistry.createTankMap();
Map, FluidStack> access = new HashMap<>();
- for (Pair virtualTankEntry : findVirtualTanks()) {
+ for (Pair virtualTankEntry : findVirtualTanks(tankMap)) {
UUID uuid = virtualTankEntry.getKey();
String key = virtualTankEntry.getValue();
FluidStack fluidStack = tankMap.get(uuid).get(key).getFluid();
@@ -229,7 +228,8 @@ public List getMenuComponents() {
@Override
public String resultDisplay(Pair result) {
- FluidStack fluidStack = VirtualTankRegistry.getTankMap().get(result.getKey()).get(result.getValue()).getFluid();
+ FluidStack fluidStack = VirtualEnderRegistry.createTankMap().get(result.getKey()).get(result.getValue())
+ .getFluid();
return String.format("Lock: %b, ID: %s, Fluid: %s", result.getKey() != null, result.getValue(),
fluidStack == null ? "-" : fluidStack.getLocalizedName());
}
diff --git a/src/main/java/gregtech/core/CoreModule.java b/src/main/java/gregtech/core/CoreModule.java
index 5e80c4b3b31..149c19344ec 100644
--- a/src/main/java/gregtech/core/CoreModule.java
+++ b/src/main/java/gregtech/core/CoreModule.java
@@ -29,9 +29,9 @@
import gregtech.api.unification.material.registry.MarkerMaterialRegistry;
import gregtech.api.util.CapesRegistry;
import gregtech.api.util.Mods;
-import gregtech.api.util.VirtualTankRegistry;
import gregtech.api.util.input.KeyBind;
import gregtech.api.util.oreglob.OreGlob;
+import gregtech.api.util.virtualregistry.VirtualEnderRegistry;
import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinHandler;
import gregtech.api.worldgen.bedrockFluids.BedrockFluidVeinSaveData;
import gregtech.api.worldgen.config.WorldGenRegistry;
@@ -347,7 +347,7 @@ public void serverStarted(FMLServerStartedEvent event) {
@Override
public void serverStopped(FMLServerStoppedEvent event) {
- VirtualTankRegistry.clearMaps();
+ VirtualEnderRegistry.clearMaps();
CapesRegistry.clearMaps();
}
}
diff --git a/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java b/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java
index be85082485c..b05a8e3f7af 100644
--- a/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java
+++ b/src/main/java/gregtech/integration/opencomputers/drivers/DriverCoverHolder.java
@@ -5,6 +5,7 @@
import gregtech.api.cover.CoverHolder;
import gregtech.api.metatileentity.interfaces.IGregTechTileEntity;
import gregtech.common.covers.*;
+import gregtech.common.covers.ender.CoverEnderFluidLink;
import gregtech.integration.opencomputers.InputValidator;
import gregtech.integration.opencomputers.values.*;
diff --git a/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java b/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java
index 8f919385959..31c8b2ae10d 100644
--- a/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java
+++ b/src/main/java/gregtech/integration/opencomputers/values/ValueCoverEnderFluidLink.java
@@ -1,8 +1,8 @@
package gregtech.integration.opencomputers.values;
import gregtech.api.cover.Cover;
-import gregtech.common.covers.CoverEnderFluidLink;
import gregtech.common.covers.CoverPump.PumpMode;
+import gregtech.common.covers.ender.CoverEnderFluidLink;
import gregtech.integration.opencomputers.InputValidator;
import net.minecraft.util.EnumFacing;
diff --git a/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java b/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java
index 0aebf88036a..377ff7a51d7 100644
--- a/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java
+++ b/src/main/java/gregtech/integration/theoneprobe/provider/CoverInfoProvider.java
@@ -6,6 +6,7 @@
import gregtech.api.cover.CoverHolder;
import gregtech.api.util.TextFormattingUtil;
import gregtech.common.covers.*;
+import gregtech.common.covers.ender.CoverEnderFluidLink;
import gregtech.common.covers.filter.*;
import net.minecraft.entity.player.EntityPlayer;
@@ -181,8 +182,8 @@ private static void fluidFilterInfo(@NotNull IProbeInfo probeInfo, @NotNull Cove
* @param enderFluidLink the ender fluid link cover to get data from
*/
private static void enderFluidLinkInfo(@NotNull IProbeInfo probeInfo, @NotNull CoverEnderFluidLink enderFluidLink) {
- transferRateText(probeInfo, enderFluidLink.getPumpMode(), " " + lang("cover.bucket.mode.milli_bucket_rate"),
- enderFluidLink.isIOEnabled() ? CoverEnderFluidLink.TRANSFER_RATE : 0);
+ transferRateText(probeInfo, enderFluidLink.getPumpMode(), " " + lang("cover.ender_fluid_link.transfer_unit"),
+ enderFluidLink.isIoEnabled() ? CoverEnderFluidLink.TRANSFER_RATE : 0);
fluidFilterText(probeInfo, enderFluidLink.getFluidFilterContainer().getFilter());
if (!enderFluidLink.getColorStr().isEmpty()) {
diff --git a/src/main/resources/assets/gregtech/lang/en_us.lang b/src/main/resources/assets/gregtech/lang/en_us.lang
index 8576e93db24..042607af612 100644
--- a/src/main/resources/assets/gregtech/lang/en_us.lang
+++ b/src/main/resources/assets/gregtech/lang/en_us.lang
@@ -1358,6 +1358,13 @@ cover.ender_fluid_link.iomode.disabled=I/O Disabled
cover.ender_fluid_link.private.tooltip.disabled=Switch to private tank mode\nPrivate mode uses the player who originally placed the cover
cover.ender_fluid_link.private.tooltip.enabled=Switch to public tank mode
cover.ender_fluid_link.incomplete_hex=Inputted color is incomplete!/nIt will be applied once complete (all 8 hex digits)/nClosing the gui will lose edits!
+cover.ender_fluid_link.transfer_unit=L/t
+
+cover.generic.ender.known_channels=Known Channels
+cover.generic.ender.open_selector=Open Entry Selector
+cover.generic.ender.set_description.tooltip=Set Description
+cover.generic.ender.set_description.title=Set Description [%s]
+cover.generic.ender.delete_entry=Delete Entry
cover.generic.advanced_detector.latched=Latched
cover.generic.advanced_detector.continuous=Continuous
diff --git a/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png b/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png
new file mode 100644
index 00000000000..dc166fac7e4
Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/gui/overlay/menu_overlay.png differ