> holder = createHolder();
+
+ public GuiGasInterfaceConfigurationTerminal(final InventoryPlayer inventoryPlayer, final PartGasInterfaceConfigurationTerminal te) {
+ super(new ContainerGasInterfaceConfigurationTerminal(inventoryPlayer, te));
+ this.holder.put("update", o -> this.postUpdate(o.get(0)));
+ this.partInterfaceTerminal = te;
+ final GuiScrollbar scrollbar = new GuiScrollbar();
+ this.setScrollBar(scrollbar);
+ this.xSize = 208;
+ this.ySize = 235;
+ }
+
+ @Override
+ public void initGui() {
+ super.initGui();
+
+ this.getScrollBar().setLeft(189);
+ this.getScrollBar().setHeight(106);
+ this.getScrollBar().setTop(31);
+
+ this.searchFieldInputs = new MEGuiTextField(this.fontRenderer, this.guiLeft + Math.max(32, this.offsetX), this.guiTop + 17, 65, 12);
+ this.searchFieldInputs.setEnableBackgroundDrawing(false);
+ this.searchFieldInputs.setMaxStringLength(25);
+ this.searchFieldInputs.setTextColor(0xFFFFFF);
+ this.searchFieldInputs.setVisible(true);
+ this.searchFieldInputs.setFocused(false);
+
+ this.searchFieldInputs.setText(partInterfaceTerminal.in);
+ }
+
+ @Override
+ public void onGuiClosed() {
+ partInterfaceTerminal.saveSearchStrings(this.searchFieldInputs.getText().toLowerCase());
+ super.onGuiClosed();
+ }
+
+ @Override
+ public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
+ this.buttonList.clear();
+
+ this.fontRenderer.drawString(this.getGuiDisplayName(I18n.format("gui.mekeng.gas_interface_terminal")), 8, 6, 4210752);
+ this.fontRenderer.drawString(GuiText.inventory.getLocal(), this.offsetX + 2, this.ySize - 96 + 3, 4210752);
+
+ final int currentScroll = this.getScrollBar().getCurrentScroll();
+
+ this.guiSlots.removeIf(slot -> slot instanceof SlotGasTank);
+
+ int offset = 30;
+ int linesDraw = 0;
+ for (int x = 0; x < LINES_ON_PAGE && linesDraw < LINES_ON_PAGE && currentScroll + x < this.lines.size(); x++) {
+ final Object lineObj = this.lines.get(currentScroll + x);
+ if (lineObj instanceof ClientDCInternalGasInv) {
+ final ClientDCInternalGasInv inv = (ClientDCInternalGasInv) lineObj;
+
+ GuiButton guiButton = new GuiImgButton(guiLeft + 4, guiTop + offset, Settings.ACTIONS, ActionItems.HIGHLIGHT_INTERFACE);
+ guiButtonHashMap.put(guiButton, inv);
+ this.buttonList.add(guiButton);
+ int extraLines = numUpgradesMap.get(inv);
+
+ for (int row = 0; row < 1 + extraLines && linesDraw < LINES_ON_PAGE; ++row) {
+ for (int z = 0; z < DualityGasInterface.NUMBER_OF_TANKS; z++) {
+ SlotGasTank tankSlot;
+ if (!matchedInterfaces.contains(inv) && !this.matchedStacks.contains(inv.getInventory().getGasStack(z + (row * 5)))) {
+ tankSlot = new SlotGasTank(inv.getInventory(), z + (row * 5), z + (row * 5), (z * 18 + 22), offset, 16, 16, true);
+ } else {
+ tankSlot = new SlotGasTank(inv.getInventory(), z + (row * 5), z + (row * 5), (z * 18 + 22), offset, 16, 16);
+ }
+ this.guiSlots.add(tankSlot);
+ guiGasTankClientDCInternalGasInvMap.put(tankSlot, inv);
+ }
+ linesDraw++;
+ offset += 18;
+ }
+ } else if (lineObj instanceof String) {
+ String name = (String) lineObj;
+ final int rows = this.byName.get(name).size();
+ if (rows > 1) {
+ name = name + " (" + rows + ')';
+ }
+
+ while (name.length() > 2 && this.fontRenderer.getStringWidth(name) > 155) {
+ name = name.substring(0, name.length() - 1);
+ }
+ this.fontRenderer.drawString(name, this.offsetX + 2, 5 + offset, 4210752);
+ linesDraw++;
+ offset += 18;
+ }
+ }
+
+ if (searchFieldInputs.isMouseIn(mouseX, mouseY)) {
+ drawTooltip(Mouse.getEventX() * this.width / this.mc.displayWidth - offsetX, mouseY - guiTop, "Inputs OR names");
+ }
+ }
+
+ @Override
+ protected void mouseClicked(final int xCoord, final int yCoord, final int btn) throws IOException {
+ this.searchFieldInputs.mouseClicked(xCoord, yCoord, btn);
+
+ if (btn == 1 && this.searchFieldInputs.isMouseIn(xCoord, yCoord)) {
+ this.searchFieldInputs.setText("");
+ this.refreshList();
+ }
+
+ for (GuiCustomSlot slot : this.guiSlots) {
+ if (slot instanceof SlotGasTank) {
+ if (this.isPointInRegion(slot.xPos(), slot.yPos(), slot.getWidth(), slot.getHeight(), xCoord, yCoord) && slot.canClick(this.mc.player)) {
+ NetworkHandler.instance().sendToServer(new PacketInventoryAction(InventoryAction.PICKUP_OR_SET_DOWN, slot.getId(), guiGasTankClientDCInternalGasInvMap.get(slot).getId()));
+ return;
+ }
+ }
+ }
+
+ super.mouseClicked(xCoord, yCoord, btn);
+ }
+
+ @Override
+ protected void actionPerformed(@Nonnull final GuiButton btn) throws IOException {
+ if (guiButtonHashMap.containsKey(btn)) {
+ BlockPos blockPos = blockPosHashMap.get(guiButtonHashMap.get(this.selectedButton));
+ BlockPos blockPos2 = mc.player.getPosition();
+ int playerDim = mc.world.provider.getDimension();
+ int interfaceDim = dimHashMap.get(guiButtonHashMap.get(this.selectedButton));
+ if (playerDim != interfaceDim) {
+ try {
+ mc.player.sendStatusMessage(new TextComponentString("Gas interface located at dimension: " + interfaceDim + " [" + DimensionManager.getWorld(interfaceDim).provider.getDimensionType().getName() + "] and cant be highlighted"), false);
+ } catch (Exception e) {
+ mc.player.sendStatusMessage(new TextComponentString("Gas interface is located in another dimension and cannot be highlighted"), false);
+ }
+ } else {
+ hilightBlock(blockPos, System.currentTimeMillis() + 500 * BlockPosUtils.getDistance(blockPos, blockPos2), playerDim);
+ mc.player.sendStatusMessage(new TextComponentString("The gas interface is now highlighted at " + "X: " + blockPos.getX() + " Y: " + blockPos.getY() + " Z: " + blockPos.getZ()), false);
+ }
+ mc.player.closeScreen();
+ }
+ }
+
+ @Override
+ public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
+ this.bindTexture("guis/interfaceconfigurationterminal.png");
+ this.drawTexturedModalRect(offsetX, offsetY, 0, 0, this.xSize, this.ySize);
+
+ int offset = 29;
+ final int ex = this.getScrollBar().getCurrentScroll();
+ int linesDraw = 0;
+ for (int x = 0; x < LINES_ON_PAGE && linesDraw < LINES_ON_PAGE && ex + x < this.lines.size(); x++) {
+ final Object lineObj = this.lines.get(ex + x);
+ if (lineObj instanceof ClientDCInternalGasInv) {
+ GlStateManager.color(1, 1, 1, 1);
+ final int width = DualityGasInterface.NUMBER_OF_TANKS * 18;
+
+ int extraLines = numUpgradesMap.get(lineObj);
+
+ for (int row = 0; row < 1 + extraLines && linesDraw < LINES_ON_PAGE; ++row) {
+ this.drawTexturedModalRect(offsetX + 20, offsetY + offset, 20, 170, width, 18);
+ offset += 18;
+ linesDraw++;
+ }
+ } else {
+ offset += 18;
+ linesDraw++;
+ }
+ }
+
+ if (this.searchFieldInputs != null) {
+ this.searchFieldInputs.drawTextBox();
+ }
+ }
+
+ @Override
+ protected void keyTyped(final char character, final int key) throws IOException {
+ if (!this.checkHotbarKeys(key)) {
+ if (character == ' ' && this.searchFieldInputs.getText().isEmpty() && this.searchFieldInputs.isFocused()) {
+ return;
+ }
+
+ if (this.searchFieldInputs.textboxKeyTyped(character, key)) {
+ this.refreshList();
+ } else {
+ super.keyTyped(character, key);
+ }
+ }
+ }
+
+ public void postUpdate(final NBTTagCompound in) {
+ if (in.getBoolean("clear")) {
+ this.byId.clear();
+ this.refreshList = true;
+ }
+
+ for (final String oKey : in.getKeySet()) {
+ if (oKey.startsWith("=")) {
+ try {
+ final long id = Long.parseLong(oKey.substring(1), Character.MAX_RADIX);
+ final NBTTagCompound invData = in.getCompoundTag(oKey);
+ final ClientDCInternalGasInv current = this.getById(id, invData.getLong("sortBy"), invData.getString("un"));
+ blockPosHashMap.put(current, NBTUtil.getPosFromTag(invData.getCompoundTag("pos")));
+ dimHashMap.put(current, invData.getInteger("dim"));
+ numUpgradesMap.put(current, invData.getInteger("numUpgrades"));
+
+ for (int x = 0; x < current.getInventory().size(); x++) {
+ final String which = Integer.toString(x);
+ if (invData.hasKey(which)) {
+ current.getInventory().setGas(x, GasStack.readFromNBT(invData.getCompoundTag(which)));
+ }
+ }
+ } catch (final NumberFormatException ignored) {
+ }
+ }
+ }
+
+ if (this.refreshList) {
+ this.refreshList = false;
+ this.cachedSearches.clear();
+ this.refreshList();
+ }
+ }
+
+ /**
+ * Rebuilds the list of interfaces.
+ *
+ * Respects a search term if present (ignores case) and adding only matching patterns.
+ */
+ private void refreshList() {
+ this.byName.clear();
+ this.buttonList.clear();
+ this.matchedStacks.clear();
+ this.matchedInterfaces.clear();
+
+ final String searchFieldInputs = this.searchFieldInputs.getText().toLowerCase();
+
+ final Set cachedSearch = this.getCacheForSearchTerm(searchFieldInputs);
+ final boolean rebuild = cachedSearch.isEmpty();
+
+ for (final ClientDCInternalGasInv entry : this.byId.values()) {
+ // ignore inventory if not doing a full rebuild and cache already marks it as miss.
+ if (!rebuild && !cachedSearch.contains(entry)) {
+ continue;
+ }
+
+ // Shortcut to skip any filter if search term is ""/empty
+
+ boolean found = searchFieldInputs.isEmpty();
+
+ // Search if the current inventory holds a pattern containing the search term.
+ if (!found) {
+ int slot = 0;
+ for (int i = 0; i < entry.getInventory().size(); i++) {
+ if (slot > 8 + numUpgradesMap.get(entry) * 9) {
+ break;
+ }
+ GasStack gs = entry.getInventory().getGasStack(i);
+ if (this.gasStackMatchesSearchTerm(gs, searchFieldInputs)) {
+ found = true;
+ matchedStacks.add(gs);
+ }
+ slot++;
+ }
+ }
+ if (searchFieldInputs.isEmpty() || entry.getName().toLowerCase().contains(searchFieldInputs)) {
+ this.matchedInterfaces.add(entry);
+ found = true;
+ }
+ // if found, filter skipped or machine name matching the search term, add it
+ if (found) {
+ this.byName.put(entry.getName(), entry);
+ cachedSearch.add(entry);
+ } else {
+ cachedSearch.remove(entry);
+ }
+ }
+
+ this.names.clear();
+ this.names.addAll(this.byName.keySet());
+
+ Collections.sort(this.names);
+
+ this.lines.clear();
+ this.lines.ensureCapacity(this.getMaxRows());
+
+ for (final String n : this.names) {
+ this.lines.add(n);
+
+ final ArrayList clientInventories = new ArrayList<>();
+ clientInventories.addAll(this.byName.get(n));
+
+ Collections.sort(clientInventories);
+ this.lines.addAll(clientInventories);
+ }
+
+ this.getScrollBar().setRange(0, this.lines.size() - 1, 1);
+ }
+
+ private boolean gasStackMatchesSearchTerm(final GasStack gasStack, final String searchTerm) {
+ if (gasStack == null) {
+ return false;
+ }
+
+ boolean foundMatchingGasStack = false;
+
+ final String displayName = Utils.getGasDisplayName(gasStack).toLowerCase();
+
+ for (String term : searchTerm.split(" ")) {
+ if (term.length() > 1 && (term.startsWith("-") || term.startsWith("!"))) {
+ term = term.substring(1);
+ if (displayName.contains(term)) {
+ return false;
+ }
+ } else if (displayName.contains(term)) {
+ foundMatchingGasStack = true;
+ } else {
+ return false;
+ }
+ }
+ return foundMatchingGasStack;
+ }
+
+ /**
+ * Tries to retrieve a cache for a with search term as keyword.
+ *
+ * If this cache should be empty, it will populate it with an earlier cache if available or at least the cache for
+ * the empty string.
+ *
+ * @param searchTerm the corresponding search
+ * @return a Set matching a superset of the search term
+ */
+ private Set getCacheForSearchTerm(final String searchTerm) {
+ if (!this.cachedSearches.containsKey(searchTerm)) {
+ this.cachedSearches.put(searchTerm, new HashSet<>());
+ }
+
+ final Set cache = this.cachedSearches.get(searchTerm);
+
+ if (cache.isEmpty() && searchTerm.length() > 1) {
+ cache.addAll(this.getCacheForSearchTerm(searchTerm.substring(0, searchTerm.length() - 1)));
+ return cache;
+ }
+
+ return cache;
+ }
+
+ /**
+ * The max amount of unique names and each inv row. Not affected by the filtering.
+ *
+ * @return max amount of unique names and each inv row
+ */
+ private int getMaxRows() {
+ return this.names.size() + this.byId.size();
+ }
+
+ private ClientDCInternalGasInv getById(final long id, final long sortBy, final String string) {
+ ClientDCInternalGasInv o = this.byId.get(id);
+
+ if (o == null) {
+ this.byId.put(id, o = new ClientDCInternalGasInv(DualityGasInterface.NUMBER_OF_TANKS, id, sortBy, string, 1000));
+ this.refreshList = true;
+ }
+
+ return o;
+ }
+
+ @Override
+ public List> getPhantomTargets(Object ingredient) {
+ GasStack gas = null;
+ if (ingredient instanceof GasStack) {
+ gas = (GasStack) ingredient;
+ } else if (ingredient instanceof ItemStack) {
+ gas = Utils.getGasFromItem((ItemStack) ingredient);
+ }
+ if (gas != null) {
+ final GasStack imGas = gas;
+ this.mapTargetSlot.clear();
+ List> targets = new ArrayList<>();
+ List slots = new ArrayList<>();
+ if (!this.getGuiSlots().isEmpty()) {
+ for (GuiCustomSlot slot : this.getGuiSlots()) {
+ if (slot instanceof SlotGasTank) {
+ slots.add((SlotGasTank) slot);
+ }
+ }
+ }
+ for (SlotGasTank slot : slots) {
+ IGhostIngredientHandler.Target targetItem = new IGhostIngredientHandler.Target() {
+ @Nonnull
+ @Override
+ public Rectangle getArea() {
+ if (slot.isSlotEnabled()) {
+ return new Rectangle(getGuiLeft() + slot.xPos(), getGuiTop() + slot.yPos(), 16, 16);
+ }
+ return new Rectangle();
+ }
+
+ @Override
+ public void accept(@Nonnull Object o) {
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("jei_set", slot.getId(), guiGasTankClientDCInternalGasInvMap.get(slot).getId(), AEGasStack.of(imGas)));
+ }
+ };
+ targets.add(targetItem);
+ this.mapTargetSlot.putIfAbsent(targetItem, slot);
+ }
+ return targets;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+
+ @Override
+ public Map, Object> getFakeSlotTargetMap() {
+ return this.mapTargetSlot;
+ }
+
+ @Nonnull
+ @Override
+ public Map> getActionMap() {
+ return this.holder;
+ }
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasLevelEmitter.java b/src/main/java/com/mekeng/github/client/gui/GuiGasLevelEmitter.java
new file mode 100644
index 0000000..35aeb59
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiGasLevelEmitter.java
@@ -0,0 +1,176 @@
+package com.mekeng.github.client.gui;
+
+import appeng.api.config.RedstoneMode;
+import appeng.api.config.Settings;
+import appeng.client.gui.widgets.GuiImgButton;
+import appeng.client.gui.widgets.GuiNumberBox;
+import appeng.core.AEConfig;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.client.slots.SlotGas;
+import com.mekeng.github.common.container.ContainerGasLevelEmitter;
+import com.mekeng.github.common.part.PartGasLevelEmitter;
+import com.mekeng.github.network.packet.CGenericPacket;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.InventoryPlayer;
+
+import java.io.IOException;
+
+public class GuiGasLevelEmitter extends GuiGasUpgradeable {
+ private final PartGasLevelEmitter levelEmitter;
+ private GuiNumberBox level;
+
+ private GuiButton plus1;
+ private GuiButton plus10;
+ private GuiButton plus100;
+ private GuiButton plus1000;
+ private GuiButton minus1;
+ private GuiButton minus10;
+ private GuiButton minus100;
+ private GuiButton minus1000;
+
+ public GuiGasLevelEmitter(final InventoryPlayer inventoryPlayer, final PartGasLevelEmitter te) {
+ super(new ContainerGasLevelEmitter(inventoryPlayer, te));
+ this.levelEmitter = te;
+ }
+
+ @Override
+ public void initGui() {
+ super.initGui();
+
+ this.level = new GuiNumberBox(this.fontRenderer, this.guiLeft + 24, this.guiTop + 43, 79, this.fontRenderer.FONT_HEIGHT, Long.class);
+ this.level.setEnableBackgroundDrawing(false);
+ this.level.setMaxStringLength(16);
+ this.level.setTextColor(0xFFFFFF);
+ this.level.setVisible(true);
+ this.level.setFocused(true);
+ ((ContainerGasLevelEmitter) this.inventorySlots).setTextField(this.level);
+
+ final int y = 40;
+ final int x = 80 + 44;
+ this.guiSlots.add(new SlotGas(this.levelEmitter.getConfig(), 0, 0, x, y));
+ }
+
+ @Override
+ protected void addButtons() {
+ this.redstoneMode = new GuiImgButton(this.guiLeft - 18, this.guiTop + 28, Settings.REDSTONE_EMITTER, RedstoneMode.LOW_SIGNAL);
+
+ final int a = AEConfig.instance().levelByMillyBuckets(0);
+ final int b = AEConfig.instance().levelByMillyBuckets(1);
+ final int c = AEConfig.instance().levelByMillyBuckets(2);
+ final int d = AEConfig.instance().levelByMillyBuckets(3);
+
+ this.buttonList.add(this.plus1 = new GuiButton(0, this.guiLeft + 20, this.guiTop + 17, 22, 20, "+" + a));
+ this.buttonList.add(this.plus10 = new GuiButton(0, this.guiLeft + 48, this.guiTop + 17, 28, 20, "+" + b));
+ this.buttonList.add(this.plus100 = new GuiButton(0, this.guiLeft + 82, this.guiTop + 17, 32, 20, "+" + c));
+ this.buttonList.add(this.plus1000 = new GuiButton(0, this.guiLeft + 120, this.guiTop + 17, 38, 20, "+" + d));
+
+ this.buttonList.add(this.minus1 = new GuiButton(0, this.guiLeft + 20, this.guiTop + 59, 22, 20, "-" + a));
+ this.buttonList.add(this.minus10 = new GuiButton(0, this.guiLeft + 48, this.guiTop + 59, 28, 20, "-" + b));
+ this.buttonList.add(this.minus100 = new GuiButton(0, this.guiLeft + 82, this.guiTop + 59, 32, 20, "-" + c));
+ this.buttonList.add(this.minus1000 = new GuiButton(0, this.guiLeft + 120, this.guiTop + 59, 38, 20, "-" + d));
+
+ this.buttonList.add(this.redstoneMode);
+ }
+
+ @Override
+ public void drawBG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
+ super.drawBG(offsetX, offsetY, mouseX, mouseY);
+ this.level.drawTextBox();
+ }
+
+ @Override
+ public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) {
+ if (isPointInRegion(24, 43, 89, this.fontRenderer.FONT_HEIGHT, mouseX, mouseY))
+ drawTooltip(mouseX - guiLeft - 7, mouseY - guiTop + 25, "Amount in millibuckets");
+ super.drawFG(offsetX, offsetY, mouseX, mouseY);
+ }
+
+ @Override
+ protected boolean drawUpgrades() {
+ return false;
+ }
+
+ @Override
+ protected String getBackground() {
+ return "guis/lvlemitter.png";
+ }
+
+ @Override
+ protected String getGuiName() {
+ return I18n.format("gui.mekeng.gas_level_emitter");
+ }
+
+ @Override
+ protected void handleButtonVisibility() {
+ // NO-OP
+ }
+
+ @Override
+ protected void actionPerformed(final GuiButton btn) throws IOException {
+ super.actionPerformed(btn);
+ final boolean isPlus = btn == this.plus1 || btn == this.plus10 || btn == this.plus100 || btn == this.plus1000;
+ final boolean isMinus = btn == this.minus1 || btn == this.minus10 || btn == this.minus100 || btn == this.minus1000;
+ if (isPlus || isMinus) {
+ this.addQty(this.getQty(btn));
+ }
+ }
+
+ private void addQty(final long i) {
+ try {
+ String Out = this.level.getText();
+
+ boolean Fixed = false;
+ while (Out.startsWith("0") && Out.length() > 1) {
+ Out = Out.substring(1);
+ Fixed = true;
+ }
+
+ if (Fixed) {
+ this.level.setText(Out);
+ }
+
+ if (Out.isEmpty()) {
+ Out = "0";
+ }
+
+ long result = Long.parseLong(Out);
+ result += i;
+ if (result < 0) {
+ result = 0;
+ }
+
+ this.level.setText(Out = Long.toString(result));
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("set_level", Long.parseLong(Out)));
+ } catch (final NumberFormatException e) {
+ // nope..
+ this.level.setText("0");
+ }
+ }
+
+ @Override
+ protected void keyTyped(final char character, final int key) throws IOException {
+ if (!this.checkHotbarKeys(key)) {
+ if ((key == 211 || key == 205 || key == 203 || key == 14 || Character.isDigit(character)) && this.level.textboxKeyTyped(character, key)) {
+ String Out = this.level.getText();
+
+ boolean Fixed = false;
+ while (Out.startsWith("0") && Out.length() > 1) {
+ Out = Out.substring(1);
+ Fixed = true;
+ }
+
+ if (Fixed) {
+ this.level.setText(Out);
+ }
+
+ if (Out.isEmpty()) {
+ Out = "0";
+ }
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("set_level", Long.parseLong(Out)));
+ } else {
+ super.keyTyped(character, key);
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasStorageBus.java b/src/main/java/com/mekeng/github/client/gui/GuiGasStorageBus.java
new file mode 100644
index 0000000..76908d6
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiGasStorageBus.java
@@ -0,0 +1,129 @@
+package com.mekeng.github.client.gui;
+
+import appeng.api.config.AccessRestriction;
+import appeng.api.config.ActionItems;
+import appeng.api.config.FuzzyMode;
+import appeng.api.config.Settings;
+import appeng.api.config.StorageFilter;
+import appeng.client.gui.widgets.GuiImgButton;
+import appeng.client.gui.widgets.GuiTabButton;
+import appeng.core.localization.GuiText;
+import appeng.core.sync.GuiBridge;
+import appeng.core.sync.network.NetworkHandler;
+import appeng.core.sync.packets.PacketConfigButton;
+import appeng.core.sync.packets.PacketSwitchGuis;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.client.slots.SlotGas;
+import com.mekeng.github.client.slots.SlotOptionalGas;
+import com.mekeng.github.common.container.ContainerGasStorageBus;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.part.PartGasStorageBus;
+import com.mekeng.github.network.packet.CGenericPacket;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.InventoryPlayer;
+import org.lwjgl.input.Mouse;
+
+import java.io.IOException;
+
+public class GuiGasStorageBus extends GuiGasUpgradeable {
+ private GuiImgButton rwMode;
+ private GuiImgButton storageFilter;
+ private GuiTabButton priority;
+ private GuiImgButton partition;
+ private GuiImgButton clear;
+ private final PartGasStorageBus bus;
+
+ public GuiGasStorageBus(InventoryPlayer inventoryPlayer, PartGasStorageBus te) {
+ super(new ContainerGasStorageBus(inventoryPlayer, te));
+ this.ySize = 251;
+ this.bus = te;
+ }
+
+ @Override
+ public void initGui() {
+ super.initGui();
+
+ final int xo = 8;
+ final int yo = 23 + 6;
+
+ final IGasInventory config = this.bus.getConfig();
+ final ContainerGasStorageBus container = (ContainerGasStorageBus) this.inventorySlots;
+
+ for (int y = 0; y < 7; y++) {
+ for (int x = 0; x < 9; x++) {
+ final int idx = y * 9 + x;
+ if (y < 2) {
+ this.guiSlots.add(new SlotGas(config, idx, idx, xo + x * 18, yo + y * 18));
+ } else {
+ this.guiSlots.add(new SlotOptionalGas(config, container, idx, idx, y - 2, xo, yo, x, y));
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void addButtons() {
+ this.clear = new GuiImgButton(this.guiLeft - 18, this.guiTop + 8, Settings.ACTIONS, ActionItems.CLOSE);
+ this.partition = new GuiImgButton(this.guiLeft - 18, this.guiTop + 28, Settings.ACTIONS, ActionItems.WRENCH);
+ this.rwMode = new GuiImgButton(this.guiLeft - 18, this.guiTop + 48, Settings.ACCESS, AccessRestriction.READ_WRITE);
+ this.storageFilter = new GuiImgButton(this.guiLeft - 18, this.guiTop + 68, Settings.STORAGE_FILTER, StorageFilter.EXTRACTABLE_ONLY);
+ this.fuzzyMode = new GuiImgButton(this.guiLeft - 18, this.guiTop + 88, Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL);
+
+ this.buttonList.add(this.priority = new GuiTabButton(this.guiLeft + 154, this.guiTop, 2 + 4 * 16, GuiText.Priority.getLocal(), this.itemRender));
+
+ this.buttonList.add(this.storageFilter);
+ this.buttonList.add(this.fuzzyMode);
+ this.buttonList.add(this.rwMode);
+ this.buttonList.add(this.partition);
+ this.buttonList.add(this.clear);
+ }
+
+ @Override
+ public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
+ this.fontRenderer.drawString(this.getGuiDisplayName(this.getGuiName()), 8, 6, 4210752);
+ this.fontRenderer.drawString(GuiText.inventory.getLocal(), 8, this.ySize - 96 + 3, 4210752);
+
+ if (this.fuzzyMode != null) {
+ this.fuzzyMode.set(this.cvb.getFuzzyMode());
+ }
+
+ if (this.storageFilter != null) {
+ this.storageFilter.set(((ContainerGasStorageBus) this.cvb).getStorageFilter());
+ }
+
+ if (this.rwMode != null) {
+ this.rwMode.set(((ContainerGasStorageBus) this.cvb).getReadWriteMode());
+ }
+ }
+
+ @Override
+ protected String getGuiName() {
+ return I18n.format("gui.mekeng.gas_storage_bus");
+ }
+
+ @Override
+ protected void actionPerformed(final GuiButton btn) throws IOException {
+ super.actionPerformed(btn);
+
+ final boolean backwards = Mouse.isButtonDown(1);
+
+ if (btn == this.partition) {
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("partition"));
+ } else if (btn == this.clear) {
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("clear"));
+ } else if (btn == this.priority) {
+ NetworkHandler.instance().sendToServer(new PacketSwitchGuis(GuiBridge.GUI_PRIORITY));
+ } else if (btn == this.rwMode) {
+ NetworkHandler.instance().sendToServer(new PacketConfigButton(this.rwMode.getSetting(), backwards));
+ } else if (btn == this.storageFilter) {
+ NetworkHandler.instance().sendToServer(new PacketConfigButton(this.storageFilter.getSetting(), backwards));
+ }
+ }
+
+ @Override
+ protected String getBackground() {
+ return "guis/storagebus.png";
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasTerminal.java b/src/main/java/com/mekeng/github/client/gui/GuiGasTerminal.java
new file mode 100644
index 0000000..6396715
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiGasTerminal.java
@@ -0,0 +1,341 @@
+package com.mekeng.github.client.gui;
+
+import appeng.api.config.Settings;
+import appeng.api.storage.ITerminalHost;
+import appeng.api.util.IConfigManager;
+import appeng.api.util.IConfigurableObject;
+import appeng.client.gui.AEBaseMEGui;
+import appeng.client.gui.widgets.GuiImgButton;
+import appeng.client.gui.widgets.GuiScrollbar;
+import appeng.client.gui.widgets.ISortSource;
+import appeng.client.gui.widgets.MEGuiTextField;
+import appeng.core.localization.GuiText;
+import appeng.core.sync.network.NetworkHandler;
+import appeng.core.sync.packets.PacketInventoryAction;
+import appeng.core.sync.packets.PacketValueConfig;
+import appeng.helpers.InventoryAction;
+import appeng.util.IConfigManagerHost;
+import appeng.util.Platform;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.client.render.GasStackSizeRenderer;
+import com.mekeng.github.client.slots.SlotGasME;
+import com.mekeng.github.common.container.ContainerGasTerminal;
+import com.mekeng.github.common.me.client.GasRepo;
+import com.mekeng.github.common.me.client.RepoSlot;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.network.packet.CGenericPacket;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.Gas;
+import mekanism.client.render.MekanismRenderer;
+import net.minecraft.client.gui.GuiButton;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.client.resources.I18n;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.ClickType;
+import net.minecraft.inventory.Slot;
+import net.minecraft.util.text.TextFormatting;
+import net.minecraftforge.fml.common.Loader;
+import org.lwjgl.input.Mouse;
+
+import javax.annotation.Nonnull;
+import java.awt.*;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+
+public class GuiGasTerminal extends AEBaseMEGui implements ISortSource, IConfigManagerHost {
+ private final List meGasSlots = new LinkedList<>();
+ private final GasRepo repo;
+ private final IConfigManager configSrc;
+ private final ContainerGasTerminal container;
+ private final GasStackSizeRenderer gasStackSizeRenderer = new GasStackSizeRenderer();
+ private final int offsetX = 9;
+ private final int rows = 6;
+ private final int perRow = 9;
+
+ protected ITerminalHost terminal;
+
+ private MEGuiTextField searchField;
+ private GuiImgButton sortByBox;
+ private GuiImgButton sortDirBox;
+ protected int jeiOffset = Loader.isModLoaded("jei") ? 24 : 0;
+
+ public GuiGasTerminal(final InventoryPlayer inventoryPlayer, final ITerminalHost te) {
+ this(inventoryPlayer, te, new ContainerGasTerminal(inventoryPlayer, te));
+ }
+
+ public GuiGasTerminal(InventoryPlayer inventoryPlayer, final ITerminalHost te, final ContainerGasTerminal c) {
+ super(c);
+ this.terminal = te;
+ this.xSize = 190;
+ this.ySize = 222;
+ final GuiScrollbar scrollbar = new GuiScrollbar();
+ this.setScrollBar(scrollbar);
+ this.repo = new GasRepo(scrollbar, this);
+ this.configSrc = ((IConfigurableObject) this.inventorySlots).getConfigManager();
+ (this.container = (ContainerGasTerminal) this.inventorySlots).setGui(this);
+ }
+
+ protected String getName() {
+ return "gui.mekeng.gas_terminal";
+ }
+
+ @Override
+ public void initGui() {
+ this.mc.player.openContainer = this.inventorySlots;
+ this.guiLeft = (this.width - this.xSize) / 2;
+ this.guiTop = (this.height - this.ySize) / 2;
+
+ this.searchField = new MEGuiTextField(this.fontRenderer, this.guiLeft + 80, this.guiTop + 4, 90, 12);
+ this.searchField.setEnableBackgroundDrawing(false);
+ this.searchField.setMaxStringLength(25);
+ this.searchField.setTextColor(0xFFFFFF);
+ this.searchField.setSelectionColor(0xFF99FF99);
+ this.searchField.setVisible(true);
+
+ int offset = this.guiTop;
+
+ this.buttonList.add(this.sortByBox = new GuiImgButton(this.guiLeft - 18, offset, Settings.SORT_BY, this.configSrc.getSetting(Settings.SORT_BY)));
+ offset += 20;
+
+ this.buttonList.add(this.sortDirBox = new GuiImgButton(this.guiLeft - 18, offset, Settings.SORT_DIRECTION, this.configSrc.getSetting(Settings.SORT_DIRECTION)));
+
+ for (int y = 0; y < this.rows; y++) {
+ for (int x = 0; x < this.perRow; x++) {
+ SlotGasME slot = new SlotGasME(new RepoSlot(this.repo, x + y * this.perRow, this.offsetX + x * 18, 18 + y * 18));
+ this.getMeGasSlots().add(slot);
+ this.inventorySlots.inventorySlots.add(slot);
+ }
+ }
+ this.setScrollBar();
+ }
+
+ @Override
+ public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) {
+ this.fontRenderer.drawString(this.getGuiDisplayName(I18n.format(this.getName())), 8, 6, 4210752);
+ this.fontRenderer.drawString(GuiText.inventory.getLocal(), 8, this.ySize - 96 + 3, 4210752);
+ }
+
+ @Override
+ public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) {
+ this.bindTexture(this.getBackground());
+ final int x_width = 197;
+ this.drawTexturedModalRect(offsetX, offsetY, 0, 0, x_width, 18);
+
+ for (int x = 0; x < 6; x++) {
+ this.drawTexturedModalRect(offsetX, offsetY + 18 + x * 18, 0, 18, x_width, 18);
+ }
+
+ this.drawTexturedModalRect(offsetX, offsetY + 16 + 6 * 18, 0, 106 - 18 - 18, x_width, 99 + 77);
+
+ if (this.searchField != null) {
+ this.searchField.drawTextBox();
+ }
+ }
+
+ @Override
+ public void drawSlot(Slot s) {
+ if (s instanceof SlotGasME && ((SlotGasME) s).shouldRenderAsGas()) {
+ final SlotGasME slot = (SlotGasME) s;
+ final IAEGasStack gs = slot.getAEGasStack();
+ if (gs != null && this.isPowered()) {
+ GlStateManager.enableLighting();
+ GlStateManager.enableBlend();
+ final Gas gas = gs.getGas();
+ mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
+ final TextureAtlasSprite sprite = gas.getSprite();
+ // Set color for dynamic gases
+ // Convert int color to RGB
+ MekanismRenderer.color(gas);
+ this.drawTexturedModalRect(s.xPos, s.yPos, sprite, 16, 16);
+ MekanismRenderer.resetColor();
+ this.gasStackSizeRenderer.renderStackSize(this.fontRenderer, gs, s.xPos, s.yPos);
+ } else if (!this.isPowered()) {
+ drawRect(s.xPos, s.yPos, 16 + s.xPos, 16 + s.yPos, 0x66111111);
+ }
+ } else {
+ super.drawSlot(s);
+ }
+ }
+
+ @Override
+ public void updateScreen() {
+ this.repo.setPower(this.container.isPowered());
+ super.updateScreen();
+ }
+
+ @Override
+ protected void renderHoveredToolTip(int mouseX, int mouseY) {
+ final Slot slot = this.getSlot(mouseX, mouseY);
+
+ if (slot instanceof SlotGasME && slot.isEnabled()) {
+ final SlotGasME gasSlot = (SlotGasME) slot;
+
+ if (gasSlot.getAEGasStack() != null && gasSlot.shouldRenderAsGas()) {
+ final IAEGasStack gasStack = gasSlot.getAEGasStack();
+ final String formattedAmount = NumberFormat.getNumberInstance(Locale.US).format(gasStack.getStackSize() / 1000.0) + " B";
+
+ final String modName = "" + TextFormatting.BLUE + TextFormatting.ITALIC + Loader.instance()
+ .getIndexedModList()
+ .get(Utils.getGasModID(gasStack))
+ .getName();
+
+ final List list = new ArrayList<>();
+
+ list.add(gasStack.getGas().getLocalizedName());
+ list.add(formattedAmount);
+ list.add(modName);
+
+ this.drawHoveringText(list, mouseX, mouseY);
+
+ return;
+ }
+ }
+ super.renderHoveredToolTip(mouseX, mouseY);
+ }
+
+ @Override
+ protected void actionPerformed(@Nonnull GuiButton btn) {
+ if (btn instanceof GuiImgButton) {
+ final boolean backwards = Mouse.isButtonDown(1);
+ final GuiImgButton iBtn = (GuiImgButton) btn;
+
+ if (iBtn.getSetting() != Settings.ACTIONS) {
+ final Enum> cv = iBtn.getCurrentValue();
+ final Enum> next = Platform.rotateEnum(cv, backwards, iBtn.getSetting().getPossibleValues());
+ try {
+ NetworkHandler.instance().sendToServer(new PacketValueConfig(iBtn.getSetting().name(), next.name()));
+ } catch (final IOException e) {
+ MekEng.log.debug(e);
+ }
+ iBtn.set(next);
+ }
+ }
+ }
+
+ @Override
+ protected void handleMouseClick(Slot slot, int slotIdx, int mouseButton, ClickType clickType) {
+ if (slot instanceof SlotGasME) {
+ final SlotGasME meSlot = (SlotGasME) slot;
+
+ if (clickType == ClickType.PICKUP) {
+ if (mouseButton == 0 && meSlot.getHasStack()) {
+ this.container.setTargetGasStack(meSlot.getAEGasStack());
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("set_target", meSlot.getAEGasStack()));
+ MekEng.log.debug("mouse0 GUI STACK SIZE %s", meSlot.getAEGasStack().getStackSize());
+ NetworkHandler.instance().sendToServer(new PacketInventoryAction(InventoryAction.FILL_ITEM, slot.slotNumber, 0));
+ } else {
+ this.container.setTargetGasStack(meSlot.getAEGasStack());
+ MekEng.proxy.netHandler.sendToServer(new CGenericPacket("set_target", meSlot.getAEGasStack()));
+ if (meSlot.getAEGasStack() != null) {
+ MekEng.log.debug("mouse1 GUI STACK SIZE %s", meSlot.getAEGasStack().getStackSize());
+ }
+ NetworkHandler.instance().sendToServer(new PacketInventoryAction(InventoryAction.EMPTY_ITEM, slot.slotNumber, 0));
+ }
+ }
+ return;
+ }
+ super.handleMouseClick(slot, slotIdx, mouseButton, clickType);
+ }
+
+ @Override
+ protected void keyTyped(final char character, final int key) throws IOException {
+ if (!this.checkHotbarKeys(key)) {
+ if (character == ' ' && this.searchField.getText().isEmpty()) {
+ return;
+ }
+ if (this.searchField.textboxKeyTyped(character, key)) {
+ this.repo.setSearchString(this.searchField.getText());
+ this.repo.updateView();
+ this.setScrollBar();
+ } else {
+ super.keyTyped(character, key);
+ }
+ }
+ }
+
+ @Override
+ protected void mouseClicked(final int xCoord, final int yCoord, final int btn) throws IOException {
+ this.searchField.mouseClicked(xCoord, yCoord, btn);
+ if (btn == 1 && this.searchField.isMouseIn(xCoord, yCoord)) {
+ this.searchField.setText("");
+ this.repo.setSearchString("");
+ this.repo.updateView();
+ this.setScrollBar();
+ }
+ super.mouseClicked(xCoord, yCoord, btn);
+ }
+
+ public void postUpdate(final List list) {
+ for (final IAEGasStack is : list) {
+ this.repo.postUpdate(is);
+ }
+ this.repo.updateView();
+ this.setScrollBar();
+ }
+
+ private void setScrollBar() {
+ this.getScrollBar().setTop(18).setLeft(175).setHeight(this.rows * 18 - 2);
+ this.getScrollBar().setRange(0, (this.repo.size() + this.perRow - 1) / this.perRow - this.rows, Math.max(1, this.rows / 6));
+ }
+
+ @Override
+ public Enum> getSortBy() {
+ return this.configSrc.getSetting(Settings.SORT_BY);
+ }
+
+ @Override
+ public Enum> getSortDir() {
+ return this.configSrc.getSetting(Settings.SORT_DIRECTION);
+ }
+
+ @Override
+ public Enum> getSortDisplay() {
+ return this.configSrc.getSetting(Settings.VIEW_MODE);
+ }
+
+ @Override
+ public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
+ if (this.sortByBox != null) {
+ this.sortByBox.set(this.configSrc.getSetting(Settings.SORT_BY));
+ }
+
+ if (this.sortDirBox != null) {
+ this.sortDirBox.set(this.configSrc.getSetting(Settings.SORT_DIRECTION));
+ }
+
+ this.repo.updateView();
+ }
+
+ protected List getMeGasSlots() {
+ return this.meGasSlots;
+ }
+
+ @Override
+ protected boolean isPowered() {
+ return this.repo.hasPower();
+ }
+
+ protected String getBackground() {
+ return "guis/terminal.png";
+ }
+
+ @Override
+ public List getJEIExclusionArea() {
+ List exclusionArea = new ArrayList<>();
+
+ int yOffset = guiTop + 8 + jeiOffset;
+
+ int visibleButtons = 2;
+ Rectangle sortDir = new Rectangle(guiLeft - 18, yOffset, 20, visibleButtons * 20 + visibleButtons - 2);
+ exclusionArea.add(sortDir);
+
+ return exclusionArea;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasUpgradeable.java b/src/main/java/com/mekeng/github/client/gui/GuiGasUpgradeable.java
new file mode 100644
index 0000000..76637e6
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiGasUpgradeable.java
@@ -0,0 +1,108 @@
+package com.mekeng.github.client.gui;
+
+import appeng.api.implementations.IUpgradeableHost;
+import appeng.client.gui.implementations.GuiUpgradeable;
+import appeng.client.gui.widgets.GuiCustomSlot;
+import appeng.container.implementations.ContainerUpgradeable;
+import appeng.core.localization.GuiText;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.client.slots.SlotGas;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.network.packet.CGasSlotSync;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.GasStack;
+import mezz.jei.api.gui.IGhostIngredientHandler;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.item.ItemStack;
+
+import javax.annotation.Nonnull;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class GuiGasUpgradeable extends GuiUpgradeable {
+
+ public GuiGasUpgradeable(InventoryPlayer inventoryPlayer, IUpgradeableHost te) {
+ super(inventoryPlayer, te);
+ }
+
+ public GuiGasUpgradeable(ContainerUpgradeable te) {
+ super(te);
+ }
+
+ @Override
+ public void drawFG(final int offsetX, final int offsetY, final int mouseX, final int mouseY) {
+ this.fontRenderer.drawString(this.getGuiDisplayName(this.getGuiName()), 8, 6, 4210752);
+ this.fontRenderer.drawString(GuiText.inventory.getLocal(), 8, this.ySize - 96 + 3, 4210752);
+
+ if (this.redstoneMode != null) {
+ this.redstoneMode.set(this.cvb.getRedStoneMode());
+ }
+
+ if (this.fuzzyMode != null) {
+ this.fuzzyMode.set(this.cvb.getFuzzyMode());
+ }
+
+ if (this.craftMode != null) {
+ this.craftMode.set(this.cvb.getCraftingMode());
+ }
+
+ if (this.schedulingMode != null) {
+ this.schedulingMode.set(this.cvb.getSchedulingMode());
+ }
+ }
+
+ @Override
+ protected String getBackground() {
+ return "guis/gas_bus.png";
+ }
+
+ protected abstract String getGuiName();
+
+ @Override
+ public List> getPhantomTargets(Object ingredient) {
+ GasStack gas = null;
+ if (ingredient instanceof GasStack) {
+ gas = (GasStack) ingredient;
+ } else if (ingredient instanceof ItemStack) {
+ gas = Utils.getGasFromItem((ItemStack) ingredient);
+ }
+ if (gas != null) {
+ final GasStack imGas = gas;
+ this.mapTargetSlot.clear();
+ List> targets = new ArrayList<>();
+ List slots = new ArrayList<>();
+ if (!this.getGuiSlots().isEmpty()) {
+ for (GuiCustomSlot slot : this.getGuiSlots()) {
+ if (slot instanceof SlotGas) {
+ slots.add((SlotGas) slot);
+ }
+ }
+ }
+ for (SlotGas slot : slots) {
+ IGhostIngredientHandler.Target targetItem = new IGhostIngredientHandler.Target() {
+ @Nonnull
+ @Override
+ public Rectangle getArea() {
+ if (slot.isSlotEnabled()) {
+ return new Rectangle(getGuiLeft() + slot.xPos(), getGuiTop() + slot.yPos(), 16, 16);
+ }
+ return new Rectangle();
+ }
+
+ @Override
+ public void accept(@Nonnull Object o) {
+ MekEng.proxy.netHandler.sendToServer(new CGasSlotSync(Collections.singletonMap(slot.getId(), AEGasStack.of(imGas))));
+ }
+ };
+ targets.add(targetItem);
+ this.mapTargetSlot.putIfAbsent(targetItem, slot);
+ }
+ return targets;
+ } else {
+ return super.getPhantomTargets(ingredient);
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiMEPortableGasCell.java b/src/main/java/com/mekeng/github/client/gui/GuiMEPortableGasCell.java
new file mode 100644
index 0000000..ac0d5e8
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiMEPortableGasCell.java
@@ -0,0 +1,18 @@
+package com.mekeng.github.client.gui;
+
+import com.mekeng.github.common.container.ContainerMEPortableGasCell;
+import com.mekeng.github.common.me.storage.IPortableGasCell;
+import net.minecraft.entity.player.InventoryPlayer;
+
+public class GuiMEPortableGasCell extends GuiGasTerminal {
+
+ public GuiMEPortableGasCell(final InventoryPlayer inventoryPlayer, final IPortableGasCell te) {
+ super(inventoryPlayer, te, new ContainerMEPortableGasCell(inventoryPlayer, te));
+ }
+
+ @Override
+ protected String getName() {
+ return "gui.mekeng.gas_portable_cell";
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/gui/GuiWirelessGasTerminal.java b/src/main/java/com/mekeng/github/client/gui/GuiWirelessGasTerminal.java
new file mode 100644
index 0000000..b28f0d4
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/gui/GuiWirelessGasTerminal.java
@@ -0,0 +1,40 @@
+package com.mekeng.github.client.gui;
+
+import appeng.helpers.WirelessTerminalGuiObject;
+import com.mekeng.github.common.container.ContainerWirelessGasTerminal;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.entity.player.InventoryPlayer;
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+
+public class GuiWirelessGasTerminal extends GuiGasTerminal {
+
+ public GuiWirelessGasTerminal(final InventoryPlayer inventoryPlayer, final WirelessTerminalGuiObject te) {
+ super(inventoryPlayer, te, new ContainerWirelessGasTerminal(inventoryPlayer, te));
+ }
+
+ @Override
+ public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) {
+ this.bindTexture("guis/wirelessupgrades.png");
+ Gui.drawModalRectWithCustomSizedTexture(offsetX + 175, offsetY + 131, 0, 0, 32, 32, 32, 32);
+ super.drawBG(offsetX, offsetY, mouseX, mouseY);
+ }
+
+ @Override
+ public java.util.List getJEIExclusionArea() {
+ List ea = new ArrayList<>();
+ ea.add(new Rectangle(this.guiLeft + 174,
+ this.guiTop + 131,
+ 32,
+ 32));
+ return ea;
+ }
+
+ @Override
+ protected String getName() {
+ return "gui.mekeng.wireless_gas_terminal";
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/model/SpecialModel.java b/src/main/java/com/mekeng/github/client/model/SpecialModel.java
new file mode 100644
index 0000000..d59d50d
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/model/SpecialModel.java
@@ -0,0 +1,9 @@
+package com.mekeng.github.client.model;
+
+import net.minecraft.util.ResourceLocation;
+
+public interface SpecialModel {
+
+ ResourceLocation getModelPath();
+
+}
diff --git a/src/main/java/com/mekeng/github/client/render/DummyGasModel.java b/src/main/java/com/mekeng/github/client/render/DummyGasModel.java
new file mode 100644
index 0000000..8e45602
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/render/DummyGasModel.java
@@ -0,0 +1,240 @@
+package com.mekeng.github.client.render;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.mekeng.github.common.ItemAndBlocks;
+import mekanism.api.gas.Gas;
+import mekanism.api.gas.GasStack;
+import mekanism.common.MekanismFluids;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.client.renderer.block.model.IBakedModel;
+import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
+import net.minecraft.client.renderer.block.model.ItemOverrideList;
+import net.minecraft.client.renderer.block.model.ItemTransformVec3f;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.vertex.VertexFormat;
+import net.minecraft.client.resources.IResourceManager;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.world.World;
+import net.minecraftforge.client.model.ICustomModelLoader;
+import net.minecraftforge.client.model.IModel;
+import net.minecraftforge.client.model.ItemLayerModel;
+import net.minecraftforge.common.model.IModelState;
+import net.minecraftforge.common.model.TRSRTransformation;
+import org.apache.commons.lang3.tuple.Pair;
+import org.lwjgl.util.vector.Vector3f;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.vecmath.Matrix4f;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+public class DummyGasModel implements IModel {
+
+ @SuppressWarnings("deprecation")
+ protected static final ItemCameraTransforms CAMERA_TRANSFORMS = new ItemCameraTransforms(
+ new ItemTransformVec3f(new Vector3f(0F, 0F, 0F), new Vector3f(0F, 0.1875F, 0.0625F), new Vector3f(0.55F, 0.55F, 0.55F)),
+ new ItemTransformVec3f(new Vector3f(0F, 0F, 0F), new Vector3f(0F, 0.1875F, 0.0625F), new Vector3f(0.55F, 0.55F, 0.55F)),
+ new ItemTransformVec3f(new Vector3f(0F, -90F, 25F), new Vector3f(0.070625F, 0.2F, 0.070625F), new Vector3f(0.68F, 0.68F, 0.68F)),
+ new ItemTransformVec3f(new Vector3f(0F, -90F, 25F), new Vector3f(0.070625F, 0.2F, 0.070625F), new Vector3f(0.68F, 0.68F, 0.68F)),
+ new ItemTransformVec3f(new Vector3f(0F, 180F, 0F), new Vector3f(0F, 0.8125F, 0.4375F), new Vector3f(1F, 1F, 1F)),
+ ItemTransformVec3f.DEFAULT,
+ new ItemTransformVec3f(new Vector3f(0F, 0F, 0F), new Vector3f(0F, 0.125F, 0F), new Vector3f(0.5F, 0.5F, 0.5F)),
+ new ItemTransformVec3f(new Vector3f(0F, 180F, 0F), new Vector3f(0F, 0F, 0F), new Vector3f(1F, 1F, 1F)));
+
+ @Override
+ @Nonnull
+ public IBakedModel bake(@Nonnull IModelState state, @Nonnull VertexFormat format, @Nonnull Function textureBakery) {
+ return new BakedGasModel(state, format);
+ }
+
+ public static class Loader implements ICustomModelLoader {
+
+ @Override
+ public void onResourceManagerReload(@Nonnull IResourceManager resourceManager) {
+ // NO-OP
+ }
+
+ @Override
+ public boolean accepts(ResourceLocation modelLocation) {
+ return modelLocation.compareTo(ItemAndBlocks.DUMMY_GAS.getModelPath()) == 0;
+ }
+
+ @Override
+ @Nonnull
+ public IModel loadModel(@Nonnull ResourceLocation modelLocation) {
+ return new DummyGasModel();
+ }
+
+ }
+
+ protected static class BakedGasModel implements IBakedModel {
+
+ @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
+ protected final Optional modelTransform;
+ protected final VertexFormat vertexFormat;
+ protected final ItemOverrideList overrides;
+ private final IBakedModel defaultOverride;
+
+ public BakedGasModel(IModelState modelState, VertexFormat vertexFormat) {
+ this.modelTransform = modelState.apply(Optional.empty());
+ this.vertexFormat = vertexFormat;
+ this.overrides = this.genOverrides();
+ this.defaultOverride = this.genDefaultOverrides();
+ }
+
+ @Override
+ @Nonnull
+ public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) {
+ return defaultOverride.getQuads(state, side, rand);
+ }
+
+ @Override
+ public boolean isAmbientOcclusion() {
+ return defaultOverride.isAmbientOcclusion();
+ }
+
+ @Override
+ public boolean isGui3d() {
+ return defaultOverride.isGui3d();
+ }
+
+ @Override
+ public boolean isBuiltInRenderer() {
+ return defaultOverride.isBuiltInRenderer();
+ }
+
+ @Override
+ @Nonnull
+ public TextureAtlasSprite getParticleTexture() {
+ return defaultOverride.getParticleTexture();
+ }
+
+ @Override
+ public boolean isAmbientOcclusion(@Nonnull IBlockState state) {
+ return defaultOverride.isAmbientOcclusion(state);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ @Nonnull
+ public ItemCameraTransforms getItemCameraTransforms() {
+ return defaultOverride.getItemCameraTransforms();
+ }
+
+ @Override
+ @Nonnull
+ public Pair extends IBakedModel, Matrix4f> handlePerspective(@Nonnull ItemCameraTransforms.TransformType cameraTransformType) {
+ return defaultOverride.handlePerspective(cameraTransformType);
+ }
+
+ @Override
+ @Nonnull
+ public ItemOverrideList getOverrides() {
+ return overrides;
+ }
+
+ protected ItemOverrideList genOverrides() {
+ return new OverrideCache();
+ }
+
+ protected IBakedModel genDefaultOverrides() {
+ return ((OverrideCache) this.overrides).resolve(new GasStack(MekanismFluids.Hydrogen, 1000));
+ }
+
+ private class OverrideCache extends ItemOverrideList {
+
+ private final Cache cache = CacheBuilder.newBuilder()
+ .maximumSize(1000)
+ .expireAfterWrite(5, TimeUnit.MINUTES)
+ .build();
+
+ OverrideCache() {
+ super(Collections.emptyList());
+ }
+
+ @Override
+ @Nonnull
+ public IBakedModel handleItemState(@Nonnull IBakedModel originalModel, ItemStack stack,
+ @Nullable World world, @Nullable EntityLivingBase entity) {
+ if (!(stack.getItem() == ItemAndBlocks.DUMMY_GAS)) {
+ return originalModel;
+ }
+ GasStack gas = ItemAndBlocks.DUMMY_GAS.getGasStack(stack);
+ return gas != null ? resolve(gas) : originalModel;
+ }
+
+ OverrideModel resolve(GasStack gas) {
+ try {
+ return cache.get(gas.getGas(), () -> new OverrideModel(gas));
+ } catch (ExecutionException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ class OverrideModel implements IBakedModel {
+
+ private final TextureAtlasSprite texture;
+ private final List quads;
+
+ OverrideModel(GasStack gasStack) {
+ this.texture = gasStack.getGas().getSprite();
+ this.quads = ItemLayerModel.getQuadsForSprite(1, texture, vertexFormat, modelTransform);
+ }
+
+ @Override
+ @Nonnull
+ public List getQuads(@Nullable IBlockState state, @Nullable EnumFacing side, long rand) {
+ return quads;
+ }
+
+ @Override
+ public boolean isAmbientOcclusion() {
+ return false;
+ }
+
+ @Override
+ public boolean isGui3d() {
+ return false;
+ }
+
+ @Override
+ public boolean isBuiltInRenderer() {
+ return false;
+ }
+
+ @Override
+ @Nonnull
+ public TextureAtlasSprite getParticleTexture() {
+ return texture;
+ }
+
+ @Override
+ @Nonnull
+ @SuppressWarnings("deprecation")
+ public ItemCameraTransforms getItemCameraTransforms() {
+ return CAMERA_TRANSFORMS;
+ }
+
+ @Override
+ @Nonnull
+ public ItemOverrideList getOverrides() {
+ return OverrideCache.this;
+ }
+
+ }
+
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/client/render/GasStackSizeRenderer.java b/src/main/java/com/mekeng/github/client/render/GasStackSizeRenderer.java
new file mode 100644
index 0000000..d935043
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/render/GasStackSizeRenderer.java
@@ -0,0 +1,92 @@
+package com.mekeng.github.client.render;
+
+import appeng.core.AEConfig;
+import appeng.util.ISlimReadableNumberConverter;
+import appeng.util.IWideReadableNumberConverter;
+import appeng.util.ReadableNumberConverter;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.GlStateManager;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+
+public class GasStackSizeRenderer {
+
+ private static final String[] NUMBER_FORMATS = new String[]{"#.000", "#.00", "#.0", "#"};
+
+ private static final ISlimReadableNumberConverter SLIM_CONVERTER = ReadableNumberConverter.INSTANCE;
+ private static final IWideReadableNumberConverter WIDE_CONVERTER = ReadableNumberConverter.INSTANCE;
+
+ public void renderStackSize(FontRenderer fontRenderer, IAEGasStack aeStack, int xPos, int yPos) {
+ if (aeStack != null) {
+ final float scaleFactor = AEConfig.instance().useTerminalUseLargeFont() ? 0.85f : 0.5f;
+ final float inverseScaleFactor = 1.0f / scaleFactor;
+ final int offset = AEConfig.instance().useTerminalUseLargeFont() ? 0 : -1;
+
+ final boolean unicodeFlag = fontRenderer.getUnicodeFlag();
+ fontRenderer.setUnicodeFlag(false);
+
+ if (aeStack.getStackSize() > 0) {
+ final String stackSize = this.getToBeRenderedStackSize(aeStack.getStackSize());
+
+ GlStateManager.disableLighting();
+ GlStateManager.disableDepth();
+ GlStateManager.disableBlend();
+ GlStateManager.pushMatrix();
+ GlStateManager.scale(scaleFactor, scaleFactor, scaleFactor);
+ final int X = (int) (((float) xPos + offset + 16.0f - fontRenderer.getStringWidth(stackSize) * scaleFactor) * inverseScaleFactor);
+ final int Y = (int) (((float) yPos + offset + 16.0f - 7.0f * scaleFactor) * inverseScaleFactor);
+ fontRenderer.drawStringWithShadow(stackSize, X, Y, 16777215);
+ GlStateManager.popMatrix();
+ GlStateManager.enableLighting();
+ GlStateManager.enableDepth();
+ GlStateManager.enableBlend();
+ }
+
+ fontRenderer.setUnicodeFlag(unicodeFlag);
+ }
+ }
+
+ public String getToBeRenderedStackSize(final long originalSize) {
+ // Handle any value below 100 (large font) or 1000 (small font) Buckets with a custom formatter,
+ // otherwise pass it to the normal number converter
+ if (originalSize < 1000 * 100 && AEConfig.instance().useTerminalUseLargeFont()) {
+ return this.getSlimRenderedStacksize(originalSize);
+ } else if (originalSize < 1000 * 1000 && !AEConfig.instance().useTerminalUseLargeFont()) {
+ return this.getWideRenderedStacksize(originalSize);
+ }
+
+ if (AEConfig.instance().useTerminalUseLargeFont()) {
+ return SLIM_CONVERTER.toSlimReadableForm(originalSize / 1000);
+ } else {
+ return WIDE_CONVERTER.toWideReadableForm(originalSize / 1000);
+ }
+ }
+
+ public String getSlimRenderedStacksize(final long originalSize) {
+ final int log = 1 + (int) Math.floor(Math.log10(originalSize)) / 2;
+
+ return this.getRenderedFluidStackSize(originalSize, log);
+ }
+
+ public String getWideRenderedStacksize(final long originalSize) {
+ final int log = (int) Math.floor(Math.log10(originalSize)) / 2;
+
+ return this.getRenderedFluidStackSize(originalSize, log);
+ }
+
+ public String getRenderedFluidStackSize(final long originalSize, final int log) {
+ final int index = Math.max(0, Math.min(3, log));
+
+ final DecimalFormatSymbols symbols = new DecimalFormatSymbols();
+ symbols.setDecimalSeparator('.');
+ final DecimalFormat format = new DecimalFormat(NUMBER_FORMATS[index]);
+ format.setDecimalFormatSymbols(symbols);
+ format.setRoundingMode(RoundingMode.DOWN);
+
+ return format.format(originalSize / 1000d);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/slots/SlotGas.java b/src/main/java/com/mekeng/github/client/slots/SlotGas.java
new file mode 100644
index 0000000..8749138
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/slots/SlotGas.java
@@ -0,0 +1,95 @@
+package com.mekeng.github.client.slots;
+
+import appeng.client.gui.widgets.GuiCustomSlot;
+import appeng.container.slot.IJEITargetSlot;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.network.packet.CGasSlotSync;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.Gas;
+import mekanism.api.gas.GasStack;
+import mekanism.client.render.MekanismRenderer;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+import java.util.Collections;
+
+public class SlotGas extends GuiCustomSlot implements IJEITargetSlot {
+ private final IGasInventory gases;
+ private final int slot;
+
+ public SlotGas(final IGasInventory gases, final int slot, final int id, final int x, final int y) {
+ super(id, x, y);
+ this.gases = gases;
+ this.slot = slot;
+ }
+
+ @Override
+ public void drawContent(final Minecraft mc, final int mouseX, final int mouseY, final float partialTicks) {
+ final GasStack gs = this.getGasStack();
+ if (gs != null) {
+ GlStateManager.enableLighting();
+ GlStateManager.enableBlend();
+ final Gas gas = gs.getGas();
+ mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
+ final TextureAtlasSprite sprite = gas.getSprite();
+ // Set color for dynamic gases
+ // Convert int color to RGB
+ MekanismRenderer.color(gas);
+ this.drawTexturedModalRect(this.xPos(), this.yPos(), sprite, this.getWidth(), this.getHeight());
+ MekanismRenderer.resetColor();
+ }
+ }
+
+ @Override
+ public boolean canClick(final EntityPlayer player) {
+ final ItemStack mouseStack = player.inventory.getItemStack();
+ return mouseStack.isEmpty() || Utils.getGasHandler(mouseStack) != null;
+ }
+
+ @Override
+ public void slotClicked(final ItemStack clickStack, int mouseButton) {
+ if (clickStack.isEmpty() || mouseButton == 1) {
+ this.setGasStack(null);
+ } else if (mouseButton == 0) {
+ final GasStack gas = Utils.getGasFromItem(clickStack);
+ if (gas != null) {
+ this.setGasStack(gas);
+ }
+ }
+ }
+
+ @Override
+ public String getMessage() {
+ final GasStack gas = this.getGasStack();
+ if (gas != null) {
+ return gas.getGas().getLocalizedName();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return true;
+ }
+
+ public GasStack getGasStack() {
+ return this.gases.getGasStack(this.slot);
+ }
+
+ public void setGasStack(final GasStack stack) {
+ this.gases.setGas(this.slot, stack);
+ MekEng.proxy.netHandler.sendToServer(new CGasSlotSync(Collections.singletonMap(this.getId(), AEGasStack.of(stack))));
+ }
+
+ @Override
+ public boolean needAccept() {
+ return this.getGasStack() == null;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/slots/SlotGasME.java b/src/main/java/com/mekeng/github/client/slots/SlotGasME.java
new file mode 100644
index 0000000..c2b0b90
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/slots/SlotGasME.java
@@ -0,0 +1,74 @@
+package com.mekeng.github.client.slots;
+
+import com.mekeng.github.common.me.client.RepoSlot;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.items.SlotItemHandler;
+
+import javax.annotation.Nonnull;
+
+public class SlotGasME extends SlotItemHandler {
+ private final RepoSlot slot;
+
+ public SlotGasME(RepoSlot slot) {
+ super(null, 0, slot.getX(), slot.getY());
+ this.slot = slot;
+ }
+
+ public boolean shouldRenderAsGas() {
+ return true;
+ }
+
+ public IAEGasStack getAEGasStack() {
+ return this.slot.hasPower() ? this.slot.getAEStack() : null;
+ }
+
+ @Override
+ public boolean isItemValid(@Nonnull ItemStack stack) {
+ return false;
+ }
+
+ @Nonnull
+ @Override
+ public ItemStack getStack() {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public boolean getHasStack() {
+ if (this.slot.hasPower()) {
+ return this.getAEGasStack() != null;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void putStack(@Nonnull ItemStack stack) {
+ // NO-OP
+ }
+
+ @Override
+ public int getSlotStackLimit() {
+ return 0;
+ }
+
+ @Nonnull
+ @Override
+ public ItemStack decrStackSize(int amt) {
+ return ItemStack.EMPTY;
+ }
+
+ @Override
+ public boolean isHere(@Nonnull IInventory inv, int slotIn) {
+ return false;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer player) {
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/client/slots/SlotGasTank.java b/src/main/java/com/mekeng/github/client/slots/SlotGasTank.java
new file mode 100644
index 0000000..35e1858
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/slots/SlotGasTank.java
@@ -0,0 +1,120 @@
+package com.mekeng.github.client.slots;
+
+import appeng.client.gui.widgets.GuiCustomSlot;
+import appeng.client.gui.widgets.ITooltip;
+import appeng.core.sync.network.NetworkHandler;
+import appeng.core.sync.packets.PacketInventoryAction;
+import appeng.helpers.InventoryAction;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import mekanism.api.gas.GasStack;
+import mekanism.client.render.MekanismRenderer;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class SlotGasTank extends GuiCustomSlot implements ITooltip {
+ private final IGasInventory tank;
+ private final int slot;
+ private final int width;
+ private final int height;
+ private boolean darkened = false;
+
+ public SlotGasTank(IGasInventory tank, int slot, int id, int x, int y, int w, int h) {
+ super(id, x, y);
+ this.tank = tank;
+ this.slot = slot;
+ this.width = w;
+ this.height = h;
+ }
+
+ public SlotGasTank(IGasInventory tank, int slot, int id, int x, int y, int w, int h, boolean darkened) {
+ super(id, x, y);
+ this.tank = tank;
+ this.slot = slot;
+ this.width = w;
+ this.height = h;
+ this.darkened = darkened;
+ }
+
+ @Override
+ public void drawContent(Minecraft mc, int mouseX, int mouseY, float partialTicks) {
+ final GasStack fs = this.getGasStack();
+ if (fs != null) {
+ GlStateManager.enableLighting();
+ GlStateManager.enableBlend();
+
+ //drawRect( this.x, this.y, this.x + this.width, this.y + this.height, AEColor.GRAY.blackVariant | 0xFF000000 );
+
+ final GasStack gas = this.tank.getGasStack(this.slot);
+ if (gas != null && gas.amount > 0) {
+ mc.getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
+
+ float red = (gas.getGas().getTint() >> 16 & 255) / 255.0F;
+ float green = (gas.getGas().getTint() >> 8 & 255) / 255.0F;
+ float blue = (gas.getGas().getTint() & 255) / 255.0F;
+ if (darkened) {
+ red = red * 0.4F;
+ green = green * 0.4F;
+ blue = blue * 0.4F;
+ }
+ GlStateManager.color(red, green, blue);
+ TextureAtlasSprite sprite = gas.getGas().getSprite();
+ int scaledHeight = (int) (this.height * ((float) gas.amount / this.tank.getTanks()[this.slot].getMaxGas()));
+ scaledHeight = Math.min(this.height, scaledHeight);
+
+ int iconHeightRemainder = scaledHeight % 16;
+ if (iconHeightRemainder > 0) {
+ this.drawTexturedModalRect(this.xPos(), this.yPos() + this.getHeight() - iconHeightRemainder, sprite, 16, iconHeightRemainder);
+ }
+ for (int i = 0; i < scaledHeight / 16; i++) {
+ this.drawTexturedModalRect(this.xPos(), this.yPos() + this.getHeight() - iconHeightRemainder - (i + 1) * 16, sprite, 16, 16);
+ }
+ MekanismRenderer.resetColor();
+ }
+ }
+ }
+
+ @Override
+ public String getMessage() {
+ final GasStack gas = this.tank.getGasStack(this.slot);
+ if (gas != null && gas.amount > 0) {
+ String desc = gas.getGas().getLocalizedName();
+ return desc + "\n" + gas.amount + "/" + this.tank.getTanks()[this.slot].getMaxGas() + "mB";
+ }
+ return null;
+ }
+
+ @Override
+ public int getWidth() {
+ return this.width;
+ }
+
+ @Override
+ public int getHeight() {
+ return this.height;
+ }
+
+ @Override
+ public boolean isVisible() {
+ return true;
+ }
+
+ public GasStack getGasStack() {
+ return this.tank.getGasStack(this.slot);
+ }
+
+ @Override
+ public void slotClicked(ItemStack clickStack, final int mouseButton) {
+ if (getGasStack() != null) {
+ NetworkHandler.instance().sendToServer(new PacketInventoryAction(InventoryAction.FILL_ITEM, slot, id));
+ } else {
+ NetworkHandler.instance().sendToServer(new PacketInventoryAction(InventoryAction.EMPTY_ITEM, slot, id));
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/client/slots/SlotOptionalGas.java b/src/main/java/com/mekeng/github/client/slots/SlotOptionalGas.java
new file mode 100644
index 0000000..797e2ec
--- /dev/null
+++ b/src/main/java/com/mekeng/github/client/slots/SlotOptionalGas.java
@@ -0,0 +1,48 @@
+package com.mekeng.github.client.slots;
+
+import appeng.container.slot.IOptionalSlotHost;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import mekanism.api.gas.GasStack;
+import net.minecraft.client.renderer.GlStateManager;
+
+public class SlotOptionalGas extends SlotGas {
+ private final IOptionalSlotHost containerBus;
+ private final int groupNum;
+ private final int srcX;
+ private final int srcY;
+
+ public SlotOptionalGas(IGasInventory gases, final IOptionalSlotHost containerBus, int slot, int id, int groupNum, int x, int y, int xoffs, int yoffs) {
+ super(gases, slot, id, x + xoffs * 18, y + yoffs * 18);
+ this.containerBus = containerBus;
+ this.groupNum = groupNum;
+ this.srcX = x;
+ this.srcY = y;
+ }
+
+ @Override
+ public boolean isSlotEnabled() {
+ if (this.containerBus == null) {
+ return false;
+ }
+ return this.containerBus.isSlotEnabled(this.groupNum);
+ }
+
+ @Override
+ public GasStack getGasStack() {
+ if (!this.isSlotEnabled() && super.getGasStack() != null) {
+ this.setGasStack(null);
+ }
+ return super.getGasStack();
+ }
+
+ @Override
+ public void drawBackground(int guileft, int guitop) {
+ GlStateManager.enableBlend();
+ if (this.isSlotEnabled()) {
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+ } else {
+ GlStateManager.color(1.0F, 1.0F, 1.0F, 0.4F);
+ }
+ this.drawTexturedModalRect(guileft + this.xPos() - 1, guitop + this.yPos() - 1, this.srcX - 1, this.srcY - 1, this.getWidth() + 2, this.getHeight() + 2);
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/ItemAndBlocks.java b/src/main/java/com/mekeng/github/common/ItemAndBlocks.java
new file mode 100644
index 0000000..6d0cae1
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/ItemAndBlocks.java
@@ -0,0 +1,81 @@
+package com.mekeng.github.common;
+
+import appeng.api.AEApi;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.block.BlockGasInterface;
+import com.mekeng.github.common.item.ItemDummyGas;
+import com.mekeng.github.common.item.ItemGasCell;
+import com.mekeng.github.common.item.ItemMkEPart;
+import com.mekeng.github.common.item.ItemNormal;
+import com.mekeng.github.common.item.ItemPortableGasCell;
+import com.mekeng.github.common.item.ItemWirelessGasTerminal;
+import com.mekeng.github.common.part.PartGasExportBus;
+import com.mekeng.github.common.part.PartGasImportBus;
+import com.mekeng.github.common.part.PartGasInterface;
+import com.mekeng.github.common.part.PartGasInterfaceConfigurationTerminal;
+import com.mekeng.github.common.part.PartGasLevelEmitter;
+import com.mekeng.github.common.part.PartGasStorageBus;
+import com.mekeng.github.common.part.PartGasTerminal;
+import com.mekeng.github.common.part.p2p.PartP2PGases;
+import com.mekeng.github.common.tile.TileGasInterface;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.ItemStack;
+
+import javax.annotation.Nonnull;
+
+public class ItemAndBlocks {
+
+ public static final CreativeTabs TAB = new CreativeTabs(MekEng.MODID) {
+ @Nonnull
+ @Override
+ public ItemStack createIcon() {
+ return new ItemStack(GAS_INTERFACE);
+ }
+ };
+
+ public static ItemDummyGas DUMMY_GAS;
+ public static ItemNormal GAS_CELL_CORE_1k;
+ public static ItemNormal GAS_CELL_CORE_4k;
+ public static ItemNormal GAS_CELL_CORE_16k;
+ public static ItemNormal GAS_CELL_CORE_64k;
+ public static ItemGasCell GAS_CELL_1k;
+ public static ItemGasCell GAS_CELL_4k;
+ public static ItemGasCell GAS_CELL_16k;
+ public static ItemGasCell GAS_CELL_64k;
+ public static ItemPortableGasCell PORTABLE_GAS_CELL;
+ public static ItemMkEPart GAS_TERMINAL;
+ public static ItemMkEPart GAS_IMPORT_BUS;
+ public static ItemMkEPart GAS_EXPORT_BUS;
+ public static BlockGasInterface GAS_INTERFACE;
+ public static ItemMkEPart GAS_INTERFACE_PART;
+ public static ItemMkEPart GAS_STORAGE_BUS;
+ public static ItemMkEPart GAS_LEVEL_EMITTER;
+ public static ItemMkEPart GAS_INTERFACE_TERMINAL;
+ public static ItemWirelessGasTerminal WIRELESS_GAS_TERMINAL;
+ public static ItemMkEPart GAS_P2P;
+
+ public static void init(RegistryHandler regHandler) {
+ ItemStack casing = AEApi.instance().definitions().materials().emptyStorageCell().maybeStack(1).orElse(null);
+ regHandler.item("dummy_gas", DUMMY_GAS = new ItemDummyGas());
+ regHandler.item("gas_core_1k", GAS_CELL_CORE_1k = new ItemNormal());
+ regHandler.item("gas_core_4k", GAS_CELL_CORE_4k = new ItemNormal());
+ regHandler.item("gas_core_16k", GAS_CELL_CORE_16k = new ItemNormal());
+ regHandler.item("gas_core_64k", GAS_CELL_CORE_64k = new ItemNormal());
+ regHandler.item("gas_cell_1k", GAS_CELL_1k = new ItemGasCell(GAS_CELL_CORE_1k, casing, 1, 0.5, 8));
+ regHandler.item("gas_cell_4k", GAS_CELL_4k = new ItemGasCell(GAS_CELL_CORE_4k, casing, 4, 1.0, 32));
+ regHandler.item("gas_cell_16k", GAS_CELL_16k = new ItemGasCell(GAS_CELL_CORE_16k, casing, 16, 1.5, 128));
+ regHandler.item("gas_cell_64k", GAS_CELL_64k = new ItemGasCell(GAS_CELL_CORE_64k, casing, 64, 2.0, 512));
+ regHandler.item("portable_gas_cell", PORTABLE_GAS_CELL = new ItemPortableGasCell());
+ regHandler.item("gas_terminal", GAS_TERMINAL = new ItemMkEPart<>(PartGasTerminal::new));
+ regHandler.item("gas_import_bus", GAS_IMPORT_BUS = new ItemMkEPart<>(PartGasImportBus::new));
+ regHandler.item("gas_export_bus", GAS_EXPORT_BUS = new ItemMkEPart<>(PartGasExportBus::new));
+ regHandler.item("gas_interface_part", GAS_INTERFACE_PART = new ItemMkEPart<>(PartGasInterface::new));
+ regHandler.item("gas_storage_bus", GAS_STORAGE_BUS = new ItemMkEPart<>(PartGasStorageBus::new));
+ regHandler.item("gas_level_emitter", GAS_LEVEL_EMITTER = new ItemMkEPart<>(PartGasLevelEmitter::new));
+ regHandler.item("gas_interface_terminal", GAS_INTERFACE_TERMINAL = new ItemMkEPart<>(PartGasInterfaceConfigurationTerminal::new));
+ regHandler.item("wireless_gas_terminal", WIRELESS_GAS_TERMINAL = new ItemWirelessGasTerminal());
+ regHandler.item("gas_p2p", GAS_P2P = new ItemMkEPart<>(PartP2PGases::new));
+ regHandler.block("gas_interface", GAS_INTERFACE = new BlockGasInterface(), TileGasInterface.class);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/RegistryHandler.java b/src/main/java/com/mekeng/github/common/RegistryHandler.java
new file mode 100644
index 0000000..8b46f44
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/RegistryHandler.java
@@ -0,0 +1,82 @@
+package com.mekeng.github.common;
+
+import appeng.block.AEBaseItemBlock;
+import appeng.block.AEBaseTileBlock;
+import appeng.core.features.ActivityState;
+import appeng.core.features.BlockStackSrc;
+import appeng.tile.AEBaseTile;
+import com.mekeng.github.MekEng;
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.event.RegistryEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.registry.ForgeRegistries;
+import net.minecraftforge.fml.common.registry.GameRegistry;
+import org.apache.commons.lang3.tuple.Pair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class RegistryHandler {
+
+ protected final List> blocks = new ArrayList<>();
+ protected final List> items = new ArrayList<>();
+ protected final List>> tiles = new ArrayList<>();
+
+ public void block(String name, Block block) {
+ blocks.add(Pair.of(name, block));
+ }
+
+ public void item(String name, Item item) {
+ items.add(Pair.of(name, item));
+ }
+
+ public void block(String name, Block block, Class extends TileEntity> tile) {
+ blocks.add(Pair.of(name, block));
+ tiles.add(Pair.of(name, tile));
+ }
+
+ @SubscribeEvent
+ public void onRegisterBlocks(RegistryEvent.Register event) {
+ for (Pair entry : blocks) {
+ String key = entry.getLeft();
+ Block block = entry.getRight();
+ block.setRegistryName(key);
+ block.setTranslationKey(MekEng.MODID + ":" + key);
+ block.setCreativeTab(ItemAndBlocks.TAB);
+ event.getRegistry().register(block);
+ }
+ }
+
+ @SubscribeEvent
+ public void onRegisterItems(RegistryEvent.Register- event) {
+ // TODO some way to handle blocks with custom ItemBlock
+ for (Pair
entry : blocks) {
+ event.getRegistry().register(initItem(entry.getLeft(), new AEBaseItemBlock(entry.getRight())));
+ }
+ for (Pair entry : items) {
+ event.getRegistry().register(initItem(entry.getLeft(), entry.getRight()));
+ }
+ }
+
+ private static Item initItem(String key, Item item) {
+ item.setRegistryName(key);
+ item.setTranslationKey(MekEng.MODID + ":" + key);
+ item.setCreativeTab(ItemAndBlocks.TAB);
+ return item;
+ }
+
+ public void onInit() {
+ for (Pair> entry : tiles) {
+ GameRegistry.registerTileEntity(entry.getRight(), MekEng.id(entry.getLeft()));
+ }
+ for (Pair entry : blocks) {
+ Block block = ForgeRegistries.BLOCKS.getValue(MekEng.id(entry.getKey()));
+ if (block instanceof AEBaseTileBlock) {
+ AEBaseTile.registerTileItem(((AEBaseTileBlock)block).getTileEntityClass(), new BlockStackSrc(block, 0, ActivityState.Enabled));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/block/BlockGasInterface.java b/src/main/java/com/mekeng/github/common/block/BlockGasInterface.java
new file mode 100644
index 0000000..f86b7bf
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/block/BlockGasInterface.java
@@ -0,0 +1,41 @@
+package com.mekeng.github.common.block;
+
+import appeng.block.AEBaseTileBlock;
+import appeng.util.Platform;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.tile.TileGasInterface;
+import net.minecraft.block.material.Material;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import javax.annotation.Nullable;
+
+public class BlockGasInterface extends AEBaseTileBlock {
+
+ public BlockGasInterface() {
+ super(Material.IRON);
+ setTileEntity(TileGasInterface.class);
+ }
+
+ @Override
+ public boolean onActivated(final World w, final BlockPos pos, final EntityPlayer p, final EnumHand hand, final @Nullable ItemStack heldItem, final EnumFacing side, final float hitX, final float hitY, final float hitZ) {
+ if (p.isSneaking()) {
+ return false;
+ }
+
+ final TileEntity tg = this.getTileEntity(w, pos);
+ if (tg instanceof TileGasInterface) {
+ if (Platform.isServer()) {
+ GuiHandler.openTileGui(p, w, pos, MkEGuis.GAS_INTERFACE);
+ }
+ return true;
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasConfigurable.java b/src/main/java/com/mekeng/github/common/container/ContainerGasConfigurable.java
new file mode 100644
index 0000000..b0e7019
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasConfigurable.java
@@ -0,0 +1,110 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.Upgrades;
+import appeng.api.implementations.IUpgradeableHost;
+import appeng.container.implementations.ContainerUpgradeable;
+import appeng.util.Platform;
+import com.mekeng.github.common.container.sync.IGasSyncContainer;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.network.packet.sync.IActionHolder;
+import com.mekeng.github.network.packet.sync.Paras;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.GasSyncHelper;
+import mekanism.api.gas.GasStack;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.IContainerListener;
+import net.minecraft.item.ItemStack;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public abstract class ContainerGasConfigurable extends ContainerUpgradeable implements IGasSyncContainer, IActionHolder {
+ protected final GasSyncHelper sync = GasSyncHelper.create(this.getGasConfigInventory(), 0);
+ protected final Map> holder = createHolder();
+
+ public ContainerGasConfigurable(InventoryPlayer ip, T te) {
+ super(ip, te);
+ this.holder.put("jei_set", o -> {
+ AEGasStack gas = o.get(1);
+ if (gas != null) {
+ gas.setStackSize(1000);
+ this.getGasConfigInventory().setGas(o.get(0), gas.getGasStack());
+ this.receiveGasSlots(Collections.singletonMap(o.get(0), gas));
+ }
+ });
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected T getUpgradeable() {
+ return (T) super.getUpgradeable();
+ }
+
+ public abstract IGasInventory getGasConfigInventory();
+
+ @Override
+ protected ItemStack transferStackToContainer(ItemStack input) {
+ GasStack gs = Utils.getGasFromItem(input);
+ if (gs != null) {
+ final IGasInventory t = this.getGasConfigInventory();
+ final IAEGasStack stack = AEGasStack.of(gs);
+ for (int i = 0; i < t.size(); ++i) {
+ if (t.getGasStack(i) == null && this.isValidForConfig(i, stack)) {
+ t.setGas(i, stack == null ? null : stack.getGasStack());
+ break;
+ }
+ }
+ }
+ return input;
+ }
+
+ protected boolean isValidForConfig(int slot, @Nullable IAEGasStack gs) {
+ if (this.supportCapacity()) {
+ // assumes 4 slots per upgrade
+ final int upgrades = this.getUpgradeable().getInstalledUpgrades(Upgrades.CAPACITY);
+ if (slot > 0 && upgrades < 1) {
+ return false;
+ }
+ return slot <= 4 || upgrades >= 2;
+ }
+ return true;
+ }
+
+ @Override
+ protected void standardDetectAndSendChanges() {
+ if (Platform.isServer()) {
+ this.sync.sendDiff(this.listeners);
+ // clear out config items that are no longer valid (eg capacity upgrade removed)
+ final IGasInventory t = this.getGasConfigInventory();
+ for (int i = 0; i < t.size(); ++i) {
+ if (t.getGasStack(i) != null && !this.isValidForConfig(i, AEGasStack.of(t.getGasStack(i)))) {
+ t.setGas(i, null);
+ }
+ }
+ }
+ super.standardDetectAndSendChanges();
+ }
+
+ @Override
+ public void addListener(@Nonnull IContainerListener listener) {
+ super.addListener(listener);
+ this.sync.sendFull(Collections.singleton(listener));
+ }
+
+ @Override
+ public void receiveGasSlots(Map gases) {
+ this.sync.readPacket(gases);
+ }
+
+ @Nonnull
+ @Override
+ public Map> getActionMap() {
+ return this.holder;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasIO.java b/src/main/java/com/mekeng/github/common/container/ContainerGasIO.java
new file mode 100644
index 0000000..c54bc44
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasIO.java
@@ -0,0 +1,23 @@
+package com.mekeng.github.common.container;
+
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.part.PartSharedGasBus;
+import net.minecraft.entity.player.InventoryPlayer;
+
+public class ContainerGasIO extends ContainerGasConfigurable {
+
+ public ContainerGasIO(InventoryPlayer ip, PartSharedGasBus te) {
+ super(ip, te);
+ }
+
+ @Override
+ public IGasInventory getGasConfigInventory() {
+ return this.getUpgradeable().getConfig();
+ }
+
+ @Override
+ protected void setupConfig() {
+ this.setupUpgrades();
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasInterface.java b/src/main/java/com/mekeng/github/common/container/ContainerGasInterface.java
new file mode 100644
index 0000000..9912f59
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasInterface.java
@@ -0,0 +1,209 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.SecurityPermissions;
+import appeng.api.config.Upgrades;
+import appeng.api.util.IConfigManager;
+import appeng.container.guisync.GuiSync;
+import appeng.helpers.InventoryAction;
+import appeng.util.IConfigManagerHost;
+import appeng.util.Platform;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.duality.impl.DualityGasInterface;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.GasSyncHelper;
+import com.mekeng.github.util.helpers.ItemGasHandler;
+import mekanism.api.gas.GasStack;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.IContainerListener;
+import net.minecraft.item.ItemStack;
+
+import javax.annotation.Nonnull;
+import java.util.Collections;
+import java.util.Map;
+
+public class ContainerGasInterface extends ContainerGasConfigurable implements IConfigManagerHost {
+ private final DualityGasInterface myDuality;
+ private final GasSyncHelper tankSync;
+ private IConfigManagerHost gui;
+ // Holds the gas the client wishes to extract, or null for insert
+ private IAEGasStack clientRequestedTargetGas = null;
+
+ @GuiSync(7)
+ public int capacityUpgrades = 0;
+
+ public ContainerGasInterface(final InventoryPlayer ip, final IGasInterfaceHost te) {
+ super(ip, te);
+
+ this.myDuality = te.getDualityGasInterface();
+ this.tankSync = GasSyncHelper.create(this.myDuality.getTanks(), DualityGasInterface.NUMBER_OF_TANKS);
+ this.holder.put("set_target", o -> this.setTargetGasStack(o.get(0)));
+ }
+
+ @Override
+ protected int getHeight() {
+ return 231;
+ }
+
+ @Override
+ public IGasInventory getGasConfigInventory() {
+ return this.getUpgradeable().getDualityGasInterface().getConfig();
+ }
+
+ @Override
+ public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
+ if (this.getGui() != null) {
+ this.getGui().updateSetting(manager, settingName, newValue);
+ }
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ this.verifyPermissions(SecurityPermissions.BUILD, false);
+
+ if (Platform.isServer()) {
+ this.tankSync.sendDiff(this.listeners);
+ if (capacityUpgrades != getUpgradeable().getInstalledUpgrades(Upgrades.CAPACITY)) {
+ capacityUpgrades = getUpgradeable().getInstalledUpgrades(Upgrades.CAPACITY);
+ }
+ }
+
+ super.detectAndSendChanges();
+ }
+
+ @Override
+ public void onUpdate(final String field, final Object oldValue, final Object newValue) {
+ super.onUpdate(field, oldValue, newValue);
+ if (Platform.isClient() && field.equals("capacityUpgrades")) {
+ this.capacityUpgrades = (int) newValue;
+ this.myDuality.getTanks().setCap((int) (Math.pow(4, this.capacityUpgrades + 1) * 1000));
+ }
+ }
+
+ @Override
+ protected void setupConfig() {
+ this.setupUpgrades();
+ }
+
+ @Override
+ protected void loadSettingsFromHost(final IConfigManager cm) {
+ }
+
+ @Override
+ public void addListener(@Nonnull IContainerListener listener) {
+ super.addListener(listener);
+ this.tankSync.sendFull(Collections.singleton(listener));
+ }
+
+ @Override
+ public void receiveGasSlots(Map gases) {
+ super.receiveGasSlots(gases);
+ this.tankSync.readPacket(gases);
+ }
+
+ private IConfigManagerHost getGui() {
+ return this.gui;
+ }
+
+ public void setGui(@Nonnull final IConfigManagerHost gui) {
+ this.gui = gui;
+ }
+
+ public void doAction(EntityPlayerMP player, InventoryAction action, int slot, long id) {
+ if (action != InventoryAction.FILL_ITEM && action != InventoryAction.EMPTY_ITEM) {
+ super.doAction(player, action, slot, id);
+ return;
+ }
+
+ final ItemStack held = player.inventory.getItemStack();
+ ItemStack heldCopy = held.copy();
+ heldCopy.setCount(1);
+ ItemGasHandler gh = Utils.getItemGasHandler(heldCopy);
+ if (gh == null) {
+ // only gas handlers items
+ return;
+ }
+
+ if (action == InventoryAction.FILL_ITEM && this.clientRequestedTargetGas != null) {
+ final IAEGasStack stack = this.clientRequestedTargetGas.copy();
+
+ // Check how much we can store in the item
+ stack.setStackSize(Integer.MAX_VALUE);
+ int amountAllowed = gh.addGas(stack.getGasStack(), false);
+ int heldAmount = held.getCount();
+ for (int i = 0; i < heldAmount; i++) {
+ ItemStack copiedGasContainer = held.copy();
+ copiedGasContainer.setCount(1);
+ gh = Utils.getItemGasHandler(copiedGasContainer);
+
+ GasStack extractableGas = this.myDuality.getTankHandler().drawGas(null, stack.setStackSize(amountAllowed).getGasStack(), false);
+ if (extractableGas == null || extractableGas.amount == 0) {
+ break;
+ }
+
+ int fillableAmount = gh.addGas(extractableGas, false);
+ if (fillableAmount > 0) {
+ GasStack extractedGas = this.myDuality.getTankHandler().drawGas(null, extractableGas, true);
+ gh.addGas(extractedGas, true);
+ }
+
+ if (held.getCount() == 1) {
+ player.inventory.setItemStack(gh.getContainer());
+ } else {
+ player.inventory.getItemStack().shrink(1);
+ if (!player.inventory.addItemStackToInventory(gh.getContainer())) {
+ player.dropItem(gh.getContainer(), false);
+ }
+ }
+ }
+ } else if (action == InventoryAction.EMPTY_ITEM) {
+ int heldAmount = held.getCount();
+ for (int i = 0; i < heldAmount; i++) {
+ ItemStack copiedGasContainer = held.copy();
+ copiedGasContainer.setCount(1);
+ gh = Utils.getItemGasHandler(copiedGasContainer);
+
+ GasStack drainable = gh.removeGas(this.myDuality.getTanks().getTanks()[slot].getMaxGas(), false);
+ if (drainable != null) {
+ gh.removeGas(drainable, true);
+ this.myDuality.getTankHandler().receiveGas(null, drainable, true);
+ }
+
+ if (held.getCount() == 1) {
+ player.inventory.setItemStack(gh.getContainer());
+ } else {
+ player.inventory.getItemStack().shrink(1);
+ if (!player.inventory.addItemStackToInventory(gh.getContainer())) {
+ player.dropItem(gh.getContainer(), false);
+ }
+ }
+ }
+ }
+ this.updateHeld(player);
+ }
+
+ public void setTargetGasStack(final IAEGasStack stack) {
+ if (Platform.isClient()) {
+ if (stack == null && this.clientRequestedTargetGas == null) {
+ return;
+ }
+ if (stack != null && this.clientRequestedTargetGas != null && stack.getGasStack().isGasEqual(this.clientRequestedTargetGas.getGasStack())) {
+ return;
+ }
+ }
+ this.clientRequestedTargetGas = stack == null ? null : stack.copy();
+ }
+
+ @Override
+ protected boolean supportCapacity() {
+ return false;
+ }
+
+ @Override
+ public int availableUpgrades() {
+ return 2;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasInterfaceConfigurationTerminal.java b/src/main/java/com/mekeng/github/common/container/ContainerGasInterfaceConfigurationTerminal.java
new file mode 100644
index 0000000..3f34721
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasInterfaceConfigurationTerminal.java
@@ -0,0 +1,251 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.Settings;
+import appeng.api.config.YesNo;
+import appeng.api.networking.IGrid;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.security.IActionHost;
+import appeng.container.AEBaseContainer;
+import appeng.helpers.InventoryAction;
+import appeng.util.Platform;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.duality.impl.DualityGasInterface;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.impl.GasInventory;
+import com.mekeng.github.common.part.PartGasInterface;
+import com.mekeng.github.common.part.PartGasInterfaceConfigurationTerminal;
+import com.mekeng.github.common.tile.TileGasInterface;
+import com.mekeng.github.network.packet.SGenericPacket;
+import com.mekeng.github.network.packet.sync.IActionHolder;
+import com.mekeng.github.network.packet.sync.Paras;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.ItemGasHandler;
+import mekanism.api.gas.GasStack;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTUtil;
+import net.minecraft.util.math.BlockPos;
+
+import javax.annotation.Nonnull;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ContainerGasInterfaceConfigurationTerminal extends AEBaseContainer implements IActionHolder {
+
+ private static long autoBase = Long.MIN_VALUE;
+ private final Map diList = new HashMap<>();
+ private final Map byId = new HashMap<>();
+ private IGrid grid;
+ private NBTTagCompound data = new NBTTagCompound();
+ private final Map> holder = createHolder();
+
+ public ContainerGasInterfaceConfigurationTerminal(final InventoryPlayer ip, final PartGasInterfaceConfigurationTerminal anchor) {
+ super(ip, anchor);
+ if (Platform.isServer()) {
+ this.grid = anchor.getActionableNode().getGrid();
+ }
+ this.bindPlayerInventory(ip, 14, 235 - /* height of player inventory */82);
+ this.holder.put("jei_set", o -> this.setFromJei(o.get(0), o.get(1), o.get(2)));
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ if (Platform.isClient()) {
+ return;
+ }
+ super.detectAndSendChanges();
+ if (this.grid == null) {
+ return;
+ }
+ int total = 0;
+ boolean missing = false;
+ final IActionHost host = this.getActionHost();
+ if (host != null) {
+ final IGridNode agn = host.getActionableNode();
+ if (agn != null && agn.isActive()) {
+ for (final IGridNode gn : this.grid.getMachines(TileGasInterface.class)) {
+ if (gn.isActive()) {
+ final IGasInterfaceHost ih = (IGasInterfaceHost) gn.getMachine();
+ if (ih.getDualityGasInterface().getConfigManager().getSetting(Settings.INTERFACE_TERMINAL) == YesNo.NO) {
+ continue;
+ }
+
+ final GasConfigTracker t = this.diList.get(ih);
+
+ if (t == null) {
+ missing = true;
+ } else {
+ final DualityGasInterface dual = ih.getDualityGasInterface();
+ if (!t.unlocalizedName.equals(dual.getTermName())) {
+ missing = true;
+ }
+ }
+
+ total++;
+ }
+ }
+
+ for (final IGridNode gn : this.grid.getMachines(PartGasInterface.class)) {
+ if (gn.isActive()) {
+ final IGasInterfaceHost ih = (IGasInterfaceHost) gn.getMachine();
+ if (ih.getDualityGasInterface().getConfigManager().getSetting(Settings.INTERFACE_TERMINAL) == YesNo.NO) {
+ continue;
+ }
+
+ final GasConfigTracker t = this.diList.get(ih);
+
+ if (t == null) {
+ missing = true;
+ } else {
+ final DualityGasInterface dual = ih.getDualityGasInterface();
+ if (!t.unlocalizedName.equals(dual.getTermName())) {
+ missing = true;
+ }
+ }
+
+ total++;
+ }
+ }
+ }
+ }
+
+ if (total != this.diList.size() || missing) {
+ this.regenList(this.data);
+ } else {
+ for (final Map.Entry en : this.diList.entrySet()) {
+ final GasConfigTracker inv = en.getValue();
+ for (int x = 0; x < inv.server.size(); x++) {
+ if ((inv.server.getGasStack(x) == null && inv.client.getGasStack(x) != null) || (inv.server.getGasStack(x) != null && !inv.server.getGasStack(x).equals(inv.client.getGasStack(x)))) {
+ this.addGases(this.data, inv, x, 1);
+ }
+ }
+ }
+ }
+
+ if (!this.data.isEmpty()) {
+ MekEng.proxy.netHandler.sendTo(new SGenericPacket("update", this.data), (EntityPlayerMP) this.getPlayerInv().player);
+ this.data = new NBTTagCompound();
+ }
+ }
+
+ @Override
+ public void doAction(final EntityPlayerMP player, final InventoryAction action, final int slot, final long id) {
+ final GasConfigTracker inv = this.byId.get(id);
+ if (inv != null) {
+ ItemStack itemInHand = player.inventory.getItemStack();
+ ItemGasHandler c = Utils.getItemGasHandler(itemInHand);
+ if (c != null) {
+ GasStack gs = c.removeGas(Integer.MAX_VALUE, false);
+ if (gs != null) {
+ inv.server.setGas(slot, gs);
+ return;
+ }
+ return;
+ }
+ inv.server.setGas(slot, null);
+ this.updateHeld(player);
+ }
+ }
+
+ private void setFromJei(int slot, long id, IAEGasStack gas) {
+ final GasConfigTracker inv = this.byId.get(id);
+ if (inv != null && gas != null) {
+ inv.server.setGas(slot, gas.getGasStack());
+ }
+ }
+
+ private void regenList(final NBTTagCompound data) {
+ this.byId.clear();
+ this.diList.clear();
+
+ final IActionHost host = this.getActionHost();
+ if (host != null) {
+ final IGridNode agn = host.getActionableNode();
+ if (agn != null && agn.isActive()) {
+ for (final IGridNode gn : this.grid.getMachines(TileGasInterface.class)) {
+ final IGasInterfaceHost ih = (IGasInterfaceHost) gn.getMachine();
+ final DualityGasInterface dual = ih.getDualityGasInterface();
+ if (gn.isActive() && dual.getConfigManager().getSetting(Settings.INTERFACE_TERMINAL) == YesNo.YES) {
+ this.diList.put(ih, new GasConfigTracker(dual, dual.getConfig(), dual.getTermName()));
+ }
+ }
+
+ for (final IGridNode gn : this.grid.getMachines(PartGasInterface.class)) {
+ final IGasInterfaceHost ih = (IGasInterfaceHost) gn.getMachine();
+ final DualityGasInterface dual = ih.getDualityGasInterface();
+ if (gn.isActive() && dual.getConfigManager().getSetting(Settings.INTERFACE_TERMINAL) == YesNo.YES) {
+ this.diList.put(ih, new GasConfigTracker(dual, dual.getConfig(), dual.getTermName()));
+ }
+ }
+ }
+ }
+
+ data.setBoolean("clear", true);
+
+ for (final Map.Entry en : this.diList.entrySet()) {
+ final GasConfigTracker inv = en.getValue();
+ this.byId.put(inv.which, inv);
+ this.addGases(data, inv, 0, inv.server.size());
+ }
+ }
+
+ private void addGases(final NBTTagCompound data, final GasConfigTracker inv, final int offset, final int length) {
+ final String name = '=' + Long.toString(inv.which, Character.MAX_RADIX);
+ final NBTTagCompound tag = data.getCompoundTag(name);
+
+ if (tag.isEmpty()) {
+ tag.setLong("sortBy", inv.sortBy);
+ tag.setString("un", inv.unlocalizedName);
+ tag.setTag("pos", NBTUtil.createPosTag(inv.pos));
+ tag.setInteger("dim", inv.dim);
+ }
+
+ for (int x = 0; x < length; x++) {
+ NBTTagCompound gasNBT = new NBTTagCompound();
+ GasStack gasStack = inv.server.getGasStack(x + offset);
+
+ // "update" client side.
+ inv.client.setGas(x + offset, gasStack == null ? null : gasStack.copy());
+
+ if (gasStack != null) {
+ gasNBT = gasStack.write(gasNBT);
+ }
+
+ tag.setTag(Integer.toString(x + offset), gasNBT);
+ }
+
+ data.setTag(name, tag);
+ }
+
+ @Nonnull
+ @Override
+ public Map> getActionMap() {
+ return this.holder;
+ }
+
+ public static class GasConfigTracker {
+
+ private final long sortBy;
+ private final long which = autoBase++;
+ private final String unlocalizedName;
+ private final IGasInventory client;
+ private final IGasInventory server;
+ private final BlockPos pos;
+ private final int dim;
+
+ public GasConfigTracker(final DualityGasInterface dual, final IGasInventory configSlots, final String unlocalizedName) {
+ this.server = configSlots;
+ this.client = new GasInventory(this.server.size());
+ this.unlocalizedName = unlocalizedName;
+ this.sortBy = dual.getSortValue();
+ this.pos = dual.getLocation().getPos();
+ this.dim = dual.getLocation().getWorld().provider.getDimension();
+ }
+
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasLevelEmitter.java b/src/main/java/com/mekeng/github/common/container/ContainerGasLevelEmitter.java
new file mode 100644
index 0000000..600df9d
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasLevelEmitter.java
@@ -0,0 +1,77 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.RedstoneMode;
+import appeng.api.config.SecurityPermissions;
+import appeng.api.config.Settings;
+import appeng.container.guisync.GuiSync;
+import appeng.util.Platform;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.part.PartGasLevelEmitter;
+import net.minecraft.client.gui.GuiTextField;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+public class ContainerGasLevelEmitter extends ContainerGasConfigurable {
+
+ @SideOnly(Side.CLIENT)
+ private GuiTextField textField;
+ @GuiSync(3)
+ public long EmitterValue = -1;
+
+ public ContainerGasLevelEmitter(final InventoryPlayer ip, final PartGasLevelEmitter te) {
+ super(ip, te);
+ this.holder.put("set_level", o -> this.setLevel(o.get(0)));
+ }
+
+ @SideOnly(Side.CLIENT)
+ public void setTextField(final GuiTextField level) {
+ this.textField = level;
+ this.textField.setText(String.valueOf(this.EmitterValue));
+ }
+
+ public void setLevel(final long l) {
+ this.getUpgradeable().setReportingValue(l);
+ this.EmitterValue = l;
+ }
+
+ @Override
+ protected void setupConfig() {
+ // NO-OP
+ }
+
+ @Override
+ protected boolean supportCapacity() {
+ return false;
+ }
+
+ @Override
+ public int availableUpgrades() {
+ return 0;
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ this.verifyPermissions(SecurityPermissions.BUILD, false);
+ if (Platform.isServer()) {
+ this.EmitterValue = this.getUpgradeable().getReportingValue();
+ this.setRedStoneMode((RedstoneMode) this.getUpgradeable().getConfigManager().getSetting(Settings.REDSTONE_EMITTER));
+ }
+ this.standardDetectAndSendChanges();
+ }
+
+ @Override
+ public void onUpdate(final String field, final Object oldValue, final Object newValue) {
+ if (field.equals("EmitterValue")) {
+ System.out.print(oldValue + " " + newValue + " " + this.EmitterValue + "\n");
+ if (this.textField != null) {
+ this.textField.setText(String.valueOf(this.EmitterValue));
+ }
+ }
+ }
+
+ @Override
+ public IGasInventory getGasConfigInventory() {
+ return this.getUpgradeable().getConfig();
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasStorageBus.java b/src/main/java/com/mekeng/github/common/container/ContainerGasStorageBus.java
new file mode 100644
index 0000000..39a89ea
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasStorageBus.java
@@ -0,0 +1,152 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.AEApi;
+import appeng.api.config.AccessRestriction;
+import appeng.api.config.FuzzyMode;
+import appeng.api.config.SecurityPermissions;
+import appeng.api.config.Settings;
+import appeng.api.config.StorageFilter;
+import appeng.api.config.Upgrades;
+import appeng.api.storage.IMEInventory;
+import appeng.api.storage.data.IItemList;
+import appeng.container.guisync.GuiSync;
+import appeng.container.slot.SlotRestrictedInput;
+import appeng.util.Platform;
+import appeng.util.iterators.NullIterator;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.common.part.PartGasStorageBus;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraftforge.items.IItemHandler;
+
+import java.util.Iterator;
+
+public class ContainerGasStorageBus extends ContainerGasConfigurable {
+
+ @GuiSync(3)
+ public AccessRestriction rwMode = AccessRestriction.READ_WRITE;
+
+ @GuiSync(4)
+ public StorageFilter storageFilter = StorageFilter.EXTRACTABLE_ONLY;
+
+ public ContainerGasStorageBus(InventoryPlayer ip, PartGasStorageBus te) {
+ super(ip, te);
+ this.holder.put("clear", o -> this.clear());
+ this.holder.put("partition", o -> this.partition());
+ }
+
+ @Override
+ protected int getHeight() {
+ return 251;
+ }
+
+ @Override
+ protected void setupConfig() {
+ final IItemHandler upgrades = this.getUpgradeable().getInventoryByName("upgrades");
+ this.addSlotToContainer((new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, 0, 187, 8, this.getInventoryPlayer()))
+ .setNotDraggable());
+ this.addSlotToContainer(
+ (new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, 1, 187, 8 + 18, this.getInventoryPlayer()))
+ .setNotDraggable());
+ this.addSlotToContainer(
+ (new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, 2, 187, 8 + 18 * 2, this.getInventoryPlayer()))
+ .setNotDraggable());
+ this.addSlotToContainer(
+ (new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, 3, 187, 8 + 18 * 3, this.getInventoryPlayer()))
+ .setNotDraggable());
+ this.addSlotToContainer(
+ (new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, 4, 187, 8 + 18 * 4, this.getInventoryPlayer()))
+ .setNotDraggable());
+ }
+
+ @Override
+ protected boolean isValidForConfig(int slot, IAEGasStack fs) {
+ if (this.supportCapacity()) {
+ final int upgrades = this.getUpgradeable().getInstalledUpgrades(Upgrades.CAPACITY);
+
+ final int y = slot / 9;
+
+ return y < upgrades + 2;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int availableUpgrades() {
+ return 5;
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ this.verifyPermissions(SecurityPermissions.BUILD, false);
+
+ if (Platform.isServer()) {
+ this.setFuzzyMode((FuzzyMode) this.getUpgradeable().getConfigManager().getSetting(Settings.FUZZY_MODE));
+ this.setReadWriteMode((AccessRestriction) this.getUpgradeable().getConfigManager().getSetting(Settings.ACCESS));
+ this.setStorageFilter((StorageFilter) this.getUpgradeable().getConfigManager().getSetting(Settings.STORAGE_FILTER));
+ }
+
+ this.standardDetectAndSendChanges();
+ }
+
+ @Override
+ public boolean isSlotEnabled(final int idx) {
+ final int upgrades = this.getUpgradeable().getInstalledUpgrades(Upgrades.CAPACITY);
+
+ return upgrades > idx;
+ }
+
+ public void clear() {
+ IGasInventory h = this.getUpgradeable().getConfig();
+ for (int i = 0; i < h.size(); ++i) {
+ h.setGas(i, null);
+ }
+ this.detectAndSendChanges();
+ }
+
+ public void partition() {
+ IGasInventory h = this.getUpgradeable().getConfig();
+
+ final IMEInventory cellInv = this.getUpgradeable().getInternalHandler();
+
+ Iterator i = new NullIterator<>();
+ if (cellInv != null) {
+ final IItemList list = cellInv.getAvailableItems(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList());
+ i = list.iterator();
+ }
+
+ for (int x = 0; x < h.size(); x++) {
+ if (i.hasNext() && this.isSlotEnabled((x / 9) - 2)) {
+ IAEGasStack gas = i.next();
+ h.setGas(x, gas == null ? null : gas.getGasStack());
+ } else {
+ h.setGas(x, null);
+ }
+ }
+ this.detectAndSendChanges();
+ }
+
+ public AccessRestriction getReadWriteMode() {
+ return this.rwMode;
+ }
+
+ private void setReadWriteMode(final AccessRestriction rwMode) {
+ this.rwMode = rwMode;
+ }
+
+ public StorageFilter getStorageFilter() {
+ return this.storageFilter;
+ }
+
+ private void setStorageFilter(final StorageFilter storageFilter) {
+ this.storageFilter = storageFilter;
+ }
+
+ @Override
+ public IGasInventory getGasConfigInventory() {
+ return this.getUpgradeable().getConfig();
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerGasTerminal.java b/src/main/java/com/mekeng/github/common/container/ContainerGasTerminal.java
new file mode 100644
index 0000000..1d2b430
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerGasTerminal.java
@@ -0,0 +1,490 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.AEApi;
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.api.config.Settings;
+import appeng.api.config.SortDir;
+import appeng.api.config.SortOrder;
+import appeng.api.config.ViewItems;
+import appeng.api.implementations.guiobjects.IGuiItemObject;
+import appeng.api.networking.IGrid;
+import appeng.api.networking.IGridHost;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.energy.IEnergyGrid;
+import appeng.api.networking.energy.IEnergySource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.storage.IBaseMonitor;
+import appeng.api.parts.IPart;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.ITerminalHost;
+import appeng.api.storage.data.IItemList;
+import appeng.api.util.AEPartLocation;
+import appeng.api.util.IConfigManager;
+import appeng.api.util.IConfigurableObject;
+import appeng.container.AEBaseContainer;
+import appeng.container.guisync.GuiSync;
+import appeng.container.slot.AppEngSlot;
+import appeng.container.slot.SlotPlayerHotBar;
+import appeng.container.slot.SlotPlayerInv;
+import appeng.core.AELog;
+import appeng.core.sync.network.NetworkHandler;
+import appeng.core.sync.packets.PacketValueConfig;
+import appeng.helpers.InventoryAction;
+import appeng.helpers.WirelessTerminalGuiObject;
+import appeng.me.helpers.ChannelPowerSrc;
+import appeng.util.ConfigManager;
+import appeng.util.IConfigManagerHost;
+import appeng.util.Platform;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.common.me.storage.IPortableGasCell;
+import com.mekeng.github.network.packet.SMEGasInventoryUpdate;
+import com.mekeng.github.network.packet.sync.IActionHolder;
+import com.mekeng.github.network.packet.sync.Paras;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.ItemGasHandler;
+import mekanism.api.gas.GasStack;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.IContainerListener;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class ContainerGasTerminal extends AEBaseContainer implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver, IActionHolder {
+ private final IConfigManager clientCM;
+ private final IMEMonitor monitor;
+ private final IItemList gases = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+ @GuiSync(99)
+ public boolean hasPower = false;
+ private final ITerminalHost terminal;
+ private IConfigManager serverCM;
+ private IConfigManagerHost gui;
+ private IGridNode networkNode;
+ // Holds the gas the client wishes to extract, or null for insert
+ private IAEGasStack clientRequestedTargetGas = null;
+ private final Map> holder = createHolder();
+
+ public ContainerGasTerminal(final InventoryPlayer ip, final ITerminalHost monitorable) {
+ this(ip, monitorable, true);
+ }
+
+ public ContainerGasTerminal(final InventoryPlayer ip, final ITerminalHost monitorable, final boolean bindInventory) {
+ this(ip, monitorable, null, bindInventory);
+ }
+
+ public ContainerGasTerminal(InventoryPlayer ip, ITerminalHost terminal, IGuiItemObject guiObj, boolean bindInventory) {
+ super(ip, terminal instanceof TileEntity ? (TileEntity) terminal : null, terminal instanceof IPart ? (IPart) terminal : null, guiObj);
+ this.terminal = terminal;
+ this.clientCM = new ConfigManager(this);
+
+ this.clientCM.registerSetting(Settings.SORT_BY, SortOrder.NAME);
+ this.clientCM.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING);
+ this.clientCM.registerSetting(Settings.VIEW_MODE, ViewItems.ALL);
+ this.holder.put("set_target", o -> this.setTargetGasStack(o.get(0)));
+ if (Platform.isServer()) {
+ this.serverCM = terminal.getConfigManager();
+ this.monitor = terminal.getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class));
+ if (this.monitor != null) {
+ this.monitor.addListener(this, null);
+ if (terminal instanceof IPortableGasCell) {
+ this.setPowerSource((IEnergySource) terminal);
+ if (terminal instanceof WirelessTerminalGuiObject) {
+ this.networkNode = ((WirelessTerminalGuiObject) terminal).getActionableNode();
+ }
+ } else if (terminal instanceof IEnergySource) {
+ this.setPowerSource((IEnergySource) terminal);
+ } else if (terminal instanceof IGridHost || terminal instanceof IActionHost) {
+ final IGridNode node;
+ if (terminal instanceof IGridHost) {
+ node = ((IGridHost) terminal).getGridNode(AEPartLocation.INTERNAL);
+ } else {
+ node = ((IActionHost) terminal).getActionableNode();
+ }
+ if (node != null) {
+ this.networkNode = node;
+ final IGrid g = node.getGrid();
+ this.setPowerSource(new ChannelPowerSrc(this.networkNode, g.getCache(IEnergyGrid.class)));
+ }
+ }
+ }
+ } else {
+ this.monitor = null;
+ }
+ if (bindInventory) {
+ this.bindPlayerInventory(ip, 0, 222 - 82);
+ }
+ }
+
+ @Override
+ public boolean isValid(Object verificationToken) {
+ return true;
+ }
+
+ @Override
+ public void postChange(IBaseMonitor monitor, Iterable change, IActionSource actionSource) {
+ for (final IAEGasStack is : change) {
+ this.gases.add(is);
+ }
+ }
+
+ @Override
+ public void onListUpdate() {
+ for (final IContainerListener c : this.listeners) {
+ this.queueInventory(c);
+ }
+ }
+
+ @Override
+ public void addListener(@Nonnull IContainerListener listener) {
+ super.addListener(listener);
+ this.queueInventory(listener);
+ }
+
+ @Override
+ public void onContainerClosed(@Nonnull final EntityPlayer player) {
+ super.onContainerClosed(player);
+ if (this.monitor != null) {
+ this.monitor.removeListener(this);
+ }
+ }
+
+ private void queueInventory(final IContainerListener c) {
+ if (Platform.isServer() && c instanceof EntityPlayer && this.monitor != null) {
+ SMEGasInventoryUpdate packet = new SMEGasInventoryUpdate();
+ final IItemList monitorCache = this.monitor.getStorageList();
+ for (final IAEGasStack send : monitorCache) {
+ if (packet.needFlush()) {
+ MekEng.proxy.netHandler.sendTo(packet, (EntityPlayerMP) c);
+ packet.clear();
+ }
+ packet.addGas(send);
+ }
+ if (!packet.isEmpty()) {
+ MekEng.proxy.netHandler.sendTo(packet, (EntityPlayerMP) c);
+ }
+ }
+ }
+
+ @Override
+ public IConfigManager getConfigManager() {
+ if (Platform.isServer()) {
+ return this.serverCM;
+ }
+ return this.clientCM;
+ }
+
+ public void setTargetGasStack(final IAEGasStack stack) {
+ if (Platform.isClient()) {
+ if (stack == null && this.clientRequestedTargetGas == null) {
+ return;
+ }
+ if (stack != null && this.clientRequestedTargetGas != null
+ && stack.getGas() == this.clientRequestedTargetGas.getGas()) {
+ return;
+ }
+ }
+ this.clientRequestedTargetGas = stack == null ? null : stack.copy();
+ }
+
+ @Override
+ public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
+ if (this.getGui() != null) {
+ this.getGui().updateSetting(manager, settingName, newValue);
+ }
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ if (Platform.isServer()) {
+ if (this.monitor != this.terminal.getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class))) {
+ this.setValidContainer(false);
+ }
+ for (final Settings set : this.serverCM.getSettings()) {
+ final Enum> sideLocal = this.serverCM.getSetting(set);
+ final Enum> sideRemote = this.clientCM.getSetting(set);
+
+ if (sideLocal != sideRemote) {
+ this.clientCM.putSetting(set, sideLocal);
+ for (final IContainerListener crafter : this.listeners) {
+ if (crafter instanceof EntityPlayerMP) {
+ try {
+ NetworkHandler.instance().sendTo(new PacketValueConfig(set.name(), sideLocal.name()), (EntityPlayerMP) crafter);
+ } catch (final IOException e) {
+ AELog.debug(e);
+ }
+ }
+ }
+ }
+ }
+
+ if (!this.gases.isEmpty()) {
+ final IItemList monitorCache = this.monitor.getStorageList();
+ final SMEGasInventoryUpdate packet = new SMEGasInventoryUpdate();
+ boolean noUpdate = true;
+ for (final IAEGasStack is : this.gases) {
+ if (packet.needFlush()) {
+ for (final Object c : this.listeners) {
+ if (c instanceof EntityPlayer) {
+ MekEng.proxy.netHandler.sendTo(packet, (EntityPlayerMP) c);
+ }
+ }
+ noUpdate = false;
+ packet.clear();
+ }
+ final IAEGasStack send = monitorCache.findPrecise(is);
+ if (send == null) {
+ is.setStackSize(0);
+ packet.addGas(is);
+ } else {
+ packet.addGas(send);
+ }
+ }
+ if (!packet.isEmpty()) {
+ for (final Object c : this.listeners) {
+ if (c instanceof EntityPlayer) {
+ MekEng.proxy.netHandler.sendTo(packet, (EntityPlayerMP) c);
+ }
+ }
+ noUpdate = false;
+ }
+ if (!noUpdate) {
+ this.gases.resetStatus();
+ }
+ }
+ this.updatePowerStatus();
+ super.detectAndSendChanges();
+ }
+ }
+
+ @Override
+ public ItemStack transferStackInSlot(final EntityPlayer p, final int idx) {
+ if (Platform.isClient()) {
+ return ItemStack.EMPTY;
+ }
+ EntityPlayerMP player = (EntityPlayerMP) p;
+ if (this.inventorySlots.get(idx) instanceof SlotPlayerInv || this.inventorySlots.get(idx) instanceof SlotPlayerHotBar) {
+ final AppEngSlot clickSlot = (AppEngSlot) this.inventorySlots.get(idx); // require AE SLots!
+ ItemStack itemStack = clickSlot.getStack();
+
+ ItemStack copy = itemStack.copy();
+ copy.setCount(1);
+ ItemGasHandler gh = Utils.getItemGasHandler(copy);
+ if (gh == null) {
+ // only gas handlers items
+ return ItemStack.EMPTY;
+ }
+
+ int heldAmount = itemStack.getCount();
+ for (int i = 0; i < heldAmount; i++) {
+ copy = itemStack.copy();
+ copy.setCount(1);
+ gh = Utils.getItemGasHandler(copy);
+
+ final GasStack extract = gh.removeGas(Integer.MAX_VALUE, false);
+ if (extract == null || extract.amount < 1) {
+ return ItemStack.EMPTY;
+ }
+
+ final IAEGasStack notStorable = Platform.poweredInsert(this.getPowerSource(), this.monitor, AEGasStack.of(extract), this.getActionSource(), Actionable.SIMULATE);
+
+ if (notStorable != null && notStorable.getStackSize() > 0) {
+ final int toStore = (int) (extract.amount - notStorable.getStackSize());
+ final GasStack storable = gh.removeGas(toStore, false);
+ if (storable == null || storable.amount == 0) {
+ return ItemStack.EMPTY;
+ } else {
+ extract.amount = storable.amount;
+ }
+ }
+
+ // Actually drain
+ final GasStack drained = gh.removeGas(extract, true);
+ extract.amount = drained == null ? 0 : drained.amount;
+ final IAEGasStack notInserted = Platform.poweredInsert(this.getPowerSource(), this.monitor, AEGasStack.of(extract), this.getActionSource());
+ if (notInserted != null && notInserted.getStackSize() > 0) {
+ IAEGasStack spill = this.monitor.injectItems(notInserted, Actionable.MODULATE, this.getActionSource());
+ if (spill != null && spill.getStackSize() > 0) {
+ gh.addGas(spill.getGasStack(), true);
+ }
+ }
+
+ if (notInserted == null || notInserted.getStackSize() == 0) {
+ if (!player.inventory.addItemStackToInventory(gh.getContainer())) {
+ player.dropItem(gh.getContainer(), false);
+ }
+ clickSlot.decrStackSize(1);
+ }
+ }
+ this.detectAndSendChanges();
+ return ItemStack.EMPTY;
+ }
+ return super.transferStackInSlot(p, idx);
+ }
+
+ @Override
+ public void doAction(EntityPlayerMP player, InventoryAction action, int slot, long id) {
+ if (action != InventoryAction.FILL_ITEM && action != InventoryAction.EMPTY_ITEM) {
+ super.doAction(player, action, slot, id);
+ return;
+ }
+
+ final ItemStack held = player.inventory.getItemStack();
+ ItemStack heldCopy = held.copy();
+ heldCopy.setCount(1);
+ ItemGasHandler gh = Utils.getItemGasHandler(heldCopy);
+ if (gh == null) {
+ // only gas handlers items
+ return;
+ }
+
+ if (action == InventoryAction.FILL_ITEM && this.clientRequestedTargetGas != null) {
+ final IAEGasStack stack = this.clientRequestedTargetGas.copy();
+
+ // Check how much we can store in the item
+ stack.setStackSize(Integer.MAX_VALUE);
+ int amountAllowed = gh.addGas(stack.getGasStack(), false);
+ int heldAmount = held.getCount();
+
+ for (int i = 0; i < heldAmount; i++) {
+ ItemStack copiedGasContainer = held.copy();
+ copiedGasContainer.setCount(1);
+ gh = Utils.getItemGasHandler(copiedGasContainer);
+
+ // Check if we can pull out of the system
+ final IAEGasStack canPull = Platform.poweredExtraction(this.getPowerSource(), this.monitor, stack.setStackSize(amountAllowed), this.getActionSource(), Actionable.SIMULATE);
+ if (canPull == null || canPull.getStackSize() < 1) {
+ return;
+ }
+
+ // How much could fit into the container
+ final int canFill = gh.addGas(canPull.getGasStack(), false);
+
+ if (canFill == 0) {
+ return;
+ }
+
+ // Now actually pull out of the system
+ final IAEGasStack pulled = Platform.poweredExtraction(this.getPowerSource(), this.monitor, stack.setStackSize(canFill), this.getActionSource());
+ if (pulled == null || pulled.getStackSize() < 1) {
+ // Something went wrong
+ MekEng.log.error("Unable to pull gas out of the ME system even though the simulation said yes ");
+ return;
+ }
+
+ // Actually fill
+ final int used = gh.addGas(pulled.getGasStack(), true);
+
+ if (used != canFill) {
+ MekEng.log.error("Gas item [%s] reported a different possible amount than it actually accepted.", held.getDisplayName());
+ }
+
+ if (held.getCount() == 1) {
+ player.inventory.setItemStack(gh.getContainer());
+ } else {
+ player.inventory.getItemStack().shrink(1);
+ if (!player.inventory.addItemStackToInventory(gh.getContainer())) {
+ player.dropItem(gh.getContainer(), false);
+ }
+ }
+ }
+ this.updateHeld(player);
+ } else if (action == InventoryAction.EMPTY_ITEM) {
+ int heldAmount = held.getCount();
+ for (int i = 0; i < heldAmount; i++) {
+ ItemStack copiedGasContainer = held.copy();
+ copiedGasContainer.setCount(1);
+ gh = Utils.getItemGasHandler(copiedGasContainer);
+
+ // See how much we can drain from the item
+ final GasStack extract = gh.removeGas(Integer.MAX_VALUE, false);
+ if (extract == null || extract.amount < 1) {
+ return;
+ }
+
+ // Check if we can push into the system
+ final IAEGasStack notStorable = Platform.poweredInsert(this.getPowerSource(), this.monitor, AEGasStack.of(extract), this.getActionSource(), Actionable.SIMULATE);
+
+ if (notStorable != null && notStorable.getStackSize() > 0) {
+ final int toStore = (int) (extract.amount - notStorable.getStackSize());
+ final GasStack storable = gh.removeGas(toStore, false);
+ if (storable == null || storable.amount == 0) {
+ return;
+ } else {
+ extract.amount = storable.amount;
+ }
+ }
+
+ // Actually drain
+ final GasStack drained = gh.removeGas(extract, true);
+ extract.amount = drained == null ? 0 : drained.amount;
+
+ final IAEGasStack notInserted = Platform.poweredInsert(this.getPowerSource(), this.monitor, AEGasStack.of(extract), this.getActionSource());
+
+ if (notInserted != null && notInserted.getStackSize() > 0) {
+ IAEGasStack spill = this.monitor.injectItems(notInserted, Actionable.MODULATE, this.getActionSource());
+ if (spill != null && spill.getStackSize() > 0) {
+ gh.addGas(spill.getGasStack(), true);
+ }
+ }
+
+ if (held.getCount() == 1) {
+ player.inventory.setItemStack(gh.getContainer());
+ } else {
+ player.inventory.getItemStack().shrink(1);
+ if (!player.inventory.addItemStackToInventory(gh.getContainer())) {
+ player.dropItem(gh.getContainer(), false);
+ }
+ }
+ }
+ this.updateHeld(player);
+ }
+ }
+
+ protected void updatePowerStatus() {
+ try {
+ if (this.networkNode != null) {
+ this.setPowered(this.networkNode.isActive());
+ } else if (this.getPowerSource() instanceof IEnergyGrid) {
+ this.setPowered(((IEnergyGrid) this.getPowerSource()).isNetworkPowered());
+ } else {
+ this.setPowered(this.getPowerSource().extractAEPower(1, Actionable.SIMULATE, PowerMultiplier.CONFIG) > 0.8);
+ }
+ } catch (final Exception ignore) {
+ // :P
+ }
+ }
+
+ private IConfigManagerHost getGui() {
+ return this.gui;
+ }
+
+ public void setGui(@Nonnull final IConfigManagerHost gui) {
+ this.gui = gui;
+ }
+
+ public boolean isPowered() {
+ return this.hasPower;
+ }
+
+ private void setPowered(final boolean isPowered) {
+ this.hasPower = isPowered;
+ }
+
+ @Nonnull
+ @Override
+ public Map> getActionMap() {
+ return this.holder;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerMEPortableGasCell.java b/src/main/java/com/mekeng/github/common/container/ContainerMEPortableGasCell.java
new file mode 100644
index 0000000..97a6d43
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerMEPortableGasCell.java
@@ -0,0 +1,64 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.container.interfaces.IInventorySlotAware;
+import com.mekeng.github.common.me.storage.IPortableGasCell;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.item.ItemStack;
+
+public class ContainerMEPortableGasCell extends ContainerGasTerminal implements IInventorySlotAware {
+
+ protected final IPortableGasCell portableCell;
+ private final int slot;
+ private int ticks = 0;
+
+ public ContainerMEPortableGasCell(InventoryPlayer ip, IPortableGasCell guiObject) {
+ super(ip, guiObject, guiObject, false);
+ if (guiObject != null) {
+ final int slotIndex = ((IInventorySlotAware) guiObject).getInventorySlot();
+ if (!((IInventorySlotAware) guiObject).isBaubleSlot()) {
+ this.lockPlayerInventorySlot(slotIndex);
+ }
+ this.slot = slotIndex;
+ } else {
+ this.slot = -1;
+ this.lockPlayerInventorySlot(ip.currentItem);
+ }
+
+ this.bindPlayerInventory(ip, 0, 222 - 82);
+
+ this.portableCell = guiObject;
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ final ItemStack currentItem = this.slot < 0 ? this.getPlayerInv().getCurrentItem() : this.getPlayerInv().getStackInSlot(this.slot);
+
+ if (this.portableCell == null || currentItem.isEmpty()) {
+ this.setValidContainer(false);
+ } else if (!this.portableCell.getItemStack().isEmpty() && currentItem != this.portableCell.getItemStack()) {
+ if (!ItemStack.areItemsEqual(this.portableCell.getItemStack(), currentItem)) {
+ this.setValidContainer(false);
+ }
+ }
+
+ // drain 1 ae t
+ this.ticks++;
+ if (this.ticks > 10 && this.portableCell != null) {
+ this.portableCell.extractAEPower(this.getPowerMultiplier() * this.ticks, Actionable.MODULATE, PowerMultiplier.CONFIG);
+ this.ticks = 0;
+ }
+ super.detectAndSendChanges();
+ }
+
+ private double getPowerMultiplier() {
+ return 0.5;
+ }
+
+ @Override
+ public int getInventorySlot() {
+ return this.slot;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/common/container/ContainerWirelessGasTerminal.java b/src/main/java/com/mekeng/github/common/container/ContainerWirelessGasTerminal.java
new file mode 100644
index 0000000..6ba5e5f
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/ContainerWirelessGasTerminal.java
@@ -0,0 +1,198 @@
+package com.mekeng.github.common.container;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.PowerMultiplier;
+import appeng.api.implementations.IUpgradeableCellContainer;
+import appeng.api.networking.security.IActionHost;
+import appeng.container.interfaces.IInventorySlotAware;
+import appeng.container.slot.SlotRestrictedInput;
+import appeng.core.AEConfig;
+import appeng.core.localization.PlayerMessages;
+import appeng.helpers.WirelessTerminalGuiObject;
+import appeng.parts.automation.StackUpgradeInventory;
+import appeng.tile.inventory.AppEngInternalInventory;
+import appeng.util.Platform;
+import appeng.util.inv.IAEAppEngInventory;
+import appeng.util.inv.InvOperation;
+import baubles.api.BaublesApi;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.ClickType;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.items.IItemHandler;
+
+import javax.annotation.Nonnull;
+
+public class ContainerWirelessGasTerminal extends ContainerGasTerminal implements IUpgradeableCellContainer, IAEAppEngInventory, IInventorySlotAware {
+
+ protected final WirelessTerminalGuiObject wirelessTerminalGUIObject;
+ private final int slot;
+ private double powerMultiplier = 0.5;
+ private int ticks = 0;
+
+ protected AppEngInternalInventory upgrades;
+ protected SlotRestrictedInput magnetSlot;
+
+ public ContainerWirelessGasTerminal(InventoryPlayer ip, WirelessTerminalGuiObject guiObject) {
+ super(ip, guiObject, guiObject, false);
+ if (guiObject != null) {
+ final int slotIndex = ((IInventorySlotAware) guiObject).getInventorySlot();
+ if (!((IInventorySlotAware) guiObject).isBaubleSlot()) {
+ this.lockPlayerInventorySlot(slotIndex);
+ }
+ this.slot = slotIndex;
+ } else {
+ this.slot = -1;
+ this.lockPlayerInventorySlot(ip.currentItem);
+ }
+
+ this.bindPlayerInventory(ip, 0, 222 - 82);
+
+ this.wirelessTerminalGUIObject = guiObject;
+ this.upgrades = new StackUpgradeInventory(wirelessTerminalGUIObject.getItemStack(), this, 2);
+ this.loadFromNBT();
+ this.setupUpgrades();
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+ if (Platform.isServer()) {
+
+ final ItemStack currentItem;
+ if (wirelessTerminalGUIObject.isBaubleSlot()) {
+ currentItem = BaublesApi.getBaublesHandler(this.getPlayerInv().player).getStackInSlot(this.slot);
+ } else {
+ currentItem = this.slot < 0 ? this.getPlayerInv().getCurrentItem() : this.getPlayerInv().getStackInSlot(this.slot);
+ }
+
+ if (currentItem.isEmpty()) {
+ this.setValidContainer(false);
+ } else if (!this.wirelessTerminalGUIObject.getItemStack().isEmpty() && currentItem != this.wirelessTerminalGUIObject.getItemStack()) {
+ if (ItemStack.areItemsEqual(this.wirelessTerminalGUIObject.getItemStack(), currentItem)) {
+ if (wirelessTerminalGUIObject.isBaubleSlot()) {
+ BaublesApi.getBaublesHandler(this.getPlayerInv().player).setStackInSlot(this.slot, this.wirelessTerminalGUIObject.getItemStack());
+ } else {
+ this.getPlayerInv().setInventorySlotContents(this.slot, this.wirelessTerminalGUIObject.getItemStack());
+ }
+ } else {
+ this.setValidContainer(false);
+ }
+ }
+
+ // drain 1 ae t
+ this.ticks++;
+ if (this.ticks > 10) {
+ double ext = this.wirelessTerminalGUIObject.extractAEPower(this.getPowerMultiplier() * this.ticks, Actionable.MODULATE, PowerMultiplier.CONFIG);
+ if (ext < this.getPowerMultiplier() * this.ticks) {
+ if (Platform.isServer() && this.isValidContainer()) {
+ this.getPlayerInv().player.sendMessage(PlayerMessages.DeviceNotPowered.get());
+ }
+
+ this.setValidContainer(false);
+ }
+ this.ticks = 0;
+ }
+
+ if (!this.wirelessTerminalGUIObject.rangeCheck()) {
+ if (Platform.isServer() && this.isValidContainer()) {
+ this.getPlayerInv().player.sendMessage(PlayerMessages.OutOfRange.get());
+ }
+
+ this.setValidContainer(false);
+ } else {
+ this.setPowerMultiplier(AEConfig.instance().wireless_getDrainRate(this.wirelessTerminalGUIObject.getRange()));
+ }
+
+ super.detectAndSendChanges();
+ }
+ }
+
+ @Override
+ public ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, @Nonnull EntityPlayer player) {
+ if (slotId >= 0 && slotId < this.inventorySlots.size()) {
+ if (clickTypeIn == ClickType.PICKUP && dragType == 1) {
+ if (this.inventorySlots.get(slotId) == magnetSlot) {
+ ItemStack itemStack = magnetSlot.getStack();
+ if (!magnetSlot.getStack().isEmpty()) {
+ NBTTagCompound tag = itemStack.getTagCompound();
+ if (tag == null) {
+ tag = new NBTTagCompound();
+ }
+ if (tag.hasKey("enabled")) {
+ boolean e = tag.getBoolean("enabled");
+ tag.setBoolean("enabled", !e);
+ } else {
+ tag.setBoolean("enabled", false);
+ }
+ magnetSlot.getStack().setTagCompound(tag);
+ magnetSlot.onSlotChanged();
+ return ItemStack.EMPTY;
+ }
+ }
+ }
+ }
+ return super.slotClick(slotId, dragType, clickTypeIn, player);
+ }
+
+ @Override
+ protected IActionHost getActionHost() {
+ return this.wirelessTerminalGUIObject;
+ }
+
+ private double getPowerMultiplier() {
+ return this.powerMultiplier;
+ }
+
+ void setPowerMultiplier(final double powerMultiplier) {
+ this.powerMultiplier = powerMultiplier;
+ }
+
+ @Override
+ public int availableUpgrades() {
+ return 1;
+ }
+
+ @Override
+ public void setupUpgrades() {
+ if (wirelessTerminalGUIObject != null) {
+ for (int upgradeSlot = 0; upgradeSlot < availableUpgrades(); upgradeSlot++) {
+ this.magnetSlot = new SlotRestrictedInput(SlotRestrictedInput.PlacableItemType.UPGRADES, upgrades, upgradeSlot, 183, 139 + upgradeSlot * 18, this.getInventoryPlayer());
+ this.magnetSlot.setNotDraggable();
+ this.addSlotToContainer(magnetSlot);
+ }
+ }
+ }
+
+ @Override
+ public void saveChanges() {
+ if (Platform.isServer()) {
+ NBTTagCompound tag = new NBTTagCompound();
+ this.upgrades.writeToNBT(tag, "upgrades");
+
+ this.wirelessTerminalGUIObject.saveChanges(tag);
+ }
+ }
+
+ protected void loadFromNBT() {
+ NBTTagCompound data = wirelessTerminalGUIObject.getItemStack().getTagCompound();
+ if (data != null) {
+ upgrades.readFromNBT(wirelessTerminalGUIObject.getItemStack().getTagCompound().getCompoundTag("upgrades"));
+ }
+ }
+
+ @Override
+ public void onChangeInventory(IItemHandler inv, int slot, InvOperation mc, ItemStack removedStack, ItemStack newStack) {
+
+ }
+
+ @Override
+ public int getInventorySlot() {
+ return wirelessTerminalGUIObject.getInventorySlot();
+ }
+
+ @Override
+ public boolean isBaubleSlot() {
+ return wirelessTerminalGUIObject.isBaubleSlot();
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/container/handler/AEGuiBridge.java b/src/main/java/com/mekeng/github/common/container/handler/AEGuiBridge.java
new file mode 100644
index 0000000..5ea2c34
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/handler/AEGuiBridge.java
@@ -0,0 +1,53 @@
+package com.mekeng.github.common.container.handler;
+
+import appeng.api.parts.IPartHost;
+import appeng.core.sync.GuiWrapper;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.util.Utils;
+import net.minecraft.util.ResourceLocation;
+
+public class AEGuiBridge implements GuiWrapper.IExternalGui {
+
+ public static AEGuiBridge GAS_INTERFACE = new AEGuiBridge(MkEGuis.GAS_INTERFACE, "gas_interface");
+ public static AEGuiBridge GAS_STORAGE_BUS = new AEGuiBridge(MkEGuis.GAS_STORAGE_BUS, "gas_storage_bus");
+ public static AEGuiBridge GAS_TERMINAL = new AEGuiBridge(MkEGuis.GAS_TERMINAL, "gas_terminal");
+ public static AEGuiBridge WIRELESS_GAS_TERM = new AEGuiBridge(MkEGuis.WIRELESS_GAS_TERM, "wireless_gas_terminal");
+
+ final ResourceLocation id;
+ final GuiFactory> obj;
+
+ public AEGuiBridge(GuiFactory> factory, String key) {
+ this.id = MekEng.id(key);
+ this.obj = factory;
+ GuiWrapper.INSTANCE.registerExternalGuiHandler(this.id, this::openGui);
+ }
+
+ private void openGui(GuiWrapper.IExternalGui gui, GuiWrapper.GuiContext ctx) {
+ if (gui instanceof AEGuiBridge) {
+ GuiFactory> factory = ((AEGuiBridge) gui).obj;
+ if (ctx.pos != null) {
+ if (ctx.facing != null) {
+ if (ctx.world.getTileEntity(ctx.pos) instanceof IPartHost) {
+ GuiHandler.openPartGui(ctx.player, ctx.world, ctx.pos, ctx.facing, factory);
+ } else {
+ GuiHandler.openTileGui(ctx.player, ctx.world, ctx.pos, factory);
+ }
+ } else {
+ GuiHandler.openTileGui(ctx.player, ctx.world, ctx.pos, factory);
+ }
+ } else if (ctx.extra != null) {
+ int slot = ctx.extra.getInteger("slot");
+ boolean isBauble = ctx.extra.getBoolean("isBauble");
+ GuiHandler.openItemGui(ctx.player, ctx.world, slot, isBauble, factory);
+ } else {
+ Utils.openItemGui(ctx.player, factory);
+ }
+ }
+ }
+
+ @Override
+ public ResourceLocation getID() {
+ return this.id;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/handler/GuiFactory.java b/src/main/java/com/mekeng/github/common/container/handler/GuiFactory.java
new file mode 100644
index 0000000..f682a67
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/handler/GuiFactory.java
@@ -0,0 +1,109 @@
+package com.mekeng.github.common.container.handler;
+
+import appeng.api.AEApi;
+import appeng.api.features.IWirelessTermHandler;
+import appeng.api.implementations.guiobjects.IGuiItem;
+import appeng.api.parts.IPart;
+import appeng.api.parts.IPartHost;
+import appeng.api.util.AEPartLocation;
+import appeng.container.AEBaseContainer;
+import appeng.container.ContainerOpenContext;
+import appeng.helpers.WirelessTerminalGuiObject;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import javax.annotation.Nullable;
+
+public abstract class GuiFactory {
+
+ protected final Class invClass;
+ private final int id;
+
+ public GuiFactory(Class invClass) {
+ this.invClass = invClass;
+ this.id = MkEGuis.registerFactory(this);
+ }
+
+ public int getId() {
+ return this.id;
+ }
+
+ protected abstract Object createServerGui(EntityPlayer player, T inv);
+
+ protected abstract Object createClientGui(EntityPlayer player, T inv);
+
+ @Nullable
+ protected T getInventory(@Nullable TileEntity tile, EntityPlayer player, EnumFacing face, int slot, boolean isBauble, GuiMode mode) {
+ switch (mode) {
+ case TILE:
+ return invClass.isInstance(tile) ? invClass.cast(tile) : null;
+ case PART:
+ IPart part = null;
+ if (tile instanceof IPartHost) {
+ part = ((IPartHost) tile).getPart(face);
+ }
+ return invClass.isInstance(part) ? invClass.cast(part) : null;
+ case ITEM:
+ ItemStack stack = player.inventory.getCurrentItem();
+ Object guiObj = null;
+ if (!stack.isEmpty()) {
+ guiObj = getItemGuiObject(stack, player, player.world, slot, isBauble);
+ }
+ return invClass.isInstance(guiObj) ? invClass.cast(guiObj) : null;
+ }
+ return null;
+ }
+
+ public Object createElement(EntityPlayer player, World world, int x, int y, int z, EnumFacing face, GuiMode mode, boolean isServer) {
+ TileEntity tile = mode == GuiMode.ITEM ? null : world.getTileEntity(new BlockPos(x, y, z));
+ int slot = 0;
+ boolean isBauble = false;
+ if (mode == GuiMode.ITEM) {
+ slot = x;
+ isBauble = y == 1;
+ }
+ T inv = getInventory(tile, player, face, slot, isBauble, mode);
+ if (inv == null) {
+ return null;
+ }
+ Object obj = isServer ? createServerGui(player, inv) : createClientGui(player, inv);
+ if (obj instanceof AEBaseContainer) {
+ ContainerOpenContext ctx = new ContainerOpenContext(inv);
+ ctx.setWorld(world);
+ ctx.setX(x);
+ ctx.setY(y);
+ ctx.setZ(z);
+ if (mode == GuiMode.PART) {
+ ctx.setSide(AEPartLocation.fromFacing(face));
+ } else {
+ ctx.setSide(AEPartLocation.INTERNAL);
+ }
+ ((AEBaseContainer) obj).setOpenContext(ctx);
+ }
+ return obj;
+ }
+
+ private static Object getItemGuiObject(ItemStack it, EntityPlayer player, World w, int slot, boolean isBauble) {
+ if (!it.isEmpty()) {
+ if (it.getItem() instanceof IGuiItem) {
+ return ((IGuiItem)it.getItem()).getGuiObject(it, w, new BlockPos(slot, isBauble ? 1 : 0, 0));
+ }
+ IWirelessTermHandler wh = AEApi.instance().registries().wireless().getWirelessTerminalHandler(it);
+ if (wh != null) {
+ return new WirelessTerminalGuiObject(wh, it, player, w, slot, isBauble ? 1 : 0, 0);
+ }
+ }
+ return null;
+ }
+
+ public enum GuiMode {
+ TILE,
+ PART,
+ ITEM
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/handler/GuiHandler.java b/src/main/java/com/mekeng/github/common/container/handler/GuiHandler.java
new file mode 100644
index 0000000..b258921
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/handler/GuiHandler.java
@@ -0,0 +1,62 @@
+package com.mekeng.github.common.container.handler;
+
+import com.mekeng.github.MekEng;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.common.network.IGuiHandler;
+
+import javax.annotation.Nullable;
+
+public class GuiHandler implements IGuiHandler {
+
+ public static final GuiHandler INSTANCE = new GuiHandler();
+
+ private GuiHandler() {
+ // NO-OP
+ }
+
+ public static void openGui(EntityPlayer player, World world, int x, int y, int z, GuiFactory.GuiMode mode, EnumFacing face, GuiFactory> gui) {
+ player.openGui(MekEng.INSTANCE, (gui.getId() << 5) | (mode.ordinal() << 3) | (face.getIndex()), world, x, y, z);
+ }
+
+ public static void openTileGui(EntityPlayer player, World world, BlockPos pos, GuiFactory> gui) {
+ player.openGui(MekEng.INSTANCE, gui.getId() << 5, world, pos.getX(), pos.getY(), pos.getZ());
+ }
+
+ public static void openPartGui(EntityPlayer player, World world, BlockPos pos, EnumFacing face, GuiFactory> gui) {
+ player.openGui(MekEng.INSTANCE, (gui.getId() << 5) | (1 << 3) | (face.getIndex()), world, pos.getX(), pos.getY(), pos.getZ());
+ }
+
+ public static void openItemGui(EntityPlayer player, World world, int slot, boolean isBauble, GuiFactory> gui) {
+ player.openGui(MekEng.INSTANCE, (gui.getId() << 5) | (2 << 3), world, slot, isBauble ? 1 : 0, 0);
+ }
+
+ /**
+ * ID structure:
+ * xxxx xxxx xxxy yzzz
+ * x: Gui ID
+ * y: Gui mode
+ * z: facing
+ */
+ @Nullable
+ @Override
+ public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
+ GuiFactory> gui = MkEGuis.getFactory(ID >>> 5);
+ GuiFactory.GuiMode mode = GuiFactory.GuiMode.values()[(ID >>> 3) & 0b11];
+ EnumFacing face = EnumFacing.byIndex(ID & 0b111);
+ return gui != null ? gui.createElement(player, world, x, y, z, face, mode, true) : null;
+ }
+
+ @Nullable
+ @Override
+ public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
+ GuiFactory> gui = MkEGuis.getFactory(ID >>> 5);
+ GuiFactory.GuiMode mode = GuiFactory.GuiMode.values()[(ID >>> 3) & 0b11];
+ EnumFacing face = EnumFacing.byIndex(ID & 0b111);
+ return gui != null ? gui.createElement(player, world, x, y, z, face, mode, false) : null;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/handler/MkEGuis.java b/src/main/java/com/mekeng/github/common/container/handler/MkEGuis.java
new file mode 100644
index 0000000..66ebd13
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/handler/MkEGuis.java
@@ -0,0 +1,143 @@
+package com.mekeng.github.common.container.handler;
+
+import appeng.api.storage.ITerminalHost;
+import appeng.helpers.WirelessTerminalGuiObject;
+import com.mekeng.github.client.gui.GuiGasIO;
+import com.mekeng.github.client.gui.GuiGasInterface;
+import com.mekeng.github.client.gui.GuiGasInterfaceConfigurationTerminal;
+import com.mekeng.github.client.gui.GuiGasLevelEmitter;
+import com.mekeng.github.client.gui.GuiGasStorageBus;
+import com.mekeng.github.client.gui.GuiGasTerminal;
+import com.mekeng.github.client.gui.GuiMEPortableGasCell;
+import com.mekeng.github.client.gui.GuiWirelessGasTerminal;
+import com.mekeng.github.common.container.ContainerGasIO;
+import com.mekeng.github.common.container.ContainerGasInterface;
+import com.mekeng.github.common.container.ContainerGasInterfaceConfigurationTerminal;
+import com.mekeng.github.common.container.ContainerGasLevelEmitter;
+import com.mekeng.github.common.container.ContainerGasStorageBus;
+import com.mekeng.github.common.container.ContainerGasTerminal;
+import com.mekeng.github.common.container.ContainerMEPortableGasCell;
+import com.mekeng.github.common.container.ContainerWirelessGasTerminal;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.storage.IPortableGasCell;
+import com.mekeng.github.common.part.PartGasInterfaceConfigurationTerminal;
+import com.mekeng.github.common.part.PartGasLevelEmitter;
+import com.mekeng.github.common.part.PartGasStorageBus;
+import com.mekeng.github.common.part.PartSharedGasBus;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import net.minecraft.entity.player.EntityPlayer;
+
+public class MkEGuis {
+
+ private static final Int2ObjectMap> GUIS = new Int2ObjectOpenHashMap<>();
+ private static int nextID = 1;
+
+ public static GuiFactory GAS_TERMINAL = new GuiFactory(ITerminalHost.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, ITerminalHost inv) {
+ return new ContainerGasTerminal(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, ITerminalHost inv) {
+ return new GuiGasTerminal(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory GAS_IO_BUS = new GuiFactory(PartSharedGasBus.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, PartSharedGasBus inv) {
+ return new ContainerGasIO(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, PartSharedGasBus inv) {
+ return new GuiGasIO(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory GAS_INTERFACE = new GuiFactory(IGasInterfaceHost.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, IGasInterfaceHost inv) {
+ return new ContainerGasInterface(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, IGasInterfaceHost inv) {
+ return new GuiGasInterface(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory GAS_STORAGE_BUS = new GuiFactory(PartGasStorageBus.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, PartGasStorageBus inv) {
+ return new ContainerGasStorageBus(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, PartGasStorageBus inv) {
+ return new GuiGasStorageBus(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory GAS_LEVEL_EMITTER = new GuiFactory(PartGasLevelEmitter.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, PartGasLevelEmitter inv) {
+ return new ContainerGasLevelEmitter(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, PartGasLevelEmitter inv) {
+ return new GuiGasLevelEmitter(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory GAS_INTERFACE_TERMINAL = new GuiFactory(PartGasInterfaceConfigurationTerminal.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, PartGasInterfaceConfigurationTerminal inv) {
+ return new ContainerGasInterfaceConfigurationTerminal(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, PartGasInterfaceConfigurationTerminal inv) {
+ return new GuiGasInterfaceConfigurationTerminal(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory PORTABLE_GAS_CELL = new GuiFactory(IPortableGasCell.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, IPortableGasCell inv) {
+ return new ContainerMEPortableGasCell(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, IPortableGasCell inv) {
+ return new GuiMEPortableGasCell(player.inventory, inv);
+ }
+ };
+
+ public static GuiFactory WIRELESS_GAS_TERM = new GuiFactory(WirelessTerminalGuiObject.class) {
+ @Override
+ protected Object createServerGui(EntityPlayer player, WirelessTerminalGuiObject inv) {
+ return new ContainerWirelessGasTerminal(player.inventory, inv);
+ }
+
+ @Override
+ protected Object createClientGui(EntityPlayer player, WirelessTerminalGuiObject inv) {
+ return new GuiWirelessGasTerminal(player.inventory, inv);
+ }
+ };
+
+ protected static synchronized int registerFactory(GuiFactory> factory) {
+ GUIS.put(nextID, factory);
+ int id = nextID;
+ nextID ++;
+ return id;
+ }
+
+ public static GuiFactory> getFactory(int id) {
+ return GUIS.get(id);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/container/sync/IGasSyncContainer.java b/src/main/java/com/mekeng/github/common/container/sync/IGasSyncContainer.java
new file mode 100644
index 0000000..0874ade
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/container/sync/IGasSyncContainer.java
@@ -0,0 +1,11 @@
+package com.mekeng.github.common.container.sync;
+
+import com.mekeng.github.common.me.data.IAEGasStack;
+
+import java.util.Map;
+
+public interface IGasSyncContainer {
+
+ void receiveGasSlots(final Map gases);
+
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemDummyGas.java b/src/main/java/com/mekeng/github/common/item/ItemDummyGas.java
new file mode 100644
index 0000000..0cf6cae
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemDummyGas.java
@@ -0,0 +1,62 @@
+package com.mekeng.github.common.item;
+
+import appeng.items.AEBaseItem;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.client.model.SpecialModel;
+import mekanism.api.gas.GasStack;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.text.translation.I18n;
+
+import javax.annotation.Nonnull;
+
+public class ItemDummyGas extends AEBaseItem implements SpecialModel {
+
+ public ItemDummyGas() {
+ setMaxStackSize(1);
+ }
+
+ @SuppressWarnings("deprecation")
+ @Nonnull
+ @Override
+ public String getItemStackDisplayName(@Nonnull ItemStack stack) {
+ GasStack gasStack = this.getGasStack(stack);
+ if (gasStack == null) {
+ return I18n.translateToLocal("item.mekeng:dummy_gas.error.name");
+ }
+ return gasStack.getGas().getLocalizedName();
+ }
+
+ public GasStack getGasStack(ItemStack is) {
+ if (is.hasTagCompound()) {
+ NBTTagCompound tag = is.getTagCompound();
+ return GasStack.readFromNBT(tag);
+ } else {
+ return null;
+ }
+ }
+
+ public void setGasStack(ItemStack is, GasStack gs) {
+ if (gs == null) {
+ is.setTagCompound(null);
+ } else {
+ NBTTagCompound tag = new NBTTagCompound();
+ gs.write(tag);
+ is.setTagCompound(tag);
+ }
+ }
+
+ @Override
+ public void getCheckedSubItems(@Nonnull CreativeTabs tab, @Nonnull NonNullList items) {
+ // NO-OP
+ }
+
+ @Override
+ public ResourceLocation getModelPath() {
+ return MekEng.id("model/dummy_gas");
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemGasCell.java b/src/main/java/com/mekeng/github/common/item/ItemGasCell.java
new file mode 100644
index 0000000..63981b7
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemGasCell.java
@@ -0,0 +1,200 @@
+package com.mekeng.github.common.item;
+
+import appeng.api.AEApi;
+import appeng.api.config.FuzzyMode;
+import appeng.api.implementations.items.IItemGroup;
+import appeng.api.implementations.items.IStorageCell;
+import appeng.api.implementations.items.IUpgradeModule;
+import appeng.api.storage.IMEInventoryHandler;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.data.IItemList;
+import appeng.core.AEConfig;
+import appeng.core.features.AEFeature;
+import appeng.core.localization.GuiText;
+import appeng.items.AEBaseItem;
+import appeng.items.contents.CellUpgrades;
+import appeng.util.InventoryAdaptor;
+import appeng.util.Platform;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.util.helpers.GasCellConfig;
+import com.mekeng.github.util.helpers.GasCellInfo;
+import net.minecraft.client.util.ITooltipFlag;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+import net.minecraftforge.items.IItemHandler;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+import java.util.Set;
+
+public class ItemGasCell extends AEBaseItem implements IStorageCell, IItemGroup {
+
+ private final ItemStack core;
+ private final ItemStack casing;
+ private final int totalBytes;
+ private final double idleDrain;
+ private final int perType;
+
+ public ItemGasCell(ItemStack core, ItemStack casing, int kilobytes, double powerDrain, int typeCost) {
+ setMaxStackSize(1);
+ this.core = core.copy();
+ this.casing = casing.copy();
+ this.totalBytes = kilobytes * 1024;
+ this.idleDrain = powerDrain;
+ this.perType = typeCost;
+ }
+
+ public ItemGasCell(Item core, ItemStack casing, int kilobytes, double powerDrain, int typeCost) {
+ this(new ItemStack(core), casing, kilobytes, powerDrain, typeCost);
+ }
+
+ @Nonnull
+ @Override
+ public ActionResult onItemRightClick(@Nonnull World world, @Nonnull EntityPlayer player, @Nonnull EnumHand hand) {
+ this.disassembleDrive(player.getHeldItem(hand), player);
+ return new ActionResult<>(EnumActionResult.SUCCESS, player.getHeldItem(hand));
+ }
+
+ @Nonnull
+ @Override
+ public EnumActionResult onItemUseFirst(@Nonnull EntityPlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumFacing side, float hitX, float hitY, float hitZ, @Nonnull EnumHand hand) {
+ return this.disassembleDrive(player.getHeldItem(hand), player) ? EnumActionResult.SUCCESS : EnumActionResult.PASS;
+ }
+
+ @SideOnly(Side.CLIENT)
+ public void addCheckedInformation(ItemStack stack, World world, List lines, ITooltipFlag advancedTooltips) {
+ GasCellInfo.addCellInformation(AEApi.instance().registries().cell().getCellInventory(stack, null, this.getChannel()), lines);
+ }
+
+ @Nonnull
+ @Override
+ public ItemStack getContainerItem(@Nonnull ItemStack itemStack) {
+ return this.casing.copy();
+ }
+
+ @Override
+ public boolean hasContainerItem(@Nonnull ItemStack stack) {
+ return AEConfig.instance().isFeatureEnabled(AEFeature.ENABLE_DISASSEMBLY_CRAFTING);
+ }
+
+ @Override
+ public int getBytes(@Nonnull ItemStack itemStack) {
+ return this.totalBytes;
+ }
+
+ @Override
+ public int getBytesPerType(@Nonnull ItemStack itemStack) {
+ return this.perType;
+ }
+
+ @Override
+ public int getTotalTypes(@Nonnull ItemStack itemStack) {
+ return 15;
+ }
+
+ @Override
+ public boolean isBlackListed(@Nonnull ItemStack itemStack, @Nonnull IAEGasStack gasStack) {
+ return false;
+ }
+
+ @Override
+ public boolean storableInStorageCell() {
+ return false;
+ }
+
+ @Override
+ public boolean isStorageCell(@Nonnull ItemStack itemStack) {
+ return true;
+ }
+
+ @Override
+ public double getIdleDrain() {
+ return this.idleDrain;
+ }
+
+ @Nonnull
+ @Override
+ public IStorageChannel getChannel() {
+ return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public boolean isEditable(ItemStack itemStack) {
+ return true;
+ }
+
+ @Override
+ public IItemHandler getUpgradesInventory(ItemStack is) {
+ return new CellUpgrades(is, 2);
+ }
+
+ @Override
+ public IItemHandler getConfigInventory(ItemStack is) {
+ return new GasCellConfig(is);
+ }
+
+ @Override
+ public FuzzyMode getFuzzyMode(ItemStack itemStack) {
+ return FuzzyMode.IGNORE_ALL;
+ }
+
+ @Override
+ public void setFuzzyMode(ItemStack itemStack, FuzzyMode fuzzyMode) {
+
+ }
+
+ private boolean disassembleDrive(ItemStack stack, EntityPlayer player) {
+ if (player.isSneaking()) {
+ if (Platform.isClient()) {
+ return false;
+ }
+ InventoryPlayer playerInventory = player.inventory;
+ IMEInventoryHandler inv = AEApi.instance().registries().cell().getCellInventory(stack, null, this.getChannel());
+ if (inv != null && playerInventory.getCurrentItem() == stack) {
+ InventoryAdaptor ia = InventoryAdaptor.getAdaptor(player);
+ IItemList list = inv.getAvailableItems(this.getChannel().createList());
+ if (list.isEmpty()) {
+ playerInventory.setInventorySlotContents(playerInventory.currentItem, ItemStack.EMPTY);
+ ItemStack extraB = ia.addItems(this.core.copy());
+ if (!extraB.isEmpty()) {
+ player.dropItem(extraB, false);
+ }
+ IItemHandler upgradesInventory = this.getUpgradesInventory(stack);
+ for (int upgradeIndex = 0; upgradeIndex < upgradesInventory.getSlots(); ++upgradeIndex) {
+ ItemStack upgradeStack = upgradesInventory.getStackInSlot(upgradeIndex);
+ ItemStack leftStack = ia.addItems(upgradeStack);
+ if (!leftStack.isEmpty() && upgradeStack.getItem() instanceof IUpgradeModule) {
+ player.dropItem(upgradeStack, false);
+ }
+ }
+ ItemStack extraA = ia.addItems(this.casing.copy());
+ if (!extraA.isEmpty()) {
+ player.dropItem(extraA, false);
+ }
+ if (player.inventoryContainer != null) {
+ player.inventoryContainer.detectAndSendChanges();
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public String getUnlocalizedGroupName(Set otherItems, ItemStack is) {
+ return GuiText.StorageCells.getUnlocalized();
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemMkEPart.java b/src/main/java/com/mekeng/github/common/item/ItemMkEPart.java
new file mode 100644
index 0000000..043e2b4
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemMkEPart.java
@@ -0,0 +1,50 @@
+package com.mekeng.github.common.item;
+
+import appeng.api.AEApi;
+import appeng.api.implementations.items.IItemGroup;
+import appeng.api.parts.IPart;
+import appeng.api.parts.IPartItem;
+import appeng.items.AEBaseItem;
+import com.mekeng.github.common.part.IPartGroup;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+import java.util.function.Function;
+
+public class ItemMkEPart extends AEBaseItem implements IPartItem, IItemGroup {
+
+ private final Function factory;
+
+ public ItemMkEPart(Function factory) {
+ this.factory = factory;
+ }
+
+ @Nonnull
+ @Override
+ public EnumActionResult onItemUse(@Nonnull EntityPlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull EnumHand hand, @Nonnull EnumFacing side, float hitX, float hitY, float hitZ) {
+ return AEApi.instance().partHelper().placeBus(player.getHeldItem(hand), pos, side, player, hand, world);
+ }
+
+ @Nullable
+ @Override
+ public T createPartFromItemStack(ItemStack stack) {
+ return this.factory.apply(stack);
+ }
+
+ @Override
+ public String getUnlocalizedGroupName(Set otherItems, ItemStack is) {
+ IPart obj = this.factory.apply(is);
+ if (obj instanceof IPartGroup) {
+ return ((IPartGroup) obj).getUnlocalizedGroupName();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemNormal.java b/src/main/java/com/mekeng/github/common/item/ItemNormal.java
new file mode 100644
index 0000000..c42a2a8
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemNormal.java
@@ -0,0 +1,7 @@
+package com.mekeng.github.common.item;
+
+import appeng.items.AEBaseItem;
+
+public class ItemNormal extends AEBaseItem {
+
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemPortableGasCell.java b/src/main/java/com/mekeng/github/common/item/ItemPortableGasCell.java
new file mode 100644
index 0000000..ab8c162
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemPortableGasCell.java
@@ -0,0 +1,149 @@
+package com.mekeng.github.common.item;
+
+import appeng.api.AEApi;
+import appeng.api.config.FuzzyMode;
+import appeng.api.implementations.guiobjects.IGuiItem;
+import appeng.api.implementations.guiobjects.IGuiItemObject;
+import appeng.api.implementations.items.IItemGroup;
+import appeng.api.implementations.items.IStorageCell;
+import appeng.api.storage.ICellInventoryHandler;
+import appeng.api.storage.IStorageChannel;
+import appeng.core.AEConfig;
+import appeng.core.localization.GuiText;
+import appeng.items.contents.CellUpgrades;
+import appeng.items.tools.powered.powersink.AEBasePoweredItem;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.GasCellConfig;
+import com.mekeng.github.util.helpers.GasCellInfo;
+import com.mekeng.github.util.helpers.PortableGasCellViewer;
+import net.minecraft.client.util.ITooltipFlag;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+import net.minecraftforge.items.IItemHandler;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+import java.util.Set;
+
+public class ItemPortableGasCell extends AEBasePoweredItem implements IStorageCell, IGuiItem, IItemGroup {
+ public ItemPortableGasCell() {
+ super(AEConfig.instance().getPortableCellBattery());
+ }
+
+ @Nonnull
+ @Override
+ public ActionResult onItemRightClick(@Nonnull final World w, @Nonnull final EntityPlayer player, @Nonnull final EnumHand hand) {
+ Utils.openItemGui(player, MkEGuis.PORTABLE_GAS_CELL);
+ return new ActionResult<>(EnumActionResult.SUCCESS, player.getHeldItem(hand));
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public boolean isFull3D() {
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public void addCheckedInformation(final ItemStack stack, final World world, final List lines, final ITooltipFlag advancedTooltips) {
+ super.addCheckedInformation(stack, world, lines, advancedTooltips);
+
+ final ICellInventoryHandler cdi = AEApi.instance()
+ .registries()
+ .cell()
+ .getCellInventory(stack, null, AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class));
+
+ GasCellInfo.addCellInformation(cdi, lines);
+ }
+
+ @Override
+ public int getBytes(@Nonnull final ItemStack cellItem) {
+ return 512;
+ }
+
+ @Override
+ public int getBytesPerType(@Nonnull final ItemStack cellItem) {
+ return 8;
+ }
+
+ @Override
+ public int getTotalTypes(@Nonnull final ItemStack cellItem) {
+ return 5;
+ }
+
+ @Override
+ public boolean isBlackListed(@Nonnull final ItemStack cellItem, @Nonnull final IAEGasStack requestedAddition) {
+ return false;
+ }
+
+ @Override
+ public boolean storableInStorageCell() {
+ return false;
+ }
+
+ @Override
+ public boolean isStorageCell(@Nonnull final ItemStack i) {
+ return true;
+ }
+
+ @Override
+ public double getIdleDrain() {
+ return 0.5;
+ }
+
+ @Nonnull
+ @Override
+ public IStorageChannel getChannel() {
+ return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public String getUnlocalizedGroupName(final Set others, final ItemStack is) {
+ return GuiText.StorageCells.getUnlocalized();
+ }
+
+ @Override
+ public boolean isEditable(final ItemStack is) {
+ return true;
+ }
+
+ @Override
+ public IItemHandler getUpgradesInventory(final ItemStack is) {
+ return new CellUpgrades(is, 2);
+ }
+
+ @Override
+ public IItemHandler getConfigInventory(final ItemStack is) {
+ return new GasCellConfig(is);
+ }
+
+ @Override
+ public FuzzyMode getFuzzyMode(final ItemStack is) {
+ return FuzzyMode.IGNORE_ALL;
+ }
+
+ @Override
+ public void setFuzzyMode(final ItemStack is, final FuzzyMode fzMode) {
+ // NO-OP
+ }
+
+ @Override
+ public IGuiItemObject getGuiObject(final ItemStack is, final World w, final BlockPos pos) {
+ return new PortableGasCellViewer(is, pos.getX());
+ }
+
+ @Override
+ public boolean shouldCauseReequipAnimation(@Nonnull ItemStack oldStack, @Nonnull ItemStack newStack, boolean slotChanged) {
+ return slotChanged;
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/item/ItemWirelessGasTerminal.java b/src/main/java/com/mekeng/github/common/item/ItemWirelessGasTerminal.java
new file mode 100644
index 0000000..17edab4
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/item/ItemWirelessGasTerminal.java
@@ -0,0 +1,22 @@
+package com.mekeng.github.common.item;
+
+import appeng.core.sync.GuiWrapper;
+import appeng.items.tools.powered.ToolWirelessTerminal;
+import com.mekeng.github.common.ItemAndBlocks;
+import com.mekeng.github.common.container.handler.AEGuiBridge;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fml.common.network.IGuiHandler;
+
+public class ItemWirelessGasTerminal extends ToolWirelessTerminal {
+
+ @Override
+ public boolean canHandle(final ItemStack is) {
+ return is.getItem() == ItemAndBlocks.WIRELESS_GAS_TERMINAL;
+ }
+
+ @Override
+ public IGuiHandler getGuiHandler(ItemStack is) {
+ return GuiWrapper.INSTANCE.wrap(AEGuiBridge.WIRELESS_GAS_TERM);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/GasTickRates.java b/src/main/java/com/mekeng/github/common/me/GasTickRates.java
new file mode 100644
index 0000000..9666f37
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/GasTickRates.java
@@ -0,0 +1,25 @@
+package com.mekeng.github.common.me;
+
+public enum GasTickRates {
+
+ GasStorageBus(5, 60),
+ GasImportBus(5, 40),
+ GasExportBus(5, 60);
+
+ private final int min;
+ private final int max;
+
+ GasTickRates(final int min, final int max) {
+ this.min = min;
+ this.max = max;
+ }
+
+ public int getMax() {
+ return this.max;
+ }
+
+ public int getMin() {
+ return this.min;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/client/ClientDCInternalGasInv.java b/src/main/java/com/mekeng/github/common/me/client/ClientDCInternalGasInv.java
new file mode 100644
index 0000000..62d9d28
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/client/ClientDCInternalGasInv.java
@@ -0,0 +1,51 @@
+package com.mekeng.github.common.me.client;
+
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.impl.GasInventory;
+import net.minecraft.util.text.translation.I18n;
+
+import javax.annotation.Nonnull;
+
+public class ClientDCInternalGasInv implements Comparable {
+
+ private final String unlocalizedName;
+ private final IGasInventory inventory;
+
+ private final long id;
+ private final long sortBy;
+
+ public ClientDCInternalGasInv(final int size, final long id, final long sortBy, final String unlocalizedName) {
+ this.inventory = new GasInventory(size, 1);
+ this.unlocalizedName = unlocalizedName;
+ this.id = id;
+ this.sortBy = sortBy;
+ }
+
+ public ClientDCInternalGasInv(final int size, final long id, final long sortBy, final String unlocalizedName, int stackSize) {
+ this.inventory = new GasInventory(size, stackSize);
+ this.unlocalizedName = unlocalizedName;
+ this.id = id;
+ this.sortBy = sortBy;
+ }
+
+ public String getName() {
+ final String s = I18n.translateToLocal(this.unlocalizedName + ".name");
+ if (s.equals(this.unlocalizedName + ".name")) {
+ return I18n.translateToLocal(this.unlocalizedName);
+ }
+ return s;
+ }
+
+ @Override
+ public int compareTo(@Nonnull final ClientDCInternalGasInv o) {
+ return Long.compare(this.sortBy, o.sortBy);
+ }
+
+ public IGasInventory getInventory() {
+ return this.inventory;
+ }
+
+ public long getId() {
+ return this.id;
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/me/client/GasRepo.java b/src/main/java/com/mekeng/github/common/me/client/GasRepo.java
new file mode 100644
index 0000000..39d3c3a
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/client/GasRepo.java
@@ -0,0 +1,176 @@
+package com.mekeng.github.common.me.client;
+
+import appeng.api.AEApi;
+import appeng.api.config.Settings;
+import appeng.api.config.SortOrder;
+import appeng.api.config.ViewItems;
+import appeng.api.config.YesNo;
+import appeng.api.storage.data.IItemList;
+import appeng.client.gui.widgets.IScrollSource;
+import appeng.client.gui.widgets.ISortSource;
+import appeng.core.AEConfig;
+import appeng.util.Platform;
+import appeng.util.prioritylist.IPartitionList;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.util.Utils;
+import com.mekeng.github.util.helpers.GasSorters;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class GasRepo {
+ private final IItemList list = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+ private final ArrayList view = new ArrayList<>();
+ private final IScrollSource src;
+ private final ISortSource sortSrc;
+
+ private int rowSize = 9;
+
+ private String searchString = "";
+ private IPartitionList myPartitionList;
+ private boolean hasPower;
+
+ public GasRepo(final IScrollSource src, final ISortSource sortSrc) {
+ this.src = src;
+ this.sortSrc = sortSrc;
+ }
+
+ public void updateView() {
+ this.view.clear();
+
+ this.view.ensureCapacity(this.list.size());
+
+ String innerSearch = this.searchString;
+
+ boolean searchMod = false;
+ if (innerSearch.startsWith("@")) {
+ searchMod = true;
+ innerSearch = innerSearch.substring(1);
+ }
+
+ Pattern m;
+ try {
+ m = Pattern.compile(innerSearch.toLowerCase(), Pattern.CASE_INSENSITIVE);
+ } catch (final Exception ignore1) {
+ try {
+ m = Pattern.compile(Pattern.quote(innerSearch.toLowerCase()), Pattern.CASE_INSENSITIVE);
+ } catch (final Exception ignore2) {
+ return;
+ }
+ }
+
+ final Enum> viewMode = this.sortSrc.getSortDisplay();
+ final boolean needsZeroCopy = viewMode == ViewItems.CRAFTABLE;
+ final boolean terminalSearchToolTips = AEConfig.instance().getConfigManager().getSetting(Settings.SEARCH_TOOLTIPS) != YesNo.NO;
+
+ boolean notDone;
+ for (IAEGasStack gs : this.list) {
+ if (this.myPartitionList != null && !this.myPartitionList.isListed(gs)) {
+ continue;
+ }
+
+ if (viewMode == ViewItems.CRAFTABLE && !gs.isCraftable()) {
+ continue;
+ }
+
+ if (viewMode == ViewItems.STORED && gs.getStackSize() == 0) {
+ continue;
+ }
+
+ final String dspName = searchMod ? Utils.getGasModID(gs) : Utils.getGasDisplayName(gs);
+ boolean foundMatchingGasStack = false;
+ notDone = true;
+
+ if (m.matcher(dspName.toLowerCase()).find()) {
+ notDone = false;
+ foundMatchingGasStack = true;
+ }
+
+ if (terminalSearchToolTips && notDone && !searchMod) {
+ final List tooltip = Platform.getTooltip(gs);
+
+ for (final String line : tooltip) {
+ if (m.matcher(line).find()) {
+ foundMatchingGasStack = true;
+ break;
+ }
+ }
+ }
+
+ if (foundMatchingGasStack) {
+ if (needsZeroCopy) {
+ gs = gs.copy();
+ gs.setStackSize(0);
+ }
+
+ this.view.add(gs);
+ }
+ }
+
+ final Enum> sortBy = this.sortSrc.getSortBy();
+ final Enum> sortDir = this.sortSrc.getSortDir();
+
+ GasSorters.setDirection((appeng.api.config.SortDir) sortDir);
+
+ if (sortBy == SortOrder.MOD) {
+ this.view.sort(GasSorters.CONFIG_BASED_SORT_BY_MOD);
+ } else if (sortBy == SortOrder.AMOUNT) {
+ this.view.sort(GasSorters.CONFIG_BASED_SORT_BY_SIZE);
+ } else {
+ this.view.sort(GasSorters.CONFIG_BASED_SORT_BY_NAME);
+ }
+ }
+
+ public void postUpdate(final IAEGasStack is) {
+ final IAEGasStack st = this.list.findPrecise(is);
+ if (st != null) {
+ st.reset();
+ st.add(is);
+ } else {
+ this.list.add(is);
+ }
+ }
+
+ public IAEGasStack getReferenceGas(int idx) {
+ idx += this.src.getCurrentScroll() * this.rowSize;
+ if (idx >= this.view.size()) {
+ return null;
+ }
+ return this.view.get(idx);
+ }
+
+ public int size() {
+ return this.view.size();
+ }
+
+ public void clear() {
+ this.list.resetStatus();
+ }
+
+ public boolean hasPower() {
+ return this.hasPower;
+ }
+
+ public void setPower(final boolean hasPower) {
+ this.hasPower = hasPower;
+ }
+
+ public int getRowSize() {
+ return this.rowSize;
+ }
+
+ public void setRowSize(final int rowSize) {
+ this.rowSize = rowSize;
+ }
+
+ public String getSearchString() {
+ return this.searchString;
+ }
+
+ public void setSearchString(@Nonnull final String searchString) {
+ this.searchString = searchString;
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/me/client/RepoSlot.java b/src/main/java/com/mekeng/github/common/me/client/RepoSlot.java
new file mode 100644
index 0000000..73a0628
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/client/RepoSlot.java
@@ -0,0 +1,35 @@
+package com.mekeng.github.common.me.client;
+
+import com.mekeng.github.common.me.data.IAEGasStack;
+
+public class RepoSlot {
+
+ private final int offset;
+ private final int xPos;
+ private final int yPos;
+ private final GasRepo repo;
+
+ public RepoSlot(GasRepo def, int offset, int displayX, int displayY) {
+ this.repo = def;
+ this.offset = offset;
+ this.xPos = displayX;
+ this.yPos = displayY;
+ }
+
+ public IAEGasStack getAEStack() {
+ return this.repo.getReferenceGas(this.offset);
+ }
+
+ public boolean hasPower() {
+ return this.repo.hasPower();
+ }
+
+ public int getX() {
+ return this.xPos;
+ }
+
+ public int getY() {
+ return this.yPos;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/data/IAEGasStack.java b/src/main/java/com/mekeng/github/common/me/data/IAEGasStack.java
new file mode 100644
index 0000000..5fd759c
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/data/IAEGasStack.java
@@ -0,0 +1,13 @@
+package com.mekeng.github.common.me.data;
+
+import appeng.api.storage.data.IAEStack;
+import mekanism.api.gas.Gas;
+import mekanism.api.gas.GasStack;
+
+public interface IAEGasStack extends IAEStack {
+
+ GasStack getGasStack();
+
+ Gas getGas();
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/data/impl/AEGasStack.java b/src/main/java/com/mekeng/github/common/me/data/impl/AEGasStack.java
new file mode 100644
index 0000000..0ca03c2
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/data/impl/AEGasStack.java
@@ -0,0 +1,177 @@
+package com.mekeng.github.common.me.data.impl;
+
+import appeng.api.AEApi;
+import appeng.api.config.FuzzyMode;
+import appeng.api.storage.IStorageChannel;
+import appeng.util.item.AEStack;
+import com.google.common.primitives.Ints;
+import com.mekeng.github.common.ItemAndBlocks;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import io.netty.buffer.ByteBuf;
+import mekanism.api.gas.Gas;
+import mekanism.api.gas.GasRegistry;
+import mekanism.api.gas.GasStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public final class AEGasStack extends AEStack implements IAEGasStack, Comparable {
+
+ private final Gas gas;
+
+ private AEGasStack(@Nonnull Gas gas, long amt) {
+ this.gas = gas;
+ this.setStackSize(amt);
+ this.setCraftable(false);
+ this.setCountRequestable(0L);
+ }
+
+ private AEGasStack(GasStack gasStack) {
+ this(gasStack.getGas(), gasStack.amount);
+ }
+
+ private AEGasStack(AEGasStack gasStack) {
+ this.gas = gasStack.gas;
+ this.setStackSize(gasStack.getStackSize());
+ this.setCraftable(gasStack.isCraftable());
+ this.setCountRequestable(gasStack.getCountRequestable());
+ }
+
+ @Nullable
+ public static AEGasStack of(GasStack input) {
+ return input == null || input.getGas() == null ? null : new AEGasStack(input);
+ }
+
+ @Nullable
+ public static IAEGasStack of(NBTTagCompound data) {
+ GasStack gasStack = GasStack.readFromNBT(data);
+ if (gasStack == null) {
+ return null;
+ } else {
+ return of(gasStack)
+ .setStackSize(data.getLong("Cnt"))
+ .setCountRequestable(data.getLong("Req"))
+ .setCraftable(data.getBoolean("Craft"));
+ }
+ }
+
+ @Nullable
+ public static IAEGasStack of(ByteBuf buffer) {
+ Gas gas = GasRegistry.getGas(buffer.readShort());
+ long amt = buffer.readLong();
+ if (gas != null) {
+ return new AEGasStack(gas, amt)
+ .setCountRequestable(buffer.readLong())
+ .setCraftable(buffer.readBoolean());
+ }
+ return null;
+ }
+
+ @Override
+ public GasStack getGasStack() {
+ return new GasStack(this.gas, Ints.saturatedCast(this.getStackSize()));
+ }
+
+ @Override
+ public Gas getGas() {
+ return this.gas;
+ }
+
+ @Override
+ public void add(IAEGasStack option) {
+ if (option != null) {
+ this.incStackSize(option.getStackSize());
+ this.setCountRequestable(this.getCountRequestable() + option.getCountRequestable())
+ .setCraftable(this.isCraftable() || option.isCraftable());
+ }
+ }
+
+ @Override
+ protected boolean hasTagCompound() {
+ return false;
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound nbt) {
+ GasStack stack = this.getGasStack();
+ stack.write(nbt);
+ nbt.setLong("Cnt", this.getStackSize());
+ nbt.setLong("Req", this.getCountRequestable());
+ nbt.setBoolean("Craft", this.isCraftable());
+ }
+
+ @Override
+ public boolean fuzzyComparison(IAEGasStack gasStack, FuzzyMode fuzzyMode) {
+ return this.gas == gasStack.getGas();
+ }
+
+ @Override
+ public void writeToPacket(ByteBuf buffer) {
+ buffer.writeShort(this.gas.getID());
+ buffer.writeLong(this.getStackSize());
+ buffer.writeLong(this.getCountRequestable());
+ buffer.writeBoolean(this.isCraftable());
+ }
+
+ @Override
+ public IAEGasStack copy() {
+ return new AEGasStack(this);
+ }
+
+ @Override
+ public boolean isItem() {
+ return false;
+ }
+
+ @Override
+ public boolean isFluid() {
+ return false;
+ }
+
+ @Override
+ public IStorageChannel getChannel() {
+ return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public ItemStack asItemStackRepresentation() {
+ ItemStack stack = new ItemStack(ItemAndBlocks.DUMMY_GAS);
+ ItemAndBlocks.DUMMY_GAS.setGasStack(stack, this.getGasStack());
+ return stack;
+ }
+
+ @Override
+ public int compareTo(AEGasStack o) {
+ if (o.gas != this.gas) {
+ return o.gas.getName().compareTo(this.gas.getName());
+ }
+ return 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.gas.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ if (other instanceof IAEGasStack) {
+ return ((IAEGasStack) other).getGas() == this.gas;
+ } else if (other instanceof GasStack) {
+ return ((GasStack) other).getGas() == this.gas;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return this.getStackSize() + "x" + this.gas.getName();
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/duality/IGasInterfaceHost.java b/src/main/java/com/mekeng/github/common/me/duality/IGasInterfaceHost.java
new file mode 100644
index 0000000..bf788e0
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/duality/IGasInterfaceHost.java
@@ -0,0 +1,22 @@
+package com.mekeng.github.common.me.duality;
+
+import appeng.api.implementations.IUpgradeableHost;
+import appeng.api.networking.security.IActionHost;
+import appeng.me.helpers.IGridProxyable;
+import com.mekeng.github.common.me.duality.impl.DualityGasInterface;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+
+import java.util.EnumSet;
+
+public interface IGasInterfaceHost extends IActionHost, IGridProxyable, IUpgradeableHost {
+
+ DualityGasInterface getDualityGasInterface();
+
+ EnumSet getTargets();
+
+ TileEntity getTileEntity();
+
+ void saveChanges();
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/duality/impl/DualityGasInterface.java b/src/main/java/com/mekeng/github/common/me/duality/impl/DualityGasInterface.java
new file mode 100644
index 0000000..c891089
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/duality/impl/DualityGasInterface.java
@@ -0,0 +1,654 @@
+package com.mekeng.github.common.me.duality.impl;
+
+import appeng.api.AEApi;
+import appeng.api.config.Actionable;
+import appeng.api.config.Settings;
+import appeng.api.config.Upgrades;
+import appeng.api.config.YesNo;
+import appeng.api.implementations.IUpgradeableHost;
+import appeng.api.implementations.tiles.ICraftingMachine;
+import appeng.api.networking.GridFlags;
+import appeng.api.networking.IGrid;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.energy.IEnergySource;
+import appeng.api.networking.security.IActionHost;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.ticking.IGridTickable;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.networking.ticking.TickingRequest;
+import appeng.api.storage.IMEInventory;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.IStorageMonitorable;
+import appeng.api.storage.IStorageMonitorableAccessor;
+import appeng.api.storage.channels.IFluidStorageChannel;
+import appeng.api.storage.channels.IItemStorageChannel;
+import appeng.api.storage.data.IAEFluidStack;
+import appeng.api.storage.data.IAEItemStack;
+import appeng.api.storage.data.IAEStack;
+import appeng.api.util.AECableType;
+import appeng.api.util.AEPartLocation;
+import appeng.api.util.DimensionalCoord;
+import appeng.api.util.IConfigManager;
+import appeng.capabilities.Capabilities;
+import appeng.core.settings.TickRates;
+import appeng.helpers.ICustomNameObject;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.AENetworkProxy;
+import appeng.me.helpers.MachineSource;
+import appeng.me.storage.MEMonitorPassThrough;
+import appeng.me.storage.NullInventory;
+import appeng.parts.automation.StackUpgradeInventory;
+import appeng.parts.automation.UpgradeInventory;
+import appeng.util.ConfigManager;
+import appeng.util.IConfigManagerHost;
+import appeng.util.InventoryAdaptor;
+import appeng.util.Platform;
+import appeng.util.inv.IAEAppEngInventory;
+import appeng.util.inv.InvOperation;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.inventory.IConfigurableGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventoryHost;
+import com.mekeng.github.common.me.inventory.impl.GasInvHandler;
+import com.mekeng.github.common.me.inventory.impl.GasInventory;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.common.me.storage.impl.MEMonitorIGasHandler;
+import gregtech.api.block.machines.BlockMachine;
+import gregtech.api.metatileentity.MetaTileEntity;
+import mekanism.api.gas.GasStack;
+import net.minecraft.block.Block;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.RayTraceResult;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.items.IItemHandler;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+
+public class DualityGasInterface implements IGridTickable, IStorageMonitorable, IAEAppEngInventory, IUpgradeableHost, IConfigManagerHost, IConfigurableGasInventory, IGasInventoryHost {
+ public static final int NUMBER_OF_TANKS = 9;
+ public static final int TANK_CAPACITY = 1000 * 4;
+ private static final Collection BAD_BLOCKS = new HashSet<>(100);
+ private final ConfigManager cm = new ConfigManager(this);
+ private final AENetworkProxy gridProxy;
+ private final IGasInterfaceHost iHost;
+ private final IActionSource interfaceRequestSource;
+ private final UpgradeInventory upgrades;
+ private boolean hasConfig = false;
+ private final IStorageMonitorableAccessor accessor = this::getMonitorable;
+ private final GasInventory tanks = new GasInventory(NUMBER_OF_TANKS, TANK_CAPACITY, this);
+ private final GasInvHandler handler;
+ private final GasInventory config = new GasInventory(NUMBER_OF_TANKS, this);
+ private final IAEGasStack[] requireWork;
+ private int isWorking = -1;
+ private int priority;
+
+ private final MEMonitorPassThrough items = new MEMonitorPassThrough<>(new NullInventory(), AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class));
+ private final MEMonitorPassThrough fluids = new MEMonitorPassThrough<>(new NullInventory(), AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class));
+ private final MEMonitorPassThrough gases = new MEMonitorPassThrough<>(new NullInventory(), AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class));
+ private boolean resetConfigCache = true;
+ private IMEMonitor configCachedHandler;
+
+ public DualityGasInterface(final AENetworkProxy networkProxy, final IGasInterfaceHost ih) {
+ this.gridProxy = networkProxy;
+ this.gridProxy.setFlags(GridFlags.REQUIRE_CHANNEL);
+
+ this.upgrades = new StackUpgradeInventory(this.gridProxy.getMachineRepresentation(), this, 2);
+ this.cm.registerSetting(Settings.INTERFACE_TERMINAL, YesNo.YES);
+
+ this.iHost = ih;
+
+ IActionSource mySource = new MachineSource(this.iHost);
+ this.interfaceRequestSource = new InterfaceRequestSource(this.iHost);
+
+ this.fluids.setChangeSource(mySource);
+ this.items.setChangeSource(mySource);
+ this.gases.setChangeSource(mySource);
+
+ this.requireWork = new IAEGasStack[NUMBER_OF_TANKS];
+ for (int i = 0; i < NUMBER_OF_TANKS; ++i) {
+ this.requireWork[i] = null;
+ }
+ this.handler = new GasInvHandler(this.tanks);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public > IMEMonitor getInventory(IStorageChannel channel) {
+ if (channel == AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)) {
+ if (this.hasConfig()) {
+ return null;
+ }
+ return (IMEMonitor) this.items;
+ } else if (channel == AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)) {
+ if (this.hasConfig()) {
+ return null;
+ }
+ return (IMEMonitor) this.fluids;
+ } else if (channel == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)) {
+ if (this.hasConfig()) {
+ if (resetConfigCache) {
+ resetConfigCache = false;
+ configCachedHandler = new InterfaceInventory(this);
+ }
+ return (IMEMonitor) configCachedHandler;
+ }
+ return (IMEMonitor) this.gases;
+ }
+ return null;
+ }
+
+ public IStorageMonitorable getMonitorable(final IActionSource src) {
+ if (Platform.canAccess(this.gridProxy, src)) {
+ return this;
+ }
+ return null;
+ }
+
+ @Nonnull
+ @Override
+ public TickingRequest getTickingRequest(@Nonnull final IGridNode node) {
+ return new TickingRequest(TickRates.Interface.getMin(), TickRates.Interface.getMax(), !this.hasWorkToDo(), true);
+ }
+
+ @Nonnull
+ @Override
+ public TickRateModulation tickingRequest(@Nonnull final IGridNode node, final int ticksSinceLastCall) {
+ if (!this.gridProxy.isActive()) {
+ return TickRateModulation.SLEEP;
+ }
+ final boolean couldDoWork = this.updateStorage();
+ return this.hasWorkToDo() ? (couldDoWork ? TickRateModulation.URGENT : TickRateModulation.SLOWER) : TickRateModulation.SLEEP;
+ }
+
+ public void notifyNeighbors() {
+ if (this.gridProxy.isActive()) {
+ try {
+ this.gridProxy.getTick().wakeDevice(this.gridProxy.getNode());
+ } catch (final GridAccessException e) {
+ // :P
+ }
+ }
+ final TileEntity te = this.iHost.getTileEntity();
+ if (te != null && te.getWorld() != null) {
+ Platform.notifyBlocksOfNeighbors(te.getWorld(), te.getPos());
+ }
+ }
+
+ public void gridChanged() {
+ try {
+ this.items.setInternal(this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IItemStorageChannel.class)));
+ this.fluids.setInternal(this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IFluidStorageChannel.class)));
+ this.gases.setInternal(this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)));
+ } catch (final GridAccessException gae) {
+ this.items.setInternal(new NullInventory<>());
+ this.fluids.setInternal(new NullInventory<>());
+ this.gases.setInternal(new NullInventory<>());
+ }
+ this.notifyNeighbors();
+ }
+
+ public AECableType getCableConnectionType() {
+ return AECableType.SMART;
+ }
+
+ public DimensionalCoord getLocation() {
+ return new DimensionalCoord(this.iHost.getTileEntity());
+ }
+
+ private boolean sameGrid(final IGrid grid) throws GridAccessException {
+ return grid == this.gridProxy.getGrid();
+ }
+
+ public String getTermName() {
+ final TileEntity hostTile = this.iHost.getTileEntity();
+ final World hostWorld = hostTile.getWorld();
+
+ if (((ICustomNameObject) this.iHost).hasCustomInventoryName()) {
+ return ((ICustomNameObject) this.iHost).getCustomInventoryName();
+ }
+
+ final EnumSet possibleDirections = this.iHost.getTargets();
+ for (final EnumFacing direction : possibleDirections) {
+ final BlockPos targ = hostTile.getPos().offset(direction);
+ final TileEntity directedTile = hostWorld.getTileEntity(targ);
+
+ if (directedTile == null) {
+ continue;
+ }
+
+ if (directedTile instanceof IGasInterfaceHost) {
+ try {
+ if (((IGasInterfaceHost) directedTile).getDualityGasInterface().sameGrid(this.gridProxy.getGrid())) {
+ continue;
+ }
+ } catch (final GridAccessException e) {
+ continue;
+ }
+ }
+
+ final InventoryAdaptor adaptor = InventoryAdaptor.getAdaptor(directedTile, direction.getOpposite());
+ if (directedTile instanceof ICraftingMachine || adaptor != null) {
+ if (adaptor != null && !adaptor.hasSlots()) {
+ continue;
+ }
+
+ final IBlockState directedBlockState = hostWorld.getBlockState(targ);
+ final Block directedBlock = directedBlockState.getBlock();
+ ItemStack what = new ItemStack(directedBlock, 1, directedBlock.getMetaFromState(directedBlockState));
+
+ if (Platform.GTLoaded && directedBlock instanceof BlockMachine) {
+ MetaTileEntity metaTileEntity = Platform.getMetaTileEntity(directedTile.getWorld(), directedTile.getPos());
+ if (metaTileEntity != null) {
+ return metaTileEntity.getMetaFullName();
+ }
+ }
+
+ try {
+ Vec3d from = new Vec3d(hostTile.getPos().getX() + 0.5, hostTile.getPos().getY() + 0.5, hostTile.getPos().getZ() + 0.5);
+ from = from.add(direction.getXOffset() * 0.501, direction.getYOffset() * 0.501, direction.getZOffset() * 0.501);
+ final Vec3d to = from.add(direction.getXOffset(), direction.getYOffset(), direction.getZOffset());
+ final RayTraceResult mop = hostWorld.rayTraceBlocks(from, to, true);
+ if (mop != null && !BAD_BLOCKS.contains(directedBlock)) {
+ if (mop.getBlockPos().equals(directedTile.getPos())) {
+ final ItemStack g = directedBlock.getPickBlock(directedBlockState, mop, hostWorld, directedTile.getPos(), null);
+ if (!g.isEmpty()) {
+ what = g;
+ }
+ }
+ }
+ } catch (final Throwable t) {
+ BAD_BLOCKS.add(directedBlock); // nope!
+ }
+
+ if (what.getItem() != Items.AIR) {
+ return what.getItem().getItemStackDisplayName(what);
+ }
+
+ final Item item = Item.getItemFromBlock(directedBlock);
+ if (item == Items.AIR) {
+ return directedBlock.getTranslationKey();
+ }
+ }
+ }
+
+ return "Nothing";
+ }
+
+ public long getSortValue() {
+ final TileEntity te = this.iHost.getTileEntity();
+ return ((long) te.getPos().getZ() << 24L) ^ ((long) te.getPos().getX() << 8L) ^ te.getPos().getY();
+ }
+
+ public boolean hasCapability(Capability> capabilityClass, EnumFacing facing) {
+ return capabilityClass == mekanism.common.capabilities.Capabilities.GAS_HANDLER_CAPABILITY || capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getCapability(Capability capabilityClass, EnumFacing facing) {
+ if (capabilityClass == mekanism.common.capabilities.Capabilities.GAS_HANDLER_CAPABILITY) {
+ return (T) this.handler;
+ } else if (capabilityClass == Capabilities.STORAGE_MONITORABLE_ACCESSOR) {
+ return (T) this.accessor;
+ }
+ return null;
+ }
+
+ public GasInvHandler getTankHandler() {
+ return this.handler;
+ }
+
+ private boolean hasConfig() {
+ return this.hasConfig;
+ }
+
+ private void readConfig() {
+ this.hasConfig = false;
+ for (int i = 0; i < this.config.size(); i++) {
+ if (this.config.getGasStack(i) != null) {
+ this.hasConfig = true;
+ break;
+ }
+ }
+
+ final boolean had = this.hasWorkToDo();
+
+ for (int x = 0; x < NUMBER_OF_TANKS; x++) {
+ this.updatePlan(x);
+ }
+
+ final boolean has = this.hasWorkToDo();
+
+ if (had != has) {
+ try {
+ if (has) {
+ this.gridProxy.getTick().alertDevice(this.gridProxy.getNode());
+ } else {
+ this.gridProxy.getTick().sleepDevice(this.gridProxy.getNode());
+ }
+ } catch (final GridAccessException e) {
+ // :P
+ }
+ }
+
+ this.notifyNeighbors();
+ }
+
+ private boolean updateStorage() {
+ boolean didSomething = false;
+ for (int x = 0; x < NUMBER_OF_TANKS; x++) {
+ if (this.requireWork[x] != null) {
+ didSomething = this.usePlan(x) || didSomething;
+ }
+ }
+ return didSomething;
+ }
+
+ private boolean hasWorkToDo() {
+ for (final IAEGasStack requiredWork : this.requireWork) {
+ if (requiredWork != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updatePlan(final int slot) {
+ final IAEGasStack req = AEGasStack.of(this.config.getGasStack(slot));
+ final IAEGasStack stored = AEGasStack.of(this.tanks.getGasStack(slot));
+
+ if (req == null && (stored != null && stored.getStackSize() > 0)) {
+ final IAEGasStack work = stored.copy();
+ this.requireWork[slot] = work.setStackSize(-work.getStackSize());
+ return;
+ } else if (req != null) {
+ int tankSize = (int) (Math.pow(4, this.getInstalledUpgrades(Upgrades.CAPACITY) + 1) * 1000);
+ if (stored == null || stored.getStackSize() == 0) // need to add stuff!
+ {
+ this.requireWork[slot] = req.copy();
+ this.requireWork[slot].setStackSize(tankSize);
+ return;
+ } else if (req.equals(stored)) // same type ( qty different? )!
+ {
+ if (stored.getStackSize() != tankSize) {
+ this.requireWork[slot] = req.copy();
+ this.requireWork[slot].setStackSize(tankSize - stored.getStackSize());
+ return;
+ }
+ } else
+ // Stored != null; dispose!
+ {
+ final IAEGasStack work = stored.copy();
+ this.requireWork[slot] = work.setStackSize(-work.getStackSize());
+ return;
+ }
+ }
+
+ this.requireWork[slot] = null;
+ }
+
+ private boolean usePlan(final int slot) {
+ IAEGasStack work = this.requireWork[slot];
+ this.isWorking = slot;
+
+ boolean changed = false;
+ try {
+ final IMEInventory dest = this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class));
+ final IEnergySource src = this.gridProxy.getEnergy();
+
+ if (work.getStackSize() > 0) {
+ // make sure strange things didn't happen...
+ if (this.tanks.addGas(slot, work.getGasStack(), true) != work.getStackSize()) {
+ changed = true;
+ } else if (this.gridProxy.getStorage().getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)).getStorageList().findPrecise(work) != null) {
+ final IAEGasStack acquired = Platform.poweredExtraction(src, dest, work, this.interfaceRequestSource);
+ if (acquired != null) {
+ changed = true;
+ final int filled = this.tanks.addGas(slot, acquired.getGasStack(), false);
+ if (filled != acquired.getStackSize()) {
+ throw new IllegalStateException("bad attempt at managing tanks. ( fill )");
+ }
+ }
+ }
+ } else if (work.getStackSize() < 0) {
+ IAEGasStack toStore = work.copy();
+ toStore.setStackSize(-toStore.getStackSize());
+
+ // make sure strange things didn't happen...
+ final GasStack canExtract = this.tanks.removeGas(slot, toStore.getGasStack(), true);
+ if (canExtract == null || canExtract.amount != toStore.getStackSize()) {
+ changed = true;
+ } else {
+ IAEGasStack notStored = Platform.poweredInsert(src, dest, toStore, this.interfaceRequestSource);
+ toStore.setStackSize(toStore.getStackSize() - (notStored == null ? 0 : notStored.getStackSize()));
+
+ if (toStore.getStackSize() > 0) {
+ // extract items!
+ changed = true;
+ final GasStack removed = this.tanks.removeGas(slot, toStore.getGasStack(), false);
+ if (removed == null || toStore.getStackSize() != removed.amount) {
+ throw new IllegalStateException("bad attempt at managing tanks. ( drain )");
+ }
+ }
+ }
+ }
+ } catch (final GridAccessException e) {
+ // :P
+ }
+
+ if (changed) {
+ this.updatePlan(slot);
+ }
+
+ this.isWorking = -1;
+ return changed;
+ }
+
+ @Override
+ public void onGasInventoryChanged(final IGasInventory inventory, final int slot) {
+ if (this.isWorking == slot) {
+ return;
+ }
+
+ if (inventory == this.config) {
+ boolean cfg = hasConfig();
+ this.readConfig();
+ if (cfg != hasConfig) {
+ resetConfigCache = true;
+ this.notifyNeighbors();
+ }
+ } else if (inventory == this.tanks) {
+ this.saveChanges();
+
+ final boolean had = this.hasWorkToDo();
+
+ this.updatePlan(slot);
+
+ final boolean now = this.hasWorkToDo();
+
+ if (had != now) {
+ try {
+ if (now) {
+ this.gridProxy.getTick().alertDevice(this.gridProxy.getNode());
+ } else {
+ this.gridProxy.getTick().sleepDevice(this.gridProxy.getNode());
+ }
+ } catch (final GridAccessException e) {
+ // :P
+ }
+ }
+ }
+ }
+
+ public int getPriority() {
+ return this.priority;
+ }
+
+ public void setPriority(final int newValue) {
+ this.priority = newValue;
+ }
+
+ public void writeToNBT(final NBTTagCompound data) {
+ data.setInteger("priority", this.priority);
+ data.setTag("storage", this.tanks.save());
+ data.setTag("config", this.config.save());
+ this.upgrades.writeToNBT(data, "upgrades");
+ }
+
+ public void readFromNBT(final NBTTagCompound data) {
+ this.config.load(data.getCompoundTag("config"));
+ this.tanks.load(data.getCompoundTag("storage"));
+ this.priority = data.getInteger("priority");
+ this.upgrades.readFromNBT(data, "upgrades");
+ this.tanks.setCap((int) (Math.pow(4, this.getInstalledUpgrades(Upgrades.CAPACITY) + 1) * 1000));
+ this.readConfig();
+ }
+
+ public IGasInventory getConfig() {
+ return this.config;
+ }
+
+ public IGasInventory getTanks() {
+ return this.tanks;
+ }
+
+ private class InterfaceRequestSource extends MachineSource {
+ private final InterfaceRequestContext context;
+
+ InterfaceRequestSource(IActionHost v) {
+ super(v);
+ this.context = new InterfaceRequestContext();
+ }
+
+ @Nonnull
+ @SuppressWarnings("unchecked")
+ @Override
+ public Optional context(Class key) {
+ if (key == InterfaceRequestContext.class) {
+ return (Optional) Optional.of(this.context);
+ }
+ return super.context(key);
+ }
+ }
+
+ private class InterfaceRequestContext implements Comparable {
+
+ @Override
+ public int compareTo(@Nonnull Integer o) {
+ return Integer.compare(DualityGasInterface.this.priority, o);
+ }
+
+ }
+
+ private class InterfaceInventory extends MEMonitorIGasHandler {
+
+ InterfaceInventory(final DualityGasInterface tileInterface) {
+ super(new GasInvHandler(tileInterface.tanks), null);
+ }
+
+ @Override
+ public IAEGasStack injectItems(final IAEGasStack input, final Actionable type, final IActionSource src) {
+ final Optional context = src.context(InterfaceRequestContext.class);
+ final boolean isInterface = context.isPresent();
+
+ if (isInterface) {
+ return input;
+ }
+
+ return super.injectItems(input, type, src);
+ }
+
+ @Override
+ public IAEGasStack extractItems(final IAEGasStack request, final Actionable type, final IActionSource src) {
+ final Optional context = src.context(InterfaceRequestContext.class);
+ final boolean hasLowerOrEqualPriority = context.map(c -> c.compareTo(DualityGasInterface.this.priority) <= 0).orElse(false);
+
+ if (hasLowerOrEqualPriority) {
+ return null;
+ }
+
+ return super.extractItems(request, type, src);
+ }
+ }
+
+ public void saveChanges() {
+ this.iHost.saveChanges();
+ }
+
+ @Override
+ public void onChangeInventory(IItemHandler inv, int slot, InvOperation mc, ItemStack removedStack, ItemStack newStack) {
+ if (inv == this.upgrades) {
+ this.tanks.setCap((int) (Math.pow(4, this.getInstalledUpgrades(Upgrades.CAPACITY) + 1) * 1000));
+ try {
+ this.gridProxy.getTick().alertDevice(this.gridProxy.getNode());
+ } catch (GridAccessException ignored) {
+ }
+ for (int x = 0; x < NUMBER_OF_TANKS; x++) {
+ this.updatePlan(x);
+ }
+ }
+ }
+
+ @Override
+ public IConfigManager getConfigManager() {
+ return this.cm;
+ }
+
+ @Override
+ public IItemHandler getInventoryByName(String name) {
+ if (name.equals("upgrades")) {
+ return this.upgrades;
+ }
+ return null;
+ }
+
+ @Override
+ public IGasInventory getGasInventoryByName(final String name) {
+ if (name.equals("config")) {
+ return this.config;
+ }
+ return null;
+ }
+
+ @Override
+ public int getInstalledUpgrades(Upgrades u) {
+ if (this.upgrades == null) {
+ return 0;
+ }
+ return this.upgrades.getInstalledUpgrades(u);
+ }
+
+ public void addDrops(final List drops) {
+ for (final ItemStack is : this.upgrades) {
+ if (!is.isEmpty()) {
+ drops.add(is);
+ }
+ }
+ }
+
+ @Override
+ public TileEntity getTile() {
+ return (TileEntity) (this.iHost instanceof TileEntity ? this.iHost : null);
+ }
+
+ @Override
+ public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/IConfigurableGasInventory.java b/src/main/java/com/mekeng/github/common/me/inventory/IConfigurableGasInventory.java
new file mode 100644
index 0000000..7642fe3
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/IConfigurableGasInventory.java
@@ -0,0 +1,9 @@
+package com.mekeng.github.common.me.inventory;
+
+public interface IConfigurableGasInventory {
+
+ default IGasInventory getGasInventoryByName(String name) {
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/IExtendedGasHandler.java b/src/main/java/com/mekeng/github/common/me/inventory/IExtendedGasHandler.java
new file mode 100644
index 0000000..8a098f0
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/IExtendedGasHandler.java
@@ -0,0 +1,11 @@
+package com.mekeng.github.common.me.inventory;
+
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.IGasHandler;
+import net.minecraft.util.EnumFacing;
+
+public interface IExtendedGasHandler extends IGasHandler {
+
+ GasStack drawGas(EnumFacing side, GasStack stack, boolean doTransfer);
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/IGasInventory.java b/src/main/java/com/mekeng/github/common/me/inventory/IGasInventory.java
new file mode 100644
index 0000000..1e5fc34
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/IGasInventory.java
@@ -0,0 +1,29 @@
+package com.mekeng.github.common.me.inventory;
+
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.GasTank;
+
+import javax.annotation.Nullable;
+
+public interface IGasInventory extends Iterable {
+
+ int size();
+
+ boolean usable(int index);
+
+ GasTank[] getTanks();
+
+ @Nullable
+ GasStack getGasStack(int index);
+
+ int addGas(int index, GasStack stack, boolean simulate);
+
+ GasStack removeGas(int index, GasStack stack, boolean simulate);
+
+ GasStack removeGas(int index, int amount, boolean simulate);
+
+ void setGas(int index, GasStack stack);
+
+ void setCap(int cap);
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/IGasInventoryHost.java b/src/main/java/com/mekeng/github/common/me/inventory/IGasInventoryHost.java
new file mode 100644
index 0000000..37267ee
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/IGasInventoryHost.java
@@ -0,0 +1,23 @@
+package com.mekeng.github.common.me.inventory;
+
+public interface IGasInventoryHost {
+
+ void onGasInventoryChanged(IGasInventory inv, int slot);
+
+ static IGasInventoryHost empty() {
+ return new EmptyHost();
+ }
+
+ final class EmptyHost implements IGasInventoryHost {
+
+ private EmptyHost() {
+ // NO-OP
+ }
+
+ @Override
+ public void onGasInventoryChanged(IGasInventory inv, int slot) {
+
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/impl/GasHandlerAdapter.java b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasHandlerAdapter.java
new file mode 100644
index 0000000..3a11c87
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasHandlerAdapter.java
@@ -0,0 +1,222 @@
+package com.mekeng.github.common.me.inventory.impl;
+
+import appeng.api.AEApi;
+import appeng.api.config.AccessRestriction;
+import appeng.api.config.Actionable;
+import appeng.api.config.Settings;
+import appeng.api.config.StorageFilter;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.storage.IBaseMonitor;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.storage.IMEInventory;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.data.IItemList;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.IGridProxyable;
+import appeng.me.storage.ITickingMonitor;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.common.part.PartGasStorageBus;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.GasTankInfo;
+import mekanism.api.gas.IGasHandler;
+import net.minecraft.util.EnumFacing;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class GasHandlerAdapter implements IMEInventory, IBaseMonitor, ITickingMonitor {
+ private final Map, Object> listeners = new HashMap<>();
+ private IActionSource source;
+ private final IGasHandler gasHandler;
+ private final IGridProxyable proxyable;
+ private final InventoryCache cache;
+ private StorageFilter mode;
+ private AccessRestriction access;
+ private final EnumFacing face;
+
+ public GasHandlerAdapter(IGasHandler gasHandler, IGridProxyable proxy, EnumFacing face) {
+ this.gasHandler = gasHandler;
+ this.proxyable = proxy;
+ this.face = face;
+ if (this.proxyable instanceof PartGasStorageBus) {
+ PartGasStorageBus partGasStorageBus = (PartGasStorageBus) this.proxyable;
+ this.mode = ((StorageFilter) partGasStorageBus.getConfigManager().getSetting(Settings.STORAGE_FILTER));
+ this.access = ((AccessRestriction) partGasStorageBus.getConfigManager().getSetting(Settings.ACCESS));
+ }
+ this.cache = new InventoryCache(this.gasHandler, this.mode, this.face);
+ this.cache.update();
+ }
+
+ @Override
+ public IAEGasStack injectItems(IAEGasStack input, Actionable type, IActionSource src) {
+ GasStack gasStack = input.getGasStack();
+
+ if (!this.gasHandler.canReceiveGas(this.face, gasStack.getGas())) {
+ return input;
+ }
+
+ // Insert
+ int wasFillled = this.gasHandler.receiveGas(this.face, gasStack, type != Actionable.SIMULATE);
+ int remaining = gasStack.amount - wasFillled;
+ if (gasStack.amount == remaining) {
+ // The stack was unmodified, target tank is full
+ return input;
+ }
+
+ if (type == Actionable.MODULATE) {
+ IAEGasStack added = input.copy().setStackSize(input.getStackSize() - remaining);
+ this.cache.currentlyCached.add(added);
+ this.postDifference(Collections.singletonList(added));
+ try {
+ this.proxyable.getProxy().getTick().alertDevice(this.proxyable.getProxy().getNode());
+ } catch (GridAccessException ex) {
+ // meh
+ }
+ }
+
+ gasStack.amount = remaining;
+
+ return AEGasStack.of(gasStack);
+ }
+
+ @Override
+ public IAEGasStack extractItems(IAEGasStack request, Actionable mode, IActionSource src) {
+ GasStack requestedGasStack = request.getGasStack();
+
+ if (!this.gasHandler.canDrawGas(this.face, requestedGasStack.getGas())) {
+ return null;
+ }
+
+ final boolean doDrain = (mode == Actionable.MODULATE);
+
+ // Drain the gas from the tank
+ GasStack gathered = Utils.drawGas(this.gasHandler, requestedGasStack, this.face, requestedGasStack.amount, doDrain);
+ if (gathered == null) {
+ // If nothing was pulled from the tank, return null
+ return null;
+ }
+
+ IAEGasStack gatheredAEGasStack = AEGasStack.of(gathered);
+ if (mode == Actionable.MODULATE) {
+ IAEGasStack cachedStack = this.cache.currentlyCached.findPrecise(request);
+ if (cachedStack != null) {
+ cachedStack.decStackSize(gatheredAEGasStack.getStackSize());
+ this.postDifference(Collections.singletonList(gatheredAEGasStack.copy().setStackSize(-gatheredAEGasStack.getStackSize())));
+ }
+ try {
+ this.proxyable.getProxy().getTick().alertDevice(this.proxyable.getProxy().getNode());
+ } catch (GridAccessException ex) {
+ // meh
+ }
+ }
+ return gatheredAEGasStack;
+ }
+
+ @Override
+ public TickRateModulation onTick() {
+ List changes = this.cache.update();
+ if (!changes.isEmpty() && access.hasPermission(AccessRestriction.READ)) {
+ this.postDifference(changes);
+ return TickRateModulation.URGENT;
+ } else {
+ return TickRateModulation.SLOWER;
+ }
+ }
+
+ @Override
+ public IItemList getAvailableItems(IItemList out) {
+ return this.cache.getAvailableItems(out);
+ }
+
+ @Override
+ public IStorageChannel getChannel() {
+ return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public void setActionSource(IActionSource source) {
+ this.source = source;
+ }
+
+ @Override
+ public void addListener(final IMEMonitorHandlerReceiver l, final Object verificationToken) {
+ this.listeners.put(l, verificationToken);
+ }
+
+ @Override
+ public void removeListener(final IMEMonitorHandlerReceiver l) {
+ this.listeners.remove(l);
+ }
+
+ private void postDifference(Iterable a) {
+ final Iterator, Object>> i = this.listeners.entrySet().iterator();
+ while (i.hasNext()) {
+ final Map.Entry, Object> l = i.next();
+ final IMEMonitorHandlerReceiver key = l.getKey();
+ if (key.isValid(l.getValue())) {
+ key.postChange(this, a, this.source);
+ } else {
+ i.remove();
+ }
+ }
+ }
+
+ private static class InventoryCache {
+ private final IGasHandler gasHandler;
+ private final StorageFilter mode;
+ private final EnumFacing face;
+ IItemList currentlyCached = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+
+ public InventoryCache(IGasHandler gasHandler, StorageFilter mode, EnumFacing face) {
+ this.mode = mode;
+ this.gasHandler = gasHandler;
+ this.face = face;
+ }
+
+ public List update() {
+ final List changes = new ArrayList<>();
+ final GasTankInfo[] tankProperties = this.gasHandler.getTankInfo();
+
+ IItemList currentlyOnStorage = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+
+ for (GasTankInfo tankProperty : tankProperties) {
+ if (this.mode == StorageFilter.EXTRACTABLE_ONLY && this.gasHandler.drawGas(this.face, 1, false) == null) {
+ continue;
+ }
+ currentlyOnStorage.add(AEGasStack.of(tankProperty.getGas()));
+ }
+
+ for (final IAEGasStack is : currentlyCached) {
+ is.setStackSize(-is.getStackSize());
+ }
+
+ for (final IAEGasStack is : currentlyOnStorage) {
+ currentlyCached.add(is);
+ }
+
+ for (final IAEGasStack is : currentlyCached) {
+ if (is.getStackSize() != 0) {
+ changes.add(is);
+ }
+ }
+
+ currentlyCached = currentlyOnStorage;
+
+ return changes;
+ }
+
+ public IItemList getAvailableItems(IItemList out) {
+ currentlyCached.iterator().forEachRemaining(out::add);
+ return out;
+ }
+
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInvHandler.java b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInvHandler.java
new file mode 100644
index 0000000..ec72c78
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInvHandler.java
@@ -0,0 +1,138 @@
+package com.mekeng.github.common.me.inventory.impl;
+
+import com.mekeng.github.common.me.inventory.IExtendedGasHandler;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import mekanism.api.gas.Gas;
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.GasTankInfo;
+import net.minecraft.util.EnumFacing;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.EnumSet;
+
+public class GasInvHandler implements IExtendedGasHandler {
+
+ private final IGasInventory inv;
+ private final EnumSet validSide;
+
+ public GasInvHandler(IGasInventory inv) {
+ this(inv, EnumSet.allOf(EnumFacing.class));
+ }
+
+ public GasInvHandler(IGasInventory inv, EnumSet faces) {
+ this.inv = inv;
+ this.validSide = faces;
+ }
+
+ public void setSide(Collection newSides) {
+ this.validSide.clear();
+ this.validSide.addAll(newSides);
+ }
+
+ @Override
+ public int receiveGas(EnumFacing side, GasStack stack, boolean doTransfer) {
+ if (this.checkSide(side) && stack != null) {
+ GasStack left = stack.copy();
+ for (int i = 0; i < this.inv.size(); i ++) {
+ if (left.amount <= 0) {
+ break;
+ }
+ left.amount -= this.inv.addGas(i, left, !doTransfer);
+ }
+ if (left.amount <= 0) {
+ return stack.amount;
+ }
+ return stack.amount - left.amount;
+ }
+ return 0;
+ }
+
+ @Override
+ public GasStack drawGas(EnumFacing side, int amount, boolean doTransfer) {
+ if (this.checkSide(side) && amount > 0) {
+ Gas type = null;
+ for (int i = 0; i < this.inv.size(); i ++) {
+ if (this.inv.getGasStack(i) != null) {
+ type = this.inv.getGasStack(i).getGas();
+ break;
+ }
+ }
+ if (type != null) {
+ GasStack toRemove = new GasStack(type, amount);
+ for (int i = 0; i < this.inv.size(); i ++) {
+ GasStack tmp = this.inv.removeGas(i, toRemove, !doTransfer);
+ if (tmp != null) {
+ toRemove.amount -= tmp.amount;
+ }
+ if (toRemove.amount <= 0) {
+ break;
+ }
+ }
+ if (amount - toRemove.amount <= 0) {
+ return null;
+ }
+ return new GasStack(type, amount - toRemove.amount);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public GasStack drawGas(EnumFacing side, GasStack stack, boolean doTransfer) {
+ if (this.checkSide(side) && stack != null && stack.amount > 0) {
+ GasStack toRemove = stack.copy();
+ int amount = stack.amount;
+ for (int i = 0; i < this.inv.size(); i ++) {
+ GasStack tmp = this.inv.removeGas(i, toRemove, !doTransfer);
+ if (tmp != null) {
+ toRemove.amount -= tmp.amount;
+ }
+ if (toRemove.amount <= 0) {
+ break;
+ }
+ }
+ if (amount - toRemove.amount <= 0) {
+ return null;
+ }
+ return new GasStack(stack.getGas(), amount - toRemove.amount);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean canReceiveGas(EnumFacing side, Gas type) {
+ if (this.checkSide(side)) {
+ for (int i = 0; i < this.inv.size(); i ++) {
+ if (this.inv.addGas(i, new GasStack(type, 1), true) > 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean canDrawGas(EnumFacing side, Gas type) {
+ if (this.checkSide(side)) {
+ for (int i = 0; i < this.inv.size(); i ++) {
+ if (this.inv.removeGas(i, new GasStack(type, 1), true) != null) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Nonnull
+ @Override
+ public GasTankInfo[] getTankInfo() {
+ return this.inv.getTanks();
+ }
+
+ public boolean checkSide(@Nullable EnumFacing side) {
+ return side == null || this.validSide.contains(side);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInventory.java b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInventory.java
new file mode 100644
index 0000000..be0609e
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/inventory/impl/GasInventory.java
@@ -0,0 +1,169 @@
+package com.mekeng.github.common.me.inventory.impl;
+
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventoryHost;
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.GasTank;
+import net.minecraft.nbt.NBTTagCompound;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.Iterator;
+
+public class GasInventory implements IGasInventory {
+
+ private final GasTank[] tanks;
+
+ public GasInventory(int size, int cap, @Nullable IGasInventoryHost host) {
+ final IGasInventoryHost h = host == null ? IGasInventoryHost.empty() : host;
+ this.tanks = new GasTank[size];
+ for (int i = 0; i < size; i ++) {
+ int index = i;
+ this.tanks[i] = new NotifiableGasTank(cap,() -> h.onGasInventoryChanged(this, index));
+ }
+ }
+
+ public GasInventory(int size, @Nullable IGasInventoryHost host) {
+ this(size, Integer.MAX_VALUE, host);
+ }
+
+ public GasInventory(int size, int cap) {
+ this(size, cap, null);
+ }
+
+ public GasInventory(int size) {
+ this(size, Integer.MAX_VALUE, null);
+ }
+
+ public NBTTagCompound save() {
+ NBTTagCompound data = new NBTTagCompound();
+ for (int i = 0; i < this.tanks.length; i ++) {
+ if (this.tanks[i] != null) {
+ data.setTag("#" + i, this.tanks[i].write(new NBTTagCompound()));
+ }
+ }
+ return data;
+ }
+
+ public void load(NBTTagCompound data) {
+ for (int i = 0; i < this.tanks.length; i ++) {
+ if (data.hasKey("#" + i, 10)) {
+ GasTank tank = GasTank.readFromNBT(data.getCompoundTag("#" + i));
+ if (tank != null) {
+ this.tanks[i].setMaxGas(tank.getMaxGas());
+ this.tanks[i].setGas(tank.getGas());
+ }
+ }
+ }
+ }
+
+ @Override
+ public int size() {
+ return this.tanks.length;
+ }
+
+ @Override
+ public boolean usable(int index) {
+ return this.tanks[index] != null && this.tanks[index].getMaxGas() > 0;
+ }
+
+ @Override
+ public GasTank[] getTanks() {
+ return this.tanks;
+ }
+
+ @Override
+ public GasStack getGasStack(int index) {
+ if (this.usable(index)) {
+ return this.tanks[index].getGas();
+ }
+ return null;
+ }
+
+ @Override
+ public int addGas(int index, @Nonnull GasStack stack, boolean simulate) {
+ if (this.usable(index)) {
+ return this.tanks[index].receive(stack, !simulate);
+ }
+ return 0;
+ }
+
+ @Override
+ public GasStack removeGas(int index, @Nonnull GasStack stack, boolean simulate) {
+ if (this.usable(index) && this.tanks[index].canDraw(stack.getGas())) {
+ return this.tanks[index].draw(stack.amount, !simulate);
+ }
+ return null;
+ }
+
+ @Override
+ public GasStack removeGas(int index, int amount, boolean simulate) {
+ if (this.usable(index)) {
+ return this.tanks[index].draw(amount, !simulate);
+ }
+ return null;
+ }
+
+ @Override
+ public void setGas(int index, @Nullable GasStack stack) {
+ if (this.usable(index)) {
+ this.tanks[index].setGas(stack);
+ }
+ }
+
+ @Override
+ public void setCap(int cap) {
+ for (GasTank tank : this.tanks) {
+ if (tank != null) {
+ tank.setMaxGas(cap);
+ }
+ }
+ }
+
+ @Nonnull
+ @Override
+ public Iterator iterator() {
+ return Arrays.stream(this.tanks).iterator();
+ }
+
+ private static class NotifiableGasTank extends GasTank {
+
+ private final Runnable callback;
+
+ NotifiableGasTank(int max, Runnable callback) {
+ super(max);
+ this.callback = callback;
+ }
+
+ @Override
+ public GasStack draw(int amount, boolean doDraw) {
+ if (doDraw) {
+ this.callback.run();
+ }
+ return super.draw(amount, doDraw);
+ }
+
+ @Override
+ public int receive(GasStack amount, boolean doReceive) {
+ if (doReceive) {
+ this.callback.run();
+ }
+ return super.receive(amount, doReceive);
+ }
+
+ @Override
+ public void setMaxGas(int capacity) {
+ this.callback.run();
+ super.setMaxGas(capacity);
+ }
+
+ @Override
+ public void setGas(GasStack stack) {
+ this.callback.run();
+ super.setGas(stack);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/IGasStorageChannel.java b/src/main/java/com/mekeng/github/common/me/storage/IGasStorageChannel.java
new file mode 100644
index 0000000..217bb0d
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/IGasStorageChannel.java
@@ -0,0 +1,8 @@
+package com.mekeng.github.common.me.storage;
+
+import appeng.api.storage.IStorageChannel;
+import com.mekeng.github.common.me.data.IAEGasStack;
+
+public interface IGasStorageChannel extends IStorageChannel {
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/IPortableGasCell.java b/src/main/java/com/mekeng/github/common/me/storage/IPortableGasCell.java
new file mode 100644
index 0000000..1aa1ec2
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/IPortableGasCell.java
@@ -0,0 +1,11 @@
+package com.mekeng.github.common.me.storage;
+
+import appeng.api.implementations.guiobjects.IGuiItemObject;
+import appeng.api.networking.energy.IEnergySource;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.ITerminalHost;
+import com.mekeng.github.common.me.data.IAEGasStack;
+
+public interface IPortableGasCell extends ITerminalHost, IMEMonitor, IEnergySource, IGuiItemObject {
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/impl/GasCellGuiHandler.java b/src/main/java/com/mekeng/github/common/me/storage/impl/GasCellGuiHandler.java
new file mode 100644
index 0000000..769ee0b
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/impl/GasCellGuiHandler.java
@@ -0,0 +1,34 @@
+package com.mekeng.github.common.me.storage.impl;
+
+import appeng.api.AEApi;
+import appeng.api.implementations.tiles.IChestOrDrive;
+import appeng.api.storage.ICellGuiHandler;
+import appeng.api.storage.ICellHandler;
+import appeng.api.storage.IMEInventoryHandler;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.data.IAEStack;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+
+public class GasCellGuiHandler implements ICellGuiHandler {
+
+ public static GasCellGuiHandler INSTANCE = new GasCellGuiHandler();
+
+ private GasCellGuiHandler() {
+ // NO-OP
+ }
+
+ @Override
+ public > boolean isHandlerFor(final IStorageChannel channel) {
+ return channel == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public void openChestGui(final EntityPlayer player, final IChestOrDrive chest, final ICellHandler cellHandler, final IMEInventoryHandler inv, final ItemStack is, final IStorageChannel chan) {
+ GuiHandler.openTileGui(player, ((TileEntity) chest).getWorld(), ((TileEntity) chest).getPos(), MkEGuis.GAS_TERMINAL);
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/impl/GasList.java b/src/main/java/com/mekeng/github/common/me/storage/impl/GasList.java
new file mode 100644
index 0000000..cd71f0d
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/impl/GasList.java
@@ -0,0 +1,132 @@
+package com.mekeng.github.common.me.storage.impl;
+
+import appeng.api.config.FuzzyMode;
+import appeng.api.storage.data.IItemList;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+
+import javax.annotation.Nonnull;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+
+public class GasList implements IItemList {
+
+ private final Map records = new Object2ObjectOpenHashMap<>();
+
+ protected GasList() {
+ // NO-OP
+ }
+
+ public static GasList create() {
+ return new GasList();
+ }
+
+ @Override
+ public void addStorage(IAEGasStack option) {
+ if (option != null) {
+ IAEGasStack st = this.getGasRecord(option);
+ if (st != null) {
+ st.incStackSize(option.getStackSize());
+ } else {
+ IAEGasStack opt = option.copy();
+ this.putGasRecord(opt);
+ }
+ }
+ }
+
+ @Override
+ public void addCrafting(IAEGasStack option) {
+ if (option != null) {
+ IAEGasStack st = this.getGasRecord(option);
+ if (st != null) {
+ st.setCraftable(true);
+ } else {
+ IAEGasStack opt = option.copy();
+ opt.setStackSize(0L);
+ opt.setCraftable(true);
+ this.putGasRecord(opt);
+ }
+ }
+ }
+
+ @Override
+ public void addRequestable(IAEGasStack option) {
+ if (option != null) {
+ IAEGasStack st = this.getGasRecord(option);
+ if (st != null) {
+ st.setCountRequestable(st.getCountRequestable() + option.getCountRequestable());
+ } else {
+ IAEGasStack opt = option.copy();
+ opt.setStackSize(0L);
+ opt.setCraftable(false);
+ opt.setCountRequestable(option.getCountRequestable());
+ this.putGasRecord(opt);
+ }
+ }
+ }
+
+ @Override
+ public IAEGasStack getFirstItem() {
+ for (IAEGasStack gas : this) {
+ return gas;
+ }
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return this.records.values().size();
+ }
+
+ @Nonnull
+ @Override
+ public Iterator iterator() {
+ return new MeaningfulGasIterator<>(this.records.values().iterator());
+ }
+
+ @Override
+ public void resetStatus() {
+ for (IAEGasStack gas : this) {
+ gas.reset();
+ }
+ }
+
+ @Override
+ public void add(IAEGasStack option) {
+ if (option != null) {
+ IAEGasStack st = this.getGasRecord(option);
+ if (st != null) {
+ st.add(option);
+ } else {
+ IAEGasStack opt = option.copy();
+ this.putGasRecord(opt);
+ }
+ }
+ }
+
+ @Override
+ public IAEGasStack findPrecise(IAEGasStack gasStack) {
+ return gasStack == null ? null : this.getGasRecord(gasStack);
+ }
+
+ @Override
+ public Collection findFuzzy(IAEGasStack filter, FuzzyMode fuzzyMode) {
+ return filter == null ? Collections.emptyList() : Collections.singletonList(this.findPrecise(filter));
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return !this.iterator().hasNext();
+ }
+
+ private IAEGasStack getGasRecord(IAEGasStack gas) {
+ return this.records.get(gas);
+ }
+
+ private void putGasRecord(IAEGasStack gas) {
+ this.records.put(gas, gas);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/impl/GasStorageChannel.java b/src/main/java/com/mekeng/github/common/me/storage/impl/GasStorageChannel.java
new file mode 100644
index 0000000..692c6d9
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/impl/GasStorageChannel.java
@@ -0,0 +1,70 @@
+package com.mekeng.github.common.me.storage.impl;
+
+import appeng.api.storage.data.IItemList;
+import com.google.common.base.Preconditions;
+import com.mekeng.github.common.ItemAndBlocks;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.util.Utils;
+import io.netty.buffer.ByteBuf;
+import mekanism.api.gas.GasStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public final class GasStorageChannel implements IGasStorageChannel {
+
+ public static GasStorageChannel INSTANCE = new GasStorageChannel();
+
+ private GasStorageChannel() {
+ // NO-OP
+ }
+
+ @Override
+ public int transferFactor() {
+ return 4000;
+ }
+
+ @Override
+ public int getUnitsPerByte() {
+ return 32000;
+ }
+
+ @Nonnull
+ @Override
+ public IItemList createList() {
+ return GasList.create();
+ }
+
+ @Nullable
+ @Override
+ public IAEGasStack createStack(@Nonnull Object input) {
+ Preconditions.checkNotNull(input);
+ if (input instanceof GasStack) {
+ return AEGasStack.of((GasStack) input);
+ } else if (input instanceof ItemStack) {
+ ItemStack is = (ItemStack) input;
+ return is.getItem() == ItemAndBlocks.DUMMY_GAS ? AEGasStack.of(ItemAndBlocks.DUMMY_GAS.getGasStack(is)) : AEGasStack.of(Utils.getGasFromItem(is));
+ } else {
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public IAEGasStack readFromPacket(@Nonnull ByteBuf input) {
+ Preconditions.checkNotNull(input);
+ return AEGasStack.of(input);
+ }
+
+ @Nullable
+ @Override
+ public IAEGasStack createFromNBT(@Nonnull NBTTagCompound nbt) {
+ Preconditions.checkNotNull(nbt);
+ return AEGasStack.of(nbt);
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/impl/MEMonitorIGasHandler.java b/src/main/java/com/mekeng/github/common/me/storage/impl/MEMonitorIGasHandler.java
new file mode 100644
index 0000000..2d38600
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/impl/MEMonitorIGasHandler.java
@@ -0,0 +1,219 @@
+package com.mekeng.github.common.me.storage.impl;
+
+import appeng.api.AEApi;
+import appeng.api.config.AccessRestriction;
+import appeng.api.config.Actionable;
+import appeng.api.config.StorageFilter;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.data.IItemList;
+import appeng.me.storage.ITickingMonitor;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.GasTankInfo;
+import mekanism.api.gas.IGasHandler;
+import net.minecraft.util.EnumFacing;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public class MEMonitorIGasHandler implements IMEMonitor, ITickingMonitor {
+ private final IGasHandler handler;
+ private final EnumFacing face;
+ private IItemList cache = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+ private final HashMap, Object> listeners = new HashMap<>();
+ private IActionSource mySource;
+ private StorageFilter mode = StorageFilter.EXTRACTABLE_ONLY;
+
+ public MEMonitorIGasHandler(final IGasHandler handler, EnumFacing face) {
+ this.handler = handler;
+ this.face = face;
+ }
+
+ @Override
+ public void addListener(final IMEMonitorHandlerReceiver l, final Object verificationToken) {
+ this.listeners.put(l, verificationToken);
+ }
+
+ @Override
+ public void removeListener(final IMEMonitorHandlerReceiver l) {
+ this.listeners.remove(l);
+ }
+
+ @Override
+ public IAEGasStack injectItems(final IAEGasStack input, final Actionable type, final IActionSource src) {
+ final int filled = this.handler.receiveGas(this.face, input.getGasStack(), type == Actionable.MODULATE);
+
+ if (filled == 0) {
+ return input.copy();
+ }
+
+ if (filled == input.getStackSize()) {
+ return null;
+ }
+
+ final IAEGasStack o = input.copy();
+ o.setStackSize(input.getStackSize() - filled);
+
+ if (type == Actionable.MODULATE) {
+ IAEGasStack added = o.copy();
+ this.cache.add(added);
+ this.postDifference(Collections.singletonList(added));
+ this.onTick();
+ }
+
+ return o;
+ }
+
+ @Override
+ public IAEGasStack extractItems(final IAEGasStack request, final Actionable type, final IActionSource src) {
+ final GasStack removed = Utils.drawGas(this.handler, request.getGasStack(), this.face, request.getGasStack().amount, type == Actionable.MODULATE);
+
+ if (removed == null || !removed.isGasEqual(request.getGasStack()) || removed.amount == 0) {
+ return null;
+ }
+
+ final IAEGasStack o = request.copy();
+ o.setStackSize(removed.amount);
+
+ if (type == Actionable.MODULATE) {
+ IAEGasStack cachedStack = this.cache.findPrecise(request);
+ if (cachedStack != null) {
+ cachedStack.decStackSize(o.getStackSize());
+ this.postDifference(Collections.singletonList(o.copy().setStackSize(-o.getStackSize())));
+ }
+ }
+ return o;
+ }
+
+ @Override
+ public IStorageChannel getChannel() {
+ return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ }
+
+ @Override
+ public TickRateModulation onTick() {
+ boolean changed = false;
+
+ final List changes = new ArrayList<>();
+ final GasTankInfo[] tankProperties = this.handler.getTankInfo();
+
+ IItemList currentlyOnStorage = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList();
+
+ for (GasTankInfo tankProperty : tankProperties) {
+ if (this.mode == StorageFilter.EXTRACTABLE_ONLY && this.handler.drawGas(this.face, 1, false) == null) {
+ continue;
+ }
+ currentlyOnStorage.add(AEGasStack.of(tankProperty.getGas()));
+ }
+
+ for (final IAEGasStack is : cache) {
+ is.setStackSize(-is.getStackSize());
+ }
+
+ for (final IAEGasStack is : currentlyOnStorage) {
+ cache.add(is);
+ }
+
+ for (final IAEGasStack is : cache) {
+ if (is.getStackSize() != 0) {
+ changes.add(is);
+ }
+ }
+
+ cache = currentlyOnStorage;
+
+ if (!changes.isEmpty()) {
+ this.postDifference(changes);
+ changed = true;
+ }
+
+ return changed ? TickRateModulation.URGENT : TickRateModulation.SLOWER;
+ }
+
+ private void postDifference(final Iterable a) {
+ if (a != null) {
+ final Iterator, Object>> i = this.listeners.entrySet().iterator();
+ while (i.hasNext()) {
+ final Map.Entry, Object> l = i.next();
+ final IMEMonitorHandlerReceiver key = l.getKey();
+ if (key.isValid(l.getValue())) {
+ key.postChange(this, a, this.getActionSource());
+ } else {
+ i.remove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public AccessRestriction getAccess() {
+ return AccessRestriction.READ_WRITE;
+ }
+
+ @Override
+ public boolean isPrioritized(final IAEGasStack input) {
+ return false;
+ }
+
+ @Override
+ public boolean canAccept(final IAEGasStack input) {
+ return true;
+ }
+
+ @Override
+ public int getPriority() {
+ return 0;
+ }
+
+ @Override
+ public int getSlot() {
+ return 0;
+ }
+
+ @Override
+ public boolean validForPass(final int i) {
+ return true;
+ }
+
+ @Override
+ public IItemList getAvailableItems(final IItemList out) {
+ for (final IAEGasStack fs : cache) {
+ out.addStorage(fs);
+ }
+ return out;
+ }
+
+ @Override
+ public IItemList getStorageList() {
+ return this.cache;
+ }
+
+ private StorageFilter getMode() {
+ return this.mode;
+ }
+
+ public void setMode(final StorageFilter mode) {
+ this.mode = mode;
+ }
+
+ private IActionSource getActionSource() {
+ return this.mySource;
+ }
+
+ @Override
+ public void setActionSource(final IActionSource mySource) {
+ this.mySource = mySource;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/me/storage/impl/MeaningfulGasIterator.java b/src/main/java/com/mekeng/github/common/me/storage/impl/MeaningfulGasIterator.java
new file mode 100644
index 0000000..04fa96a
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/me/storage/impl/MeaningfulGasIterator.java
@@ -0,0 +1,39 @@
+package com.mekeng.github.common.me.storage.impl;
+
+import appeng.api.storage.data.IAEStack;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class MeaningfulGasIterator> implements Iterator {
+ private final Iterator parent;
+ private T next;
+
+ public MeaningfulGasIterator(Iterator iterator) {
+ this.parent = iterator;
+ }
+
+ public boolean hasNext() {
+ while(this.parent.hasNext()) {
+ this.next = this.parent.next();
+ if (this.next.isMeaningful()) {
+ return true;
+ }
+ this.parent.remove();
+ }
+ this.next = null;
+ return false;
+ }
+
+ public T next() {
+ if (this.next == null) {
+ throw new NoSuchElementException();
+ } else {
+ return this.next;
+ }
+ }
+
+ public void remove() {
+ this.parent.remove();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/mekeng/github/common/part/IPartGroup.java b/src/main/java/com/mekeng/github/common/part/IPartGroup.java
new file mode 100644
index 0000000..f7ec59f
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/IPartGroup.java
@@ -0,0 +1,7 @@
+package com.mekeng.github.common.part;
+
+public interface IPartGroup {
+
+ String getUnlocalizedGroupName();
+
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasExportBus.java b/src/main/java/com/mekeng/github/common/part/PartGasExportBus.java
new file mode 100644
index 0000000..418f525
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasExportBus.java
@@ -0,0 +1,147 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.FuzzyMode;
+import appeng.api.config.RedstoneMode;
+import appeng.api.config.SchedulingMode;
+import appeng.api.config.Settings;
+import appeng.api.config.YesNo;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.networking.ticking.TickingRequest;
+import appeng.api.parts.IPartCollisionHelper;
+import appeng.api.parts.IPartModel;
+import appeng.api.storage.IMEMonitor;
+import appeng.items.parts.PartModels;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.MachineSource;
+import appeng.parts.PartModel;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.me.GasTickRates;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import mekanism.api.gas.IGasHandler;
+import mekanism.common.capabilities.Capabilities;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+
+import javax.annotation.Nonnull;
+
+public class PartGasExportBus extends PartSharedGasBus {
+
+ public static final ResourceLocation MODEL_BASE = new ResourceLocation(MekEng.MODID, "part/gas_export_bus_base");
+ @PartModels
+ public static final IPartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_export_bus_off"));
+ @PartModels
+ public static final IPartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_export_bus_on"));
+ @PartModels
+ public static final IPartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_export_bus_has_channel"));
+
+ private final IActionSource source;
+
+ public PartGasExportBus(ItemStack is) {
+ super(is);
+ this.getConfigManager().registerSetting(Settings.REDSTONE_CONTROLLED, RedstoneMode.IGNORE);
+ this.getConfigManager().registerSetting(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL);
+ this.getConfigManager().registerSetting(Settings.CRAFT_ONLY, YesNo.NO);
+ this.getConfigManager().registerSetting(Settings.SCHEDULING_MODE, SchedulingMode.DEFAULT);
+ this.source = new MachineSource(this);
+ }
+
+ @Nonnull
+ @Override
+ public TickingRequest getTickingRequest(@Nonnull IGridNode node) {
+ return new TickingRequest(GasTickRates.GasExportBus.getMin(), GasTickRates.GasExportBus.getMax(), this.isSleeping(), false);
+ }
+
+ @Nonnull
+ @Override
+ public TickRateModulation tickingRequest(@Nonnull IGridNode node, int ticksSinceLastCall) {
+ return this.canDoBusWork() ? this.doBusWork() : TickRateModulation.IDLE;
+ }
+
+ @Override
+ protected boolean canDoBusWork() {
+ return this.getProxy().isActive();
+ }
+
+ @Override
+ public boolean isExport() {
+ return true;
+ }
+
+ @Override
+ protected TickRateModulation doBusWork() {
+ if (!this.canDoBusWork()) {
+ return TickRateModulation.IDLE;
+ }
+
+ final TileEntity te = this.getConnectedTE();
+
+ if (te != null && te.hasCapability(Capabilities.GAS_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite())) {
+ try {
+ final EnumFacing side = this.getSide().getFacing().getOpposite();
+ final IGasHandler gh = te.getCapability(Capabilities.GAS_HANDLER_CAPABILITY, side);
+ final IMEMonitor inv = this.getProxy().getStorage().getInventory(this.getChannel());
+
+ if (gh != null) {
+ for (int i = 0; i < this.getConfig().size(); i++) {
+ IAEGasStack gas = AEGasStack.of(this.getConfig().getGasStack(i));
+ if (gas != null) {
+ final IAEGasStack toExtract = gas.copy();
+
+ toExtract.setStackSize(this.calculateAmountToSend());
+
+ final IAEGasStack out = inv.extractItems(toExtract, Actionable.SIMULATE, this.source);
+
+ if (out != null) {
+ int wasInserted = gh.receiveGas(side, out.getGasStack(), true);
+
+ if (wasInserted > 0) {
+ toExtract.setStackSize(wasInserted);
+ inv.extractItems(toExtract, Actionable.MODULATE, this.source);
+ return TickRateModulation.FASTER;
+ }
+ }
+ }
+ }
+
+ return TickRateModulation.SLOWER;
+ }
+ } catch (GridAccessException e) {
+ // Ignore
+ }
+ }
+
+ return TickRateModulation.SLEEP;
+ }
+
+ @Override
+ public void getBoxes(final IPartCollisionHelper bch) {
+ bch.addBox(4, 4, 12, 12, 12, 14);
+ bch.addBox(5, 5, 14, 11, 11, 15);
+ bch.addBox(6, 6, 15, 10, 10, 16);
+ bch.addBox(6, 6, 11, 10, 10, 12);
+ }
+
+ @Override
+ public RedstoneMode getRSMode() {
+ return (RedstoneMode) this.getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED);
+ }
+
+ @Nonnull
+ @Override
+ public IPartModel getStaticModels() {
+ if (this.isActive() && this.isPowered()) {
+ return MODELS_HAS_CHANNEL;
+ } else if (this.isPowered()) {
+ return MODELS_ON;
+ } else {
+ return MODELS_OFF;
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasImportBus.java b/src/main/java/com/mekeng/github/common/part/PartGasImportBus.java
new file mode 100644
index 0000000..ad903f4
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasImportBus.java
@@ -0,0 +1,157 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.config.Actionable;
+import appeng.api.config.FuzzyMode;
+import appeng.api.config.RedstoneMode;
+import appeng.api.config.SchedulingMode;
+import appeng.api.config.Settings;
+import appeng.api.config.YesNo;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.networking.ticking.TickingRequest;
+import appeng.api.parts.IPartModel;
+import appeng.api.storage.IMEMonitor;
+import appeng.me.GridAccessException;
+import appeng.me.helpers.MachineSource;
+import appeng.parts.PartModel;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.me.GasTickRates;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.util.Utils;
+import mekanism.api.gas.GasStack;
+import mekanism.api.gas.IGasHandler;
+import mekanism.common.capabilities.Capabilities;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
+
+import javax.annotation.Nonnull;
+
+public class PartGasImportBus extends PartSharedGasBus {
+
+ public static final ResourceLocation MODEL_BASE = new ResourceLocation(MekEng.MODID, "part/gas_import_bus_base");
+ public static final IPartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_import_bus_off"));
+ public static final IPartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_import_bus_on"));
+ public static final IPartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_import_bus_has_channel"));
+
+ private final IActionSource source;
+
+ public PartGasImportBus(ItemStack is) {
+ super(is);
+ this.getConfigManager().registerSetting(Settings.REDSTONE_CONTROLLED, RedstoneMode.IGNORE);
+ this.getConfigManager().registerSetting(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL);
+ this.getConfigManager().registerSetting(Settings.CRAFT_ONLY, YesNo.NO);
+ this.getConfigManager().registerSetting(Settings.SCHEDULING_MODE, SchedulingMode.DEFAULT);
+ this.source = new MachineSource(this);
+ }
+
+ @Nonnull
+ @Override
+ public TickingRequest getTickingRequest(@Nonnull IGridNode node) {
+ return new TickingRequest(GasTickRates.GasImportBus.getMin(), GasTickRates.GasImportBus.getMax(), this.isSleeping(), false);
+ }
+
+ @Nonnull
+ @Override
+ public TickRateModulation tickingRequest(@Nonnull IGridNode node, int ticksSinceLastCall) {
+ return this.canDoBusWork() ? this.doBusWork() : TickRateModulation.IDLE;
+ }
+
+ @Override
+ protected TickRateModulation doBusWork() {
+ if (!this.canDoBusWork()) {
+ return TickRateModulation.IDLE;
+ }
+
+ final TileEntity te = this.getConnectedTE();
+
+ if (te != null && te.hasCapability(Capabilities.GAS_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite())) {
+ try {
+ final EnumFacing side = this.getSide().getFacing().getOpposite();
+ final IGasHandler gh = te.getCapability(Capabilities.GAS_HANDLER_CAPABILITY, side);
+ final IMEMonitor inv = this.getProxy().getStorage().getInventory(this.getChannel());
+
+ if (gh != null) {
+ final GasStack gasStack = gh.drawGas(side, this.calculateAmountToSend(), false);
+
+ if (this.filterEnabled() && !this.isInFilter(gasStack)) {
+ return TickRateModulation.SLOWER;
+ }
+
+ final AEGasStack aeGasStack = AEGasStack.of(gasStack);
+
+ if (aeGasStack != null) {
+ final IAEGasStack notInserted = inv.injectItems(aeGasStack, Actionable.MODULATE, this.source);
+
+ if (notInserted != null && notInserted.getStackSize() > 0) {
+ aeGasStack.decStackSize(notInserted.getStackSize());
+ }
+
+ Utils.drawGas(gh, aeGasStack.getGasStack(), side, aeGasStack.getGasStack().amount, true);
+
+ return TickRateModulation.FASTER;
+ }
+
+ return TickRateModulation.IDLE;
+ }
+ } catch (GridAccessException e) {
+ MekEng.log.error(e);
+ }
+ }
+
+ return TickRateModulation.SLEEP;
+ }
+
+ @Override
+ protected boolean canDoBusWork() {
+ return this.getProxy().isActive();
+ }
+
+ @Override
+ public boolean isExport() {
+ return false;
+ }
+
+ private boolean isInFilter(GasStack gas) {
+ if (gas == null) {
+ return false;
+ }
+ for (int i = 0; i < this.getConfig().size(); i++) {
+ final GasStack filter = this.getConfig().getGasStack(i);
+ if (gas.isGasEqual(filter)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean filterEnabled() {
+ for (int i = 0; i < this.getConfig().size(); i++) {
+ if (this.getConfig().getGasStack(i) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public RedstoneMode getRSMode() {
+ return (RedstoneMode) this.getConfigManager().getSetting(Settings.REDSTONE_CONTROLLED);
+ }
+
+ @Nonnull
+ @Override
+ public IPartModel getStaticModels() {
+ if (this.isActive() && this.isPowered()) {
+ return MODELS_HAS_CHANNEL;
+ } else if (this.isPowered()) {
+ return MODELS_ON;
+ } else {
+ return MODELS_OFF;
+ }
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasInterface.java b/src/main/java/com/mekeng/github/common/part/PartGasInterface.java
new file mode 100644
index 0000000..78dacb5
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasInterface.java
@@ -0,0 +1,224 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.config.Upgrades;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.events.MENetworkChannelsChanged;
+import appeng.api.networking.events.MENetworkEventSubscribe;
+import appeng.api.networking.events.MENetworkPowerStatusChange;
+import appeng.api.networking.ticking.IGridTickable;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.networking.ticking.TickingRequest;
+import appeng.api.parts.IPartCollisionHelper;
+import appeng.api.parts.IPartHost;
+import appeng.api.parts.IPartModel;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.IStorageMonitorable;
+import appeng.api.storage.data.IAEStack;
+import appeng.api.util.AECableType;
+import appeng.api.util.AEPartLocation;
+import appeng.api.util.IConfigManager;
+import appeng.core.sync.GuiBridge;
+import appeng.core.sync.GuiWrapper;
+import appeng.helpers.IPriorityHost;
+import appeng.helpers.Reflected;
+import appeng.items.parts.PartModels;
+import appeng.parts.PartBasicState;
+import appeng.parts.PartModel;
+import appeng.util.Platform;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.ItemAndBlocks;
+import com.mekeng.github.common.container.handler.AEGuiBridge;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.duality.impl.DualityGasInterface;
+import com.mekeng.github.common.me.inventory.IConfigurableGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.Vec3d;
+import net.minecraftforge.common.capabilities.Capability;
+import net.minecraftforge.items.IItemHandler;
+
+import javax.annotation.Nonnull;
+import java.util.EnumSet;
+import java.util.List;
+
+public class PartGasInterface extends PartBasicState implements IGridTickable, IStorageMonitorable, IGasInterfaceHost, IPriorityHost, IConfigurableGasInventory {
+ public static final ResourceLocation MODEL_BASE = new ResourceLocation(MekEng.MODID, "part/gas_interface_base");
+
+ @PartModels
+ public static final PartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_interface_off"));
+
+ @PartModels
+ public static final PartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_interface_on"));
+
+ @PartModels
+ public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_interface_has_channel"));
+
+ private final DualityGasInterface duality = new DualityGasInterface(this.getProxy(), this);
+
+ @Reflected
+ public PartGasInterface(final ItemStack is) {
+ super(is);
+ }
+
+ @Override
+ public void setPartHostInfo(final AEPartLocation side, final IPartHost host, final TileEntity tile) {
+ super.setPartHostInfo(side, host, tile);
+ this.duality.getTankHandler().setSide(this.getTargets());
+ }
+
+ @Override
+ public DualityGasInterface getDualityGasInterface() {
+ return this.duality;
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void chanRender(final MENetworkChannelsChanged c) {
+ this.duality.notifyNeighbors();
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void powerRender(final MENetworkPowerStatusChange c) {
+ this.duality.notifyNeighbors();
+ }
+
+ @Override
+ public void getBoxes(final IPartCollisionHelper bch) {
+ bch.addBox(2, 2, 14, 14, 14, 16);
+ bch.addBox(5, 5, 12, 11, 11, 14);
+ }
+
+ @Override
+ public void gridChanged() {
+ this.duality.gridChanged();
+ }
+
+ @Override
+ public void readFromNBT(final NBTTagCompound data) {
+ super.readFromNBT(data);
+ this.duality.readFromNBT(data);
+ }
+
+ @Override
+ public void writeToNBT(final NBTTagCompound data) {
+ super.writeToNBT(data);
+ this.duality.writeToNBT(data);
+ }
+
+ @Override
+ public void getDrops(final List drops, final boolean wrenched) {
+ this.duality.addDrops(drops);
+ }
+
+ @Override
+ public float getCableConnectionLength(AECableType cable) {
+ return 4;
+ }
+
+ @Override
+ public boolean onPartActivate(final EntityPlayer p, final EnumHand hand, final Vec3d pos) {
+ if (Platform.isServer() && this.getActionableNode() != null) {
+ GuiHandler.openPartGui(p, this.getTile().getWorld(), this.getTile().getPos(), this.getSide().getFacing(), MkEGuis.GAS_INTERFACE);
+ }
+ return true;
+ }
+
+ @Override
+ public > IMEMonitor getInventory(IStorageChannel channel) {
+ return this.duality.getInventory(channel);
+ }
+
+ @Nonnull
+ @Override
+ public TickingRequest getTickingRequest(@Nonnull final IGridNode node) {
+ return this.duality.getTickingRequest(node);
+ }
+
+ @Nonnull
+ @Override
+ public TickRateModulation tickingRequest(@Nonnull final IGridNode node, final int ticksSinceLastCall) {
+ return this.duality.tickingRequest(node, ticksSinceLastCall);
+ }
+
+ @Override
+ public EnumSet getTargets() {
+ return EnumSet.of(this.getSide().getFacing());
+ }
+
+ @Override
+ public TileEntity getTileEntity() {
+ return super.getHost().getTile();
+ }
+
+ @Nonnull
+ @Override
+ public IPartModel getStaticModels() {
+ if (this.isActive() && this.isPowered()) {
+ return MODELS_HAS_CHANNEL;
+ } else if (this.isPowered()) {
+ return MODELS_ON;
+ } else {
+ return MODELS_OFF;
+ }
+ }
+
+ @Override
+ public int getPriority() {
+ return this.duality.getPriority();
+ }
+
+ @Override
+ public void setPriority(final int newValue) {
+ this.duality.setPriority(newValue);
+ }
+
+ @Override
+ public boolean hasCapability(Capability> capabilityClass) {
+ return this.duality.hasCapability(capabilityClass, this.getSide().getFacing());
+ }
+
+ @Override
+ public T getCapability(Capability capabilityClass) {
+ return this.duality.getCapability(capabilityClass, this.getSide().getFacing());
+ }
+
+ @Override
+ public int getInstalledUpgrades(Upgrades u) {
+ return this.duality.getInstalledUpgrades(u);
+ }
+
+ @Override
+ public IConfigManager getConfigManager() {
+ return this.duality.getConfigManager();
+ }
+
+ @Override
+ public IItemHandler getInventoryByName(String name) {
+ return this.duality.getInventoryByName(name);
+ }
+
+ @Override
+ public IGasInventory getGasInventoryByName(final String name) {
+ return this.duality.getGasInventoryByName(name);
+ }
+
+ @Override
+ public ItemStack getItemStackRepresentation() {
+ return new ItemStack(ItemAndBlocks.GAS_INTERFACE_PART);
+ }
+
+ @Override
+ public GuiBridge getGuiBridge() {
+ return GuiWrapper.INSTANCE.wrap(AEGuiBridge.GAS_INTERFACE);
+ }
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasInterfaceConfigurationTerminal.java b/src/main/java/com/mekeng/github/common/part/PartGasInterfaceConfigurationTerminal.java
new file mode 100644
index 0000000..a164de3
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasInterfaceConfigurationTerminal.java
@@ -0,0 +1,53 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.parts.IPartModel;
+import appeng.core.AppEng;
+import appeng.items.parts.PartModels;
+import appeng.parts.PartModel;
+import appeng.parts.reporting.AbstractPartDisplay;
+import appeng.util.Platform;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.Vec3d;
+
+import javax.annotation.Nonnull;
+
+public class PartGasInterfaceConfigurationTerminal extends AbstractPartDisplay {
+
+ public static final ResourceLocation MODEL_OFF = new ResourceLocation(MekEng.MODID, "part/gas_interface_configuration_terminal_off");
+ public static final ResourceLocation MODEL_ON = new ResourceLocation(MekEng.MODID, "part/gas_interface_configuration_terminal_on");
+ public static final IPartModel MODELS_OFF = new PartModel(MODEL_BASE, MODEL_OFF, MODEL_STATUS_OFF);
+ public static final IPartModel MODELS_ON = new PartModel(MODEL_BASE, MODEL_ON, MODEL_STATUS_ON);
+ public static final IPartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, MODEL_ON, MODEL_STATUS_HAS_CHANNEL);
+ public String in = "";
+
+ public PartGasInterfaceConfigurationTerminal(final ItemStack is) {
+ super(is);
+ }
+
+ @Override
+ public boolean onPartActivate(final EntityPlayer player, final EnumHand hand, final Vec3d pos) {
+ if (!super.onPartActivate(player, hand, pos)) {
+ if (Platform.isServer() && this.getActionableNode() != null) {
+ GuiHandler.openPartGui(player, this.getTile().getWorld(), this.getTile().getPos(), this.getSide().getFacing(), MkEGuis.GAS_INTERFACE_TERMINAL);
+ }
+ }
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public IPartModel getStaticModels() {
+ return this.selectModel(MODELS_OFF, MODELS_ON, MODELS_HAS_CHANNEL);
+ }
+
+ public void saveSearchStrings(String in) {
+ this.in = in;
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasLevelEmitter.java b/src/main/java/com/mekeng/github/common/part/PartGasLevelEmitter.java
new file mode 100644
index 0000000..e715682
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasLevelEmitter.java
@@ -0,0 +1,331 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.AEApi;
+import appeng.api.config.RedstoneMode;
+import appeng.api.config.Settings;
+import appeng.api.networking.events.MENetworkChannelsChanged;
+import appeng.api.networking.events.MENetworkEventSubscribe;
+import appeng.api.networking.events.MENetworkPowerStatusChange;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.storage.IBaseMonitor;
+import appeng.api.networking.storage.IStackWatcher;
+import appeng.api.networking.storage.IStackWatcherHost;
+import appeng.api.parts.IPartCollisionHelper;
+import appeng.api.parts.IPartModel;
+import appeng.api.storage.IMEMonitor;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.data.IAEStack;
+import appeng.api.storage.data.IItemList;
+import appeng.api.util.AECableType;
+import appeng.api.util.AEPartLocation;
+import appeng.api.util.IConfigManager;
+import appeng.core.AppEng;
+import appeng.items.parts.PartModels;
+import appeng.me.GridAccessException;
+import appeng.me.cache.NetworkMonitor;
+import appeng.parts.PartModel;
+import appeng.util.IConfigManagerHost;
+import appeng.util.Platform;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.inventory.IConfigurableGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventoryHost;
+import com.mekeng.github.common.me.inventory.impl.GasInventory;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.EnumParticleTypes;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+
+import javax.annotation.Nonnull;
+import java.util.Random;
+
+public class PartGasLevelEmitter extends PartGasUpgradeable implements IStackWatcherHost, IConfigManagerHost, IGasInventoryHost, IMEMonitorHandlerReceiver, IConfigurableGasInventory {
+ @PartModels
+ public static final ResourceLocation MODEL_BASE_OFF = new ResourceLocation(AppEng.MOD_ID, "part/level_emitter_base_off");
+ @PartModels
+ public static final ResourceLocation MODEL_BASE_ON = new ResourceLocation(AppEng.MOD_ID, "part/level_emitter_base_on");
+ @PartModels
+ public static final ResourceLocation MODEL_STATUS_OFF = new ResourceLocation(AppEng.MOD_ID, "part/level_emitter_status_off");
+ @PartModels
+ public static final ResourceLocation MODEL_STATUS_ON = new ResourceLocation(AppEng.MOD_ID, "part/level_emitter_status_on");
+ @PartModels
+ public static final ResourceLocation MODEL_STATUS_HAS_CHANNEL = new ResourceLocation(AppEng.MOD_ID, "part/level_emitter_status_has_channel");
+
+ public static final PartModel MODEL_OFF_OFF = new PartModel(MODEL_BASE_OFF, MODEL_STATUS_OFF);
+ public static final PartModel MODEL_OFF_ON = new PartModel(MODEL_BASE_OFF, MODEL_STATUS_ON);
+ public static final PartModel MODEL_OFF_HAS_CHANNEL = new PartModel(MODEL_BASE_OFF, MODEL_STATUS_HAS_CHANNEL);
+ public static final PartModel MODEL_ON_OFF = new PartModel(MODEL_BASE_ON, MODEL_STATUS_OFF);
+ public static final PartModel MODEL_ON_ON = new PartModel(MODEL_BASE_ON, MODEL_STATUS_ON);
+ public static final PartModel MODEL_ON_HAS_CHANNEL = new PartModel(MODEL_BASE_ON, MODEL_STATUS_HAS_CHANNEL);
+
+ private static final int FLAG_ON = 4;
+
+ private boolean prevState = false;
+ private long lastReportedValue = 0;
+ private long reportingValue = 0;
+ private IStackWatcher stackWatcher = null;
+ private final GasInventory config = new GasInventory(1, this);
+
+ public PartGasLevelEmitter(ItemStack is) {
+ super(is);
+ this.getConfigManager().registerSetting(Settings.REDSTONE_EMITTER, RedstoneMode.HIGH_SIGNAL);
+ }
+
+ public long getReportingValue() {
+ return this.reportingValue;
+ }
+
+ public void setReportingValue(final long v) {
+ this.reportingValue = v;
+ this.updateState();
+ }
+
+ @Override
+ public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) {
+ this.configureWatchers();
+ }
+
+ @Override
+ public void updateWatcher(IStackWatcher newWatcher) {
+ this.stackWatcher = newWatcher;
+ this.configureWatchers();
+ }
+
+ @Override
+ public void onStackChange(IItemList> o, IAEStack> fullStack, IAEStack> diffStack, IActionSource src, IStorageChannel> chan) {
+ if (chan == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class) && fullStack.equals(this.config.getGasStack(0))) {
+ this.lastReportedValue = fullStack.getStackSize();
+ this.updateState();
+ }
+ }
+
+ @Override
+ public void onGasInventoryChanged(IGasInventory inv, int slot) {
+ this.configureWatchers();
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void powerRender(final MENetworkPowerStatusChange powerEvent) {
+ if (this.getProxy().isActive()) {
+ onListUpdate();
+ }
+ this.updateState();
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void chanRender(final MENetworkChannelsChanged c) {
+ if (this.getProxy().isActive()) {
+ onListUpdate();
+ }
+ this.updateState();
+ }
+
+ @Override
+ public int isProvidingStrongPower() {
+ return this.prevState ? 15 : 0;
+ }
+
+ @Override
+ public int isProvidingWeakPower() {
+ return this.prevState ? 15 : 0;
+ }
+
+ @Override
+ protected int populateFlags(final int cf) {
+ return cf | (this.prevState ? FLAG_ON : 0);
+ }
+
+ @Override
+ public boolean isValid(final Object effectiveGrid) {
+ try {
+ return this.getProxy().getGrid() == effectiveGrid;
+ } catch (final GridAccessException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void postChange(final IBaseMonitor monitor, final Iterable change, final IActionSource actionSource) {
+ this.updateReportingValue((IMEMonitor) monitor);
+ }
+
+ @Override
+ public void onListUpdate() {
+ try {
+ final IStorageChannel channel = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+ final IMEMonitor inventory = this.getProxy().getStorage().getInventory(channel);
+
+ this.updateReportingValue(inventory);
+ } catch (final GridAccessException e) {
+ // ;P
+ }
+ }
+
+ private void updateState() {
+ final boolean isOn = this.isLevelEmitterOn();
+ if (this.prevState != isOn) {
+ this.getHost().markForUpdate();
+ final TileEntity te = this.getHost().getTile();
+ this.prevState = isOn;
+ Platform.notifyBlocksOfNeighbors(te.getWorld(), te.getPos());
+ Platform.notifyBlocksOfNeighbors(te.getWorld(), te.getPos().offset(this.getSide().getFacing()));
+ }
+ }
+
+ private void configureWatchers() {
+ final IGasStorageChannel channel = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class);
+
+ if (this.stackWatcher != null) {
+ this.stackWatcher.reset();
+
+ final IAEGasStack myStack = AEGasStack.of(this.config.getGasStack(0));
+
+ try {
+ if (myStack != null) {
+ this.getProxy().getStorage().getInventory(channel).removeListener(this);
+ this.stackWatcher.add(myStack);
+ } else {
+ this.getProxy()
+ .getStorage()
+ .getInventory(channel)
+ .addListener(this, this.getProxy().getGrid());
+ }
+
+ final IMEMonitor inventory = this.getProxy().getStorage().getInventory(channel);
+
+ this.updateReportingValue(inventory);
+ } catch (GridAccessException e) {
+ // NOP
+ }
+ }
+ }
+
+ private void updateReportingValue(final IMEMonitor monitor) {
+ final IAEGasStack myStack = AEGasStack.of(this.config.getGasStack(0));
+
+ if (myStack == null) {
+ if (monitor instanceof NetworkMonitor) {
+ this.lastReportedValue = ((NetworkMonitor) monitor).getGridCurrentCount();
+ }
+ } else {
+ final IAEGasStack r = monitor.getStorageList().findPrecise(myStack);
+ if (r == null) {
+ this.lastReportedValue = 0;
+ } else {
+ this.lastReportedValue = r.getStackSize();
+ }
+ }
+ this.updateState();
+ }
+
+ private boolean isLevelEmitterOn() {
+ if (Platform.isClient()) {
+ return (this.getClientFlags() & FLAG_ON) == FLAG_ON;
+ }
+
+ if (!this.getProxy().isActive()) {
+ return false;
+ }
+
+ final boolean flipState = this.getConfigManager().getSetting(Settings.REDSTONE_EMITTER) == RedstoneMode.LOW_SIGNAL;
+ return flipState == (this.reportingValue > this.lastReportedValue);
+ }
+
+ @Nonnull
+ @Override
+ public AECableType getCableConnectionType(final AEPartLocation dir) {
+ return AECableType.SMART;
+ }
+
+ @Override
+ public float getCableConnectionLength(AECableType cable) {
+ return 16;
+ }
+
+ @Override
+ public boolean canConnectRedstone() {
+ return true;
+ }
+
+ @Override
+ public void getBoxes(final IPartCollisionHelper bch) {
+ bch.addBox(7, 7, 11, 9, 9, 16);
+ }
+
+ @Override
+ public void randomDisplayTick(final World world, final BlockPos pos, final Random r) {
+ if (this.isLevelEmitterOn()) {
+ final AEPartLocation d = this.getSide();
+
+ final double d0 = d.xOffset * 0.45F + (r.nextFloat() - 0.5F) * 0.2D;
+ final double d1 = d.yOffset * 0.45F + (r.nextFloat() - 0.5F) * 0.2D;
+ final double d2 = d.zOffset * 0.45F + (r.nextFloat() - 0.5F) * 0.2D;
+
+ world.spawnParticle(EnumParticleTypes.REDSTONE, 0.5 + pos.getX() + d0, 0.5 + pos.getY() + d1, 0.5 + pos.getZ() + d2, 0.0D, 0.0D, 0.0D);
+ }
+ }
+
+ @Override
+ public boolean onPartActivate(final EntityPlayer player, final EnumHand hand, final Vec3d pos) {
+ if (Platform.isServer()) {
+ GuiHandler.openPartGui(player, this.getTile().getWorld(), this.getTile().getPos(), this.getSide().getFacing(), MkEGuis.GAS_LEVEL_EMITTER);
+ }
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public IPartModel getStaticModels() {
+ if (this.isActive() && this.isPowered()) {
+ return this.isLevelEmitterOn() ? MODEL_ON_HAS_CHANNEL : MODEL_OFF_HAS_CHANNEL;
+ } else if (this.isPowered()) {
+ return this.isLevelEmitterOn() ? MODEL_ON_ON : MODEL_OFF_ON;
+ } else {
+ return this.isLevelEmitterOn() ? MODEL_ON_OFF : MODEL_OFF_OFF;
+ }
+ }
+
+ public IGasInventory getConfig() {
+ return this.config;
+ }
+
+ @Override
+ public IGasInventory getGasInventoryByName(final String name) {
+ if (name.equals("config")) {
+ return this.config;
+ }
+ return null;
+ }
+
+ @Override
+ public void readFromNBT(final NBTTagCompound data) {
+ super.readFromNBT(data);
+ this.lastReportedValue = data.getLong("lastReportedValue");
+ this.reportingValue = data.getLong("reportingValue");
+ this.prevState = data.getBoolean("prevState");
+ this.config.load(data.getCompoundTag("config"));
+ }
+
+ @Override
+ public void writeToNBT(final NBTTagCompound data) {
+ super.writeToNBT(data);
+ data.setLong("lastReportedValue", this.lastReportedValue);
+ data.setLong("reportingValue", this.reportingValue);
+ data.setBoolean("prevState", this.prevState);
+ data.setTag("config", this.config.save());
+ }
+
+}
diff --git a/src/main/java/com/mekeng/github/common/part/PartGasStorageBus.java b/src/main/java/com/mekeng/github/common/part/PartGasStorageBus.java
new file mode 100644
index 0000000..65a4c81
--- /dev/null
+++ b/src/main/java/com/mekeng/github/common/part/PartGasStorageBus.java
@@ -0,0 +1,559 @@
+package com.mekeng.github.common.part;
+
+import appeng.api.AEApi;
+import appeng.api.config.AccessRestriction;
+import appeng.api.config.FuzzyMode;
+import appeng.api.config.IncludeExclude;
+import appeng.api.config.Settings;
+import appeng.api.config.StorageFilter;
+import appeng.api.config.Upgrades;
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.events.MENetworkCellArrayUpdate;
+import appeng.api.networking.events.MENetworkChannelsChanged;
+import appeng.api.networking.events.MENetworkEventSubscribe;
+import appeng.api.networking.events.MENetworkPowerStatusChange;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.networking.storage.IBaseMonitor;
+import appeng.api.networking.storage.IStorageGrid;
+import appeng.api.networking.ticking.IGridTickable;
+import appeng.api.networking.ticking.ITickManager;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.api.networking.ticking.TickingRequest;
+import appeng.api.parts.IPart;
+import appeng.api.parts.IPartCollisionHelper;
+import appeng.api.parts.IPartModel;
+import appeng.api.storage.ICellContainer;
+import appeng.api.storage.ICellInventory;
+import appeng.api.storage.IMEInventory;
+import appeng.api.storage.IMEInventoryHandler;
+import appeng.api.storage.IMEMonitorHandlerReceiver;
+import appeng.api.storage.IStorageChannel;
+import appeng.api.storage.IStorageMonitorable;
+import appeng.api.storage.IStorageMonitorableAccessor;
+import appeng.api.storage.data.IItemList;
+import appeng.api.util.AECableType;
+import appeng.api.util.IConfigManager;
+import appeng.capabilities.Capabilities;
+import appeng.core.sync.GuiBridge;
+import appeng.core.sync.GuiWrapper;
+import appeng.helpers.IPriorityHost;
+import appeng.items.parts.PartModels;
+import appeng.me.GridAccessException;
+import appeng.me.cache.GridStorageCache;
+import appeng.me.helpers.MachineSource;
+import appeng.me.storage.ITickingMonitor;
+import appeng.me.storage.MEInventoryHandler;
+import appeng.parts.PartModel;
+import appeng.tile.networking.TileCableBus;
+import appeng.util.ConfigManager;
+import appeng.util.Platform;
+import appeng.util.prioritylist.FuzzyPriorityList;
+import appeng.util.prioritylist.PrecisePriorityList;
+import com.mekeng.github.MekEng;
+import com.mekeng.github.common.ItemAndBlocks;
+import com.mekeng.github.common.container.handler.AEGuiBridge;
+import com.mekeng.github.common.container.handler.GuiHandler;
+import com.mekeng.github.common.container.handler.MkEGuis;
+import com.mekeng.github.common.me.GasTickRates;
+import com.mekeng.github.common.me.data.IAEGasStack;
+import com.mekeng.github.common.me.data.impl.AEGasStack;
+import com.mekeng.github.common.me.duality.IGasInterfaceHost;
+import com.mekeng.github.common.me.inventory.IConfigurableGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventory;
+import com.mekeng.github.common.me.inventory.IGasInventoryHost;
+import com.mekeng.github.common.me.inventory.impl.GasHandlerAdapter;
+import com.mekeng.github.common.me.inventory.impl.GasInventory;
+import com.mekeng.github.common.me.storage.IGasStorageChannel;
+import mekanism.api.gas.IGasHandler;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.IBlockAccess;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class PartGasStorageBus extends PartGasUpgradeable implements IGridTickable, ICellContainer, IMEMonitorHandlerReceiver, IGasInventoryHost, IConfigurableGasInventory, IPriorityHost {
+ public static final ResourceLocation MODEL_BASE = new ResourceLocation(MekEng.MODID, "part/gas_storage_bus_base");
+ @PartModels
+ public static final IPartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_storage_bus_off"));
+ @PartModels
+ public static final IPartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_storage_bus_on"));
+ @PartModels
+ public static final IPartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(MekEng.MODID, "part/gas_storage_bus_has_channel"));
+
+ private final IActionSource source;
+ private final GasInventory config = new GasInventory(63, this);
+ private int priority = 0;
+ private boolean cached = false;
+ private ITickingMonitor monitor = null;
+ private MEInventoryHandler handler = null;
+ private int handlerHash = 0;
+ private boolean wasActive = false;
+ private byte resetCacheLogic = 0;
+ private boolean accessChanged;
+ private boolean readOncePass;
+
+ public PartGasStorageBus(ItemStack is) {
+ super(is);
+ this.getConfigManager().registerSetting(Settings.ACCESS, AccessRestriction.READ_WRITE);
+ this.getConfigManager().registerSetting(Settings.FUZZY_MODE, FuzzyMode.IGNORE_ALL);
+ this.getConfigManager().registerSetting(Settings.STORAGE_FILTER, StorageFilter.EXTRACTABLE_ONLY);
+ this.source = new MachineSource(this);
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void powerRender(final MENetworkPowerStatusChange c) {
+ this.updateStatus();
+ }
+
+ protected void updateStatus() {
+ final boolean currentActive = this.getProxy().isActive();
+ if (this.wasActive != currentActive) {
+ this.wasActive = currentActive;
+ try {
+ this.getProxy().getGrid().postEvent(new MENetworkCellArrayUpdate());
+ this.getHost().markForUpdate();
+ } catch (final GridAccessException e) {
+ // :P
+ }
+ }
+ }
+
+ @Override
+ @MENetworkEventSubscribe
+ public void chanRender(final MENetworkChannelsChanged changedChannels) {
+ this.updateStatus();
+ }
+
+ @Override
+ protected int getUpgradeSlots() {
+ return 5;
+ }
+
+ @Override
+ public void updateSetting(final IConfigManager manager, final Enum settingName, final Enum newValue) {
+ if (settingName.name().equals("ACCESS")) {
+ this.accessChanged = true;
+ }
+ this.resetCache(true);
+ this.getHost().markForSave();
+ }
+
+ @Override
+ public void onGasInventoryChanged(IGasInventory inv, int slot) {
+ if (inv == this.config) {
+ this.resetCache(true);
+ }
+ }
+
+ @Override
+ public void upgradesChanged() {
+ super.upgradesChanged();
+ this.resetCache(true);
+ }
+
+ @Override
+ public void readFromNBT(final NBTTagCompound data) {
+ super.readFromNBT(data);
+ this.config.load(data.getCompoundTag("config"));
+ this.priority = data.getInteger("priority");
+ this.accessChanged = false;
+ }
+
+ @Override
+ public void writeToNBT(final NBTTagCompound data) {
+ super.writeToNBT(data);
+ data.setTag("config", this.config.save());
+ data.setInteger("priority", this.priority);
+ }
+
+ @Override
+ public IGasInventory getGasInventoryByName(final String name) {
+ if (name.equals("config")) {
+ return this.config;
+ }
+ return null;
+ }
+
+ protected void resetCache(final boolean fullReset) {
+ if (this.getHost() == null || this.getHost().getTile() == null || this.getHost().getTile().getWorld() == null || this.getHost().getTile().getWorld().isRemote) {
+ return;
+ }
+
+ if (fullReset) {
+ this.resetCacheLogic = 2;
+ } else if (this.resetCacheLogic < 2) {
+ this.resetCacheLogic = 1;
+ }
+
+ try {
+ this.getProxy().getTick().alertDevice(this.getProxy().getNode());
+ } catch (final GridAccessException e) {
+ // :P
+ }
+ }
+
+ @Override
+ public boolean isValid(final Object verificationToken) {
+ return this.handler == verificationToken;
+ }
+
+ @Override
+ public void postChange(final IBaseMonitor monitor, final Iterable change, final IActionSource source) {
+ if (this.getProxy().isActive()) {
+ Iterable filteredChanges = this.filterChanges(change);
+
+ AccessRestriction currentAccess = (AccessRestriction) this.getConfigManager().getSetting(Settings.ACCESS);
+ if (readOncePass) {
+ readOncePass = false;
+ try {
+ this.getProxy().getStorage().postAlterationOfStoredItems(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class), filteredChanges, this.source);
+ } catch (final GridAccessException e) {
+ // :(
+ }
+ return;
+ }
+ if (!currentAccess.hasPermission(AccessRestriction.READ)) {
+ return;
+ }
+ try {
+ this.getProxy().getStorage().postAlterationOfStoredItems(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class), filteredChanges, source);
+ } catch (final GridAccessException e) {
+ // :(
+ }
+ }
+ }
+
+ @Override
+ public void onListUpdate() {
+ // not used here.
+ }
+
+ @Override
+ public void getBoxes(final IPartCollisionHelper bch) {
+ bch.addBox(3, 3, 15, 13, 13, 16);
+ bch.addBox(2, 2, 14, 14, 14, 15);
+ bch.addBox(5, 5, 12, 11, 11, 14);
+ }
+
+ @Override
+ public void onNeighborChanged(IBlockAccess w, BlockPos pos, BlockPos neighbor) {
+ if (pos.offset(this.getSide().getFacing()).equals(neighbor)) {
+
+ final TileEntity te = w.getTileEntity(neighbor);
+
+ // In case the TE was destroyed, we have to do a full reset immediately.
+ if (te instanceof TileCableBus) {
+ IPart iPart = ((TileCableBus) te).getPart(this.getSide().getOpposite());
+ if (iPart == null || iPart instanceof IGasInterfaceHost) {
+ this.resetCache(true);
+ this.resetCache();
+ }
+ } else if (te == null || te instanceof IGasInterfaceHost) {
+ this.resetCache(true);
+ this.resetCache();
+ } else {
+ this.resetCache(false);
+ }
+ }
+ }
+
+ @Override
+ public float getCableConnectionLength(AECableType cable) {
+ return 4;
+ }
+
+ @Override
+ public boolean onPartActivate(final EntityPlayer player, final EnumHand hand, final Vec3d pos) {
+ if (Platform.isServer() && this.getActionableNode() != null) {
+ GuiHandler.openPartGui(player, this.getTile().getWorld(), this.getTile().getPos(), this.getSide().getFacing(), MkEGuis.GAS_STORAGE_BUS);
+ }
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public TickingRequest getTickingRequest(@Nonnull IGridNode node) {
+ return new TickingRequest(GasTickRates.GasStorageBus.getMin(), GasTickRates.GasStorageBus.getMax(), monitor == null, true);
+ }
+
+ @Nonnull
+ @Override
+ public TickRateModulation tickingRequest(@Nonnull IGridNode node, int ticksSinceLastCall) {
+ if (this.resetCacheLogic != 0) {
+ this.resetCache();
+ }
+
+ if (this.monitor != null) {
+ return this.monitor.onTick();
+ }
+
+ return TickRateModulation.SLEEP;
+ }
+
+ protected void resetCache() {
+ final boolean fullReset = this.resetCacheLogic == 2;
+ this.resetCacheLogic = 0;
+
+ final MEInventoryHandler