From a849fde6c4d27a109395dec2562a05eed5198a8b Mon Sep 17 00:00:00 2001 From: GlodBlock <1356392126@qq.com> Date: Thu, 30 May 2024 14:20:53 +0800 Subject: [PATCH] init --- .gitignore | 23 + LICENSE.txt | 165 +++++ README.md | 1 + build.gradle | 116 ++++ gradle.properties | 22 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 52271 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 +++++ gradlew.bat | 90 +++ src/api/java/baubles/api/BaubleType.java | 30 + src/api/java/baubles/api/BaublesApi.java | 40 ++ src/api/java/baubles/api/IBauble.java | 31 + .../baubles/api/cap/BaublesCapabilities.java | 41 ++ .../baubles/api/cap/IBaublesItemHandler.java | 19 + .../api/inv/BaublesInventoryWrapper.java | 100 +++ .../mousetweaks/api/IMTModGuiContainer2.java | 87 +++ .../mousetweaks/api/MouseTweaksIgnore.java | 14 + src/main/java/com/mekeng/github/MekEng.java | 54 ++ .../github/client/ClientRegistryHandler.java | 58 ++ .../mekeng/github/client/gui/GuiGasIO.java | 45 ++ .../github/client/gui/GuiGasInterface.java | 107 +++ .../GuiGasInterfaceConfigurationTerminal.java | 499 +++++++++++++ .../github/client/gui/GuiGasLevelEmitter.java | 176 +++++ .../github/client/gui/GuiGasStorageBus.java | 129 ++++ .../github/client/gui/GuiGasTerminal.java | 341 +++++++++ .../github/client/gui/GuiGasUpgradeable.java | 108 +++ .../client/gui/GuiMEPortableGasCell.java | 18 + .../client/gui/GuiWirelessGasTerminal.java | 40 ++ .../github/client/model/SpecialModel.java | 9 + .../github/client/render/DummyGasModel.java | 240 +++++++ .../client/render/GasStackSizeRenderer.java | 92 +++ .../mekeng/github/client/slots/SlotGas.java | 95 +++ .../mekeng/github/client/slots/SlotGasME.java | 74 ++ .../github/client/slots/SlotGasTank.java | 120 ++++ .../github/client/slots/SlotOptionalGas.java | 48 ++ .../mekeng/github/common/ItemAndBlocks.java | 81 +++ .../mekeng/github/common/RegistryHandler.java | 82 +++ .../common/block/BlockGasInterface.java | 41 ++ .../container/ContainerGasConfigurable.java | 110 +++ .../common/container/ContainerGasIO.java | 23 + .../container/ContainerGasInterface.java | 209 ++++++ ...inerGasInterfaceConfigurationTerminal.java | 251 +++++++ .../container/ContainerGasLevelEmitter.java | 77 +++ .../container/ContainerGasStorageBus.java | 152 ++++ .../container/ContainerGasTerminal.java | 490 +++++++++++++ .../container/ContainerMEPortableGasCell.java | 64 ++ .../ContainerWirelessGasTerminal.java | 198 ++++++ .../common/container/handler/AEGuiBridge.java | 53 ++ .../common/container/handler/GuiFactory.java | 109 +++ .../common/container/handler/GuiHandler.java | 62 ++ .../common/container/handler/MkEGuis.java | 143 ++++ .../container/sync/IGasSyncContainer.java | 11 + .../github/common/item/ItemDummyGas.java | 62 ++ .../github/common/item/ItemGasCell.java | 200 ++++++ .../github/common/item/ItemMkEPart.java | 50 ++ .../mekeng/github/common/item/ItemNormal.java | 7 + .../common/item/ItemPortableGasCell.java | 149 ++++ .../common/item/ItemWirelessGasTerminal.java | 22 + .../mekeng/github/common/me/GasTickRates.java | 25 + .../me/client/ClientDCInternalGasInv.java | 51 ++ .../github/common/me/client/GasRepo.java | 176 +++++ .../github/common/me/client/RepoSlot.java | 35 + .../github/common/me/data/IAEGasStack.java | 13 + .../common/me/data/impl/AEGasStack.java | 177 +++++ .../common/me/duality/IGasInterfaceHost.java | 22 + .../me/duality/impl/DualityGasInterface.java | 654 ++++++++++++++++++ .../inventory/IConfigurableGasInventory.java | 9 + .../me/inventory/IExtendedGasHandler.java | 11 + .../common/me/inventory/IGasInventory.java | 29 + .../me/inventory/IGasInventoryHost.java | 23 + .../me/inventory/impl/GasHandlerAdapter.java | 222 ++++++ .../me/inventory/impl/GasInvHandler.java | 138 ++++ .../me/inventory/impl/GasInventory.java | 169 +++++ .../common/me/storage/IGasStorageChannel.java | 8 + .../common/me/storage/IPortableGasCell.java | 11 + .../me/storage/impl/GasCellGuiHandler.java | 34 + .../common/me/storage/impl/GasList.java | 132 ++++ .../me/storage/impl/GasStorageChannel.java | 70 ++ .../me/storage/impl/MEMonitorIGasHandler.java | 219 ++++++ .../storage/impl/MeaningfulGasIterator.java | 39 ++ .../mekeng/github/common/part/IPartGroup.java | 7 + .../github/common/part/PartGasExportBus.java | 147 ++++ .../github/common/part/PartGasImportBus.java | 157 +++++ .../github/common/part/PartGasInterface.java | 224 ++++++ ...PartGasInterfaceConfigurationTerminal.java | 53 ++ .../common/part/PartGasLevelEmitter.java | 331 +++++++++ .../github/common/part/PartGasStorageBus.java | 559 +++++++++++++++ .../github/common/part/PartGasTerminal.java | 124 ++++ .../common/part/PartGasUpgradeable.java | 57 ++ .../github/common/part/PartSharedGasBus.java | 157 +++++ .../github/common/part/p2p/PartP2PGases.java | 259 +++++++ .../github/common/tile/TileGasInterface.java | 167 +++++ .../com/mekeng/github/core/CoreHooks.java | 110 +++ .../github/core/MkEClassTransformer.java | 57 ++ .../java/com/mekeng/github/core/MkECore.java | 41 ++ .../PartP2PTunnelTransformer.java | 63 ++ .../transformers/TileChestTransformer.java | 80 +++ .../TileEntityPressurizedTubeTransformer.java | 68 ++ .../com/mekeng/github/network/Packets.java | 31 + .../github/network/packet/CGasSlotSync.java | 80 +++ .../github/network/packet/CGenericPacket.java | 75 ++ .../github/network/packet/CSwitchGuis.java | 77 +++ .../github/network/packet/MkEMessage.java | 32 + .../github/network/packet/SGasSlotSync.java | 41 ++ .../github/network/packet/SGenericPacket.java | 75 ++ .../network/packet/SMEGasInventoryUpdate.java | 83 +++ .../network/packet/sync/IActionHolder.java | 17 + .../network/packet/sync/ParaSerializer.java | 116 ++++ .../github/network/packet/sync/Paras.java | 25 + .../com/mekeng/github/proxy/ClientProxy.java | 41 ++ .../com/mekeng/github/proxy/CommonProxy.java | 84 +++ .../com/mekeng/github/util/Ae2Reflect.java | 137 ++++ .../java/com/mekeng/github/util/Utils.java | 87 +++ .../github/util/helpers/GasCellConfig.java | 67 ++ .../github/util/helpers/GasCellInfo.java | 115 +++ .../github/util/helpers/GasSorters.java | 55 ++ .../github/util/helpers/GasSyncHelper.java | 89 +++ .../github/util/helpers/ItemGasHandler.java | 82 +++ .../util/helpers/PortableGasCellViewer.java | 110 +++ .../util/helpers/WirelessGasGuiObj.java | 352 ++++++++++ .../github/xmod/jei/GasCellGuiHandler.java | 60 ++ .../com/mekeng/github/xmod/jei/MkEJei.java | 24 + .../models/part/p2p/p2p_tunnel_gases.json | 6 + .../textures/guis/gas_bus.png | Bin 0 -> 1381 bytes .../mekeng/blockstates/gas_interface.json | 7 + .../resources/assets/mekeng/lang/en_us.lang | 38 + .../mekeng/models/block/gas_interface.json | 6 + .../mekeng/models/item/gas_cell_16k.json | 6 + .../mekeng/models/item/gas_cell_1k.json | 6 + .../mekeng/models/item/gas_cell_4k.json | 6 + .../mekeng/models/item/gas_cell_64k.json | 6 + .../mekeng/models/item/gas_core_16k.json | 6 + .../mekeng/models/item/gas_core_1k.json | 6 + .../mekeng/models/item/gas_core_4k.json | 6 + .../mekeng/models/item/gas_core_64k.json | 6 + .../mekeng/models/item/gas_export_bus.json | 6 + .../mekeng/models/item/gas_import_bus.json | 6 + .../mekeng/models/item/gas_interface.json | 3 + .../models/item/gas_interface_part.json | 8 + .../models/item/gas_interface_terminal.json | 9 + .../mekeng/models/item/gas_level_emitter.json | 3 + .../assets/mekeng/models/item/gas_p2p.json | 6 + .../mekeng/models/item/gas_storage_bus.json | 6 + .../mekeng/models/item/gas_terminal.json | 9 + .../mekeng/models/item/portable_gas_cell.json | 6 + .../models/item/wireless_gas_terminal.json | 6 + .../models/part/gas_export_bus_base.json | 6 + .../part/gas_export_bus_has_channel.json | 3 + .../models/part/gas_export_bus_off.json | 3 + .../mekeng/models/part/gas_export_bus_on.json | 3 + .../models/part/gas_import_bus_base.json | 6 + .../part/gas_import_bus_has_channel.json | 3 + .../models/part/gas_import_bus_off.json | 3 + .../mekeng/models/part/gas_import_bus_on.json | 3 + .../models/part/gas_interface_base.json | 9 + ..._interface_configuration_terminal_off.json | 8 + ...s_interface_configuration_terminal_on.json | 76 ++ .../part/gas_interface_has_channel.json | 3 + .../mekeng/models/part/gas_interface_off.json | 3 + .../mekeng/models/part/gas_interface_on.json | 3 + .../models/part/gas_storage_bus_base.json | 6 + .../part/gas_storage_bus_has_channel.json | 3 + .../models/part/gas_storage_bus_off.json | 3 + .../models/part/gas_storage_bus_on.json | 3 + .../mekeng/models/part/gas_terminal_off.json | 8 + .../mekeng/models/part/gas_terminal_on.json | 76 ++ .../assets/mekeng/recipes/_constants.json | 30 + .../assets/mekeng/recipes/cell/16k.json | 28 + .../assets/mekeng/recipes/cell/16k_asm.json | 15 + .../assets/mekeng/recipes/cell/16k_cell.json | 28 + .../assets/mekeng/recipes/cell/1k.json | 25 + .../assets/mekeng/recipes/cell/1k_asm.json | 15 + .../assets/mekeng/recipes/cell/1k_cell.json | 28 + .../assets/mekeng/recipes/cell/4k.json | 28 + .../assets/mekeng/recipes/cell/4k_asm.json | 15 + .../assets/mekeng/recipes/cell/4k_cell.json | 28 + .../assets/mekeng/recipes/cell/64k.json | 28 + .../assets/mekeng/recipes/cell/64k_asm.json | 15 + .../assets/mekeng/recipes/cell/64k_cell.json | 28 + .../assets/mekeng/recipes/cell/portable.json | 21 + .../recipes/gas_configuration_terminal.json | 15 + .../gas_configuration_terminal_revert.json | 12 + .../assets/mekeng/recipes/gas_export_bus.json | 27 + .../assets/mekeng/recipes/gas_import_bus.json | 27 + .../assets/mekeng/recipes/gas_interface.json | 30 + .../mekeng/recipes/gas_interface_alt.json | 11 + .../mekeng/recipes/gas_interface_part.json | 11 + .../recipes/gas_interface_terminal.json | 20 + .../mekeng/recipes/gas_level_emitter.json | 19 + .../mekeng/recipes/gas_storage_bus.json | 21 + .../mekeng/recipes/wireless_gas_terminal.json | 23 + .../mekeng/textures/blocks/gas_interface.png | Bin 0 -> 311 bytes .../mekeng/textures/items/gas_cell_16k.png | Bin 0 -> 289 bytes .../mekeng/textures/items/gas_cell_1k.png | Bin 0 -> 278 bytes .../mekeng/textures/items/gas_cell_4k.png | Bin 0 -> 285 bytes .../mekeng/textures/items/gas_cell_64k.png | Bin 0 -> 259 bytes .../mekeng/textures/items/gas_core_16k.png | Bin 0 -> 226 bytes .../mekeng/textures/items/gas_core_1k.png | Bin 0 -> 205 bytes .../mekeng/textures/items/gas_core_4k.png | Bin 0 -> 220 bytes .../mekeng/textures/items/gas_core_64k.png | Bin 0 -> 304 bytes .../textures/items/portable_gas_cell.png | Bin 0 -> 337 bytes .../textures/items/wireless_gas_terminal.png | Bin 0 -> 325 bytes .../mekeng/textures/parts/gas_storage_bus.png | Bin 0 -> 242 bytes .../parts/gas_terminal/lights_bright.png | Bin 0 -> 147 bytes .../parts/gas_terminal/lights_dark.png | Bin 0 -> 203 bytes .../parts/gas_terminal/lights_medium.png | Bin 0 -> 153 bytes .../mekeng/textures/parts/interface/back.png | Bin 0 -> 374 bytes .../mekeng/textures/parts/interface/front.png | Bin 0 -> 197 bytes .../mekeng/textures/parts/interface/sides.png | Bin 0 -> 411 bytes .../parts/interface_terminal/bright.png | Bin 0 -> 142 bytes .../parts/interface_terminal/dark.png | Bin 0 -> 205 bytes .../parts/interface_terminal/medium.png | Bin 0 -> 128 bytes .../textures/parts/io_bus/gas_export_bus.png | Bin 0 -> 212 bytes .../textures/parts/io_bus/gas_import_bus.png | Bin 0 -> 224 bytes .../mekeng/textures/parts/p2p/gases_p2p.png | Bin 0 -> 90 bytes src/main/resources/mcmod.info | 16 + src/main/resources/pack.mcmeta | 7 + 217 files changed, 13706 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 build.gradle create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 src/api/java/baubles/api/BaubleType.java create mode 100644 src/api/java/baubles/api/BaublesApi.java create mode 100644 src/api/java/baubles/api/IBauble.java create mode 100644 src/api/java/baubles/api/cap/BaublesCapabilities.java create mode 100644 src/api/java/baubles/api/cap/IBaublesItemHandler.java create mode 100644 src/api/java/baubles/api/inv/BaublesInventoryWrapper.java create mode 100644 src/api/java/yalter/mousetweaks/api/IMTModGuiContainer2.java create mode 100644 src/api/java/yalter/mousetweaks/api/MouseTweaksIgnore.java create mode 100644 src/main/java/com/mekeng/github/MekEng.java create mode 100644 src/main/java/com/mekeng/github/client/ClientRegistryHandler.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasIO.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasInterface.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasInterfaceConfigurationTerminal.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasLevelEmitter.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasStorageBus.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiGasUpgradeable.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiMEPortableGasCell.java create mode 100644 src/main/java/com/mekeng/github/client/gui/GuiWirelessGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/client/model/SpecialModel.java create mode 100644 src/main/java/com/mekeng/github/client/render/DummyGasModel.java create mode 100644 src/main/java/com/mekeng/github/client/render/GasStackSizeRenderer.java create mode 100644 src/main/java/com/mekeng/github/client/slots/SlotGas.java create mode 100644 src/main/java/com/mekeng/github/client/slots/SlotGasME.java create mode 100644 src/main/java/com/mekeng/github/client/slots/SlotGasTank.java create mode 100644 src/main/java/com/mekeng/github/client/slots/SlotOptionalGas.java create mode 100644 src/main/java/com/mekeng/github/common/ItemAndBlocks.java create mode 100644 src/main/java/com/mekeng/github/common/RegistryHandler.java create mode 100644 src/main/java/com/mekeng/github/common/block/BlockGasInterface.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasConfigurable.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasIO.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasInterface.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasInterfaceConfigurationTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasLevelEmitter.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasStorageBus.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerMEPortableGasCell.java create mode 100644 src/main/java/com/mekeng/github/common/container/ContainerWirelessGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/container/handler/AEGuiBridge.java create mode 100644 src/main/java/com/mekeng/github/common/container/handler/GuiFactory.java create mode 100644 src/main/java/com/mekeng/github/common/container/handler/GuiHandler.java create mode 100644 src/main/java/com/mekeng/github/common/container/handler/MkEGuis.java create mode 100644 src/main/java/com/mekeng/github/common/container/sync/IGasSyncContainer.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemDummyGas.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemGasCell.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemMkEPart.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemNormal.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemPortableGasCell.java create mode 100644 src/main/java/com/mekeng/github/common/item/ItemWirelessGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/me/GasTickRates.java create mode 100644 src/main/java/com/mekeng/github/common/me/client/ClientDCInternalGasInv.java create mode 100644 src/main/java/com/mekeng/github/common/me/client/GasRepo.java create mode 100644 src/main/java/com/mekeng/github/common/me/client/RepoSlot.java create mode 100644 src/main/java/com/mekeng/github/common/me/data/IAEGasStack.java create mode 100644 src/main/java/com/mekeng/github/common/me/data/impl/AEGasStack.java create mode 100644 src/main/java/com/mekeng/github/common/me/duality/IGasInterfaceHost.java create mode 100644 src/main/java/com/mekeng/github/common/me/duality/impl/DualityGasInterface.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/IConfigurableGasInventory.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/IExtendedGasHandler.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/IGasInventory.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/IGasInventoryHost.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/impl/GasHandlerAdapter.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/impl/GasInvHandler.java create mode 100644 src/main/java/com/mekeng/github/common/me/inventory/impl/GasInventory.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/IGasStorageChannel.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/IPortableGasCell.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/impl/GasCellGuiHandler.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/impl/GasList.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/impl/GasStorageChannel.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/impl/MEMonitorIGasHandler.java create mode 100644 src/main/java/com/mekeng/github/common/me/storage/impl/MeaningfulGasIterator.java create mode 100644 src/main/java/com/mekeng/github/common/part/IPartGroup.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasExportBus.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasImportBus.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasInterface.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasInterfaceConfigurationTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasLevelEmitter.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasStorageBus.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasTerminal.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartGasUpgradeable.java create mode 100644 src/main/java/com/mekeng/github/common/part/PartSharedGasBus.java create mode 100644 src/main/java/com/mekeng/github/common/part/p2p/PartP2PGases.java create mode 100644 src/main/java/com/mekeng/github/common/tile/TileGasInterface.java create mode 100644 src/main/java/com/mekeng/github/core/CoreHooks.java create mode 100644 src/main/java/com/mekeng/github/core/MkEClassTransformer.java create mode 100644 src/main/java/com/mekeng/github/core/MkECore.java create mode 100644 src/main/java/com/mekeng/github/core/transformers/PartP2PTunnelTransformer.java create mode 100644 src/main/java/com/mekeng/github/core/transformers/TileChestTransformer.java create mode 100644 src/main/java/com/mekeng/github/core/transformers/TileEntityPressurizedTubeTransformer.java create mode 100644 src/main/java/com/mekeng/github/network/Packets.java create mode 100644 src/main/java/com/mekeng/github/network/packet/CGasSlotSync.java create mode 100644 src/main/java/com/mekeng/github/network/packet/CGenericPacket.java create mode 100644 src/main/java/com/mekeng/github/network/packet/CSwitchGuis.java create mode 100644 src/main/java/com/mekeng/github/network/packet/MkEMessage.java create mode 100644 src/main/java/com/mekeng/github/network/packet/SGasSlotSync.java create mode 100644 src/main/java/com/mekeng/github/network/packet/SGenericPacket.java create mode 100644 src/main/java/com/mekeng/github/network/packet/SMEGasInventoryUpdate.java create mode 100644 src/main/java/com/mekeng/github/network/packet/sync/IActionHolder.java create mode 100644 src/main/java/com/mekeng/github/network/packet/sync/ParaSerializer.java create mode 100644 src/main/java/com/mekeng/github/network/packet/sync/Paras.java create mode 100644 src/main/java/com/mekeng/github/proxy/ClientProxy.java create mode 100644 src/main/java/com/mekeng/github/proxy/CommonProxy.java create mode 100644 src/main/java/com/mekeng/github/util/Ae2Reflect.java create mode 100644 src/main/java/com/mekeng/github/util/Utils.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/GasCellConfig.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/GasCellInfo.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/GasSorters.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/GasSyncHelper.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/ItemGasHandler.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/PortableGasCellViewer.java create mode 100644 src/main/java/com/mekeng/github/util/helpers/WirelessGasGuiObj.java create mode 100644 src/main/java/com/mekeng/github/xmod/jei/GasCellGuiHandler.java create mode 100644 src/main/java/com/mekeng/github/xmod/jei/MkEJei.java create mode 100644 src/main/resources/assets/appliedenergistics2/models/part/p2p/p2p_tunnel_gases.json create mode 100644 src/main/resources/assets/appliedenergistics2/textures/guis/gas_bus.png create mode 100644 src/main/resources/assets/mekeng/blockstates/gas_interface.json create mode 100644 src/main/resources/assets/mekeng/lang/en_us.lang create mode 100644 src/main/resources/assets/mekeng/models/block/gas_interface.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_cell_16k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_cell_1k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_cell_4k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_cell_64k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_core_16k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_core_1k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_core_4k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_core_64k.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_export_bus.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_import_bus.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_interface.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_interface_part.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_interface_terminal.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_level_emitter.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_p2p.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_storage_bus.json create mode 100644 src/main/resources/assets/mekeng/models/item/gas_terminal.json create mode 100644 src/main/resources/assets/mekeng/models/item/portable_gas_cell.json create mode 100644 src/main/resources/assets/mekeng/models/item/wireless_gas_terminal.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_export_bus_base.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_export_bus_has_channel.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_export_bus_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_export_bus_on.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_import_bus_base.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_import_bus_has_channel.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_import_bus_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_import_bus_on.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_base.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_on.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_has_channel.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_interface_on.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_storage_bus_base.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_storage_bus_has_channel.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_storage_bus_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_storage_bus_on.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_terminal_off.json create mode 100644 src/main/resources/assets/mekeng/models/part/gas_terminal_on.json create mode 100644 src/main/resources/assets/mekeng/recipes/_constants.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/16k.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/16k_asm.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/16k_cell.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/1k.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/1k_asm.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/1k_cell.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/4k.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/4k_asm.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/4k_cell.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/64k.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/64k_asm.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/64k_cell.json create mode 100644 src/main/resources/assets/mekeng/recipes/cell/portable.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_configuration_terminal.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_configuration_terminal_revert.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_export_bus.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_import_bus.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_interface.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_interface_alt.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_interface_part.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_interface_terminal.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_level_emitter.json create mode 100644 src/main/resources/assets/mekeng/recipes/gas_storage_bus.json create mode 100644 src/main/resources/assets/mekeng/recipes/wireless_gas_terminal.json create mode 100644 src/main/resources/assets/mekeng/textures/blocks/gas_interface.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_cell_16k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_cell_1k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_cell_4k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_cell_64k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_core_16k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_core_1k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_core_4k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/gas_core_64k.png create mode 100644 src/main/resources/assets/mekeng/textures/items/portable_gas_cell.png create mode 100644 src/main/resources/assets/mekeng/textures/items/wireless_gas_terminal.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/gas_storage_bus.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_bright.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_dark.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_medium.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface/back.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface/front.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface/sides.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface_terminal/bright.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface_terminal/dark.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/interface_terminal/medium.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/io_bus/gas_export_bus.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/io_bus/gas_import_bus.png create mode 100644 src/main/resources/assets/mekeng/textures/parts/p2p/gases_p2p.png create mode 100644 src/main/resources/mcmod.info create mode 100644 src/main/resources/pack.mcmeta diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a74c99f --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# eclipse +bin +*.launch +.settings +.metadata +.classpath +.project + +# idea +out +*.ipr +*.iws +*.iml +.idea + +# gradle +build +.gradle + +# other +eclipse +run + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0a04128 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0d4ed4 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Mek gas support with AE \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..9fbdb94 --- /dev/null +++ b/build.gradle @@ -0,0 +1,116 @@ +buildscript { + repositories { + maven { + url = 'https://maven.minecraftforge.net' + } + maven { + url = 'https://repo.spongepowered.org/maven' + } + } + dependencies { + classpath 'net.minecraftforge.gradle:ForgeGradle:2.3.4' + if (project.use_mixins.toBoolean()) { + classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT' + } + } +} + +apply plugin: 'net.minecraftforge.gradle.forge' + +if (project.use_mixins.toBoolean()) { + apply plugin: 'org.spongepowered.mixin' +} + +version = project.mod_version +group = project.maven_group +archivesBaseName = "MekanismEnergistics" + +sourceCompatibility = targetCompatibility = '1.8' + +compileJava { + sourceCompatibility = targetCompatibility = '1.8' +} + +configurations { + embed + implementation.extendsFrom(embed) +} + +minecraft { + version = '1.12.2-14.23.5.2847' + runDir = 'run' + mappings = 'stable_39' + def args = [] + if (project.use_coremod.toBoolean()) { + args << '-Dfml.coreMods.load=' + coremod_plugin_class_name + } + if (project.use_mixins.toBoolean()) { + args << '-Dmixin.hotSwap=true' + args << '-Dmixin.checks.interfaces=true' + args << '-Dmixin.debug.export=true' + } + clientJvmArgs.addAll(args) + serverJvmArgs.addAll(args) +} + +repositories { + maven { + url = 'https://maven.cleanroommc.com' + } + maven { + url = 'https://repo.spongepowered.org/maven' + } + maven { url 'https://jitpack.io/' } + maven { url 'https://cursemaven.com/' } + maven { url 'https://minecraft.curseforge.com/api/maven/' } +} + +dependencies { + deobfCompile "curse.maven:jei-238222:4538010" //jei + deobfCompile 'curse.maven:ae2-extended-life-570458:5378163' //pae2 + deobfCompile "curse.maven:mekanism-ce-399904:4804509" //mek-ce + compileOnly "curse.maven:gregtech-ce-unofficial-557242:5050979" // gt api +} + +sourceSets { + main { + ext.refMap = 'mixins.' + archives_base_name + '.refmap.json' + } +} + +processResources { + // this will ensure that this task is redone when the versions change. + inputs.property 'version', project.version + inputs.property 'mcversion', project.minecraft.version + // replace stuff in mcmod.info, nothing else + from(sourceSets.main.resources.srcDirs) { + include 'mcmod.info' + // replace version and mcversion + expand 'version':project.version, 'mcversion':project.minecraft.version + } + // copy everything else except the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } + + if (project.use_access_transformer.toBoolean()) { + rename '(.+_at.cfg)', 'META-INF/$1' // Access Transformers + } +} + +jar { + manifest { + def attribute_map = [:] + if (project.use_coremod.toBoolean()) { + attribute_map['FMLCorePlugin'] = project.coremod_plugin_class_name + if (project.include_mod.toBoolean()) { + attribute_map['FMLCorePluginContainsFMLMod'] = true + attribute_map['ForceLoadAsMod'] = project.gradle.startParameter.taskNames[0] == "build" + } + } + if (project.use_access_transformer.toBoolean()) { + attribute_map['FMLAT'] = project.archives_base_name + '_at.cfg' + } + attributes(attribute_map) + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..26a9d0c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,22 @@ +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs = -Xmx3G + +# Mod Information +mod_version = 0.1.0 +maven_group = com.mekeng.github +archives_base_name = mekeng + +# If any properties changes below this line, run `gradlew setupDecompWorkspace` and refresh gradle again to ensure everything is working correctly. + +# Boilerplate Options +use_mixins = false +use_coremod = true +use_assetmover = false + +# Access Transformer files should be in the root of `resources` folder and with the filename formatted as: `{archives_base_name}_at.cfg` +use_access_transformer = false + +# Coremod Arguments +include_mod = true +coremod_plugin_class_name = com.mekeng.github.core.MkECore \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..30d399d8d2bf522ff5de94bf434a7cc43a9a74b5 GIT binary patch literal 52271 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 p \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/api/java/baubles/api/BaubleType.java b/src/api/java/baubles/api/BaubleType.java new file mode 100644 index 0000000..5209c39 --- /dev/null +++ b/src/api/java/baubles/api/BaubleType.java @@ -0,0 +1,30 @@ +package baubles.api; + +public enum BaubleType { + AMULET(0), + RING(1, 2), + BELT(3), + TRINKET(0, 1, 2, 3, 4, 5, 6), + HEAD(4), + BODY(5), + CHARM(6); + + final int[] validSlots; + + BaubleType(int... validSlots) { + this.validSlots = validSlots; + } + + public boolean hasSlot(int slot) { + for (int s : this.validSlots) { + if (s == slot) { + return true; + } + } + return false; + } + + public int[] getValidSlots() { + return this.validSlots; + } +} diff --git a/src/api/java/baubles/api/BaublesApi.java b/src/api/java/baubles/api/BaublesApi.java new file mode 100644 index 0000000..26d7993 --- /dev/null +++ b/src/api/java/baubles/api/BaublesApi.java @@ -0,0 +1,40 @@ +package baubles.api; + +import baubles.api.cap.BaublesCapabilities; +import baubles.api.cap.IBaublesItemHandler; +import baubles.api.inv.BaublesInventoryWrapper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.util.EnumFacing; + +public class BaublesApi { + public BaublesApi() { + } + + public static IBaublesItemHandler getBaublesHandler(EntityPlayer player) { + IBaublesItemHandler handler = (IBaublesItemHandler)player.getCapability(BaublesCapabilities.CAPABILITY_BAUBLES, (EnumFacing)null); + handler.setPlayer(player); + return handler; + } + + /** @deprecated */ + @Deprecated + public static IInventory getBaubles(EntityPlayer player) { + IBaublesItemHandler handler = (IBaublesItemHandler)player.getCapability(BaublesCapabilities.CAPABILITY_BAUBLES, (EnumFacing)null); + handler.setPlayer(player); + return new BaublesInventoryWrapper(handler, player); + } + + public static int isBaubleEquipped(EntityPlayer player, Item bauble) { + IBaublesItemHandler handler = getBaublesHandler(player); + + for(int a = 0; a < handler.getSlots(); ++a) { + if (!handler.getStackInSlot(a).isEmpty() && handler.getStackInSlot(a).getItem() == bauble) { + return a; + } + } + + return -1; + } +} diff --git a/src/api/java/baubles/api/IBauble.java b/src/api/java/baubles/api/IBauble.java new file mode 100644 index 0000000..24d60a6 --- /dev/null +++ b/src/api/java/baubles/api/IBauble.java @@ -0,0 +1,31 @@ +package baubles.api; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; + +public interface IBauble { + + BaubleType getBaubleType(ItemStack var1); + + default void onWornTick(ItemStack itemstack, EntityLivingBase player) { + } + + default void onEquipped(ItemStack itemstack, EntityLivingBase player) { + } + + default void onUnequipped(ItemStack itemstack, EntityLivingBase player) { + } + + default boolean canEquip(ItemStack itemstack, EntityLivingBase player) { + return true; + } + + default boolean canUnequip(ItemStack itemstack, EntityLivingBase player) { + return true; + } + + default boolean willAutoSync(ItemStack itemstack, EntityLivingBase player) { + return false; + } + +} diff --git a/src/api/java/baubles/api/cap/BaublesCapabilities.java b/src/api/java/baubles/api/cap/BaublesCapabilities.java new file mode 100644 index 0000000..57a3c86 --- /dev/null +++ b/src/api/java/baubles/api/cap/BaublesCapabilities.java @@ -0,0 +1,41 @@ +package baubles.api.cap; + +import baubles.api.IBauble; +import net.minecraft.nbt.NBTBase; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; + +public class BaublesCapabilities { + @CapabilityInject(IBaublesItemHandler.class) + public static final Capability CAPABILITY_BAUBLES = null; + @CapabilityInject(IBauble.class) + public static final Capability CAPABILITY_ITEM_BAUBLE = null; + + public BaublesCapabilities() { + } + + public static class CapabilityItemBaubleStorage implements Capability.IStorage { + public CapabilityItemBaubleStorage() { + } + + public NBTBase writeNBT(Capability capability, IBauble instance, EnumFacing side) { + return null; + } + + public void readNBT(Capability capability, IBauble instance, EnumFacing side, NBTBase nbt) { + } + } + + public static class CapabilityBaubles implements Capability.IStorage { + public CapabilityBaubles() { + } + + public NBTBase writeNBT(Capability capability, IBaublesItemHandler instance, EnumFacing side) { + return null; + } + + public void readNBT(Capability capability, IBaublesItemHandler instance, EnumFacing side, NBTBase nbt) { + } + } +} diff --git a/src/api/java/baubles/api/cap/IBaublesItemHandler.java b/src/api/java/baubles/api/cap/IBaublesItemHandler.java new file mode 100644 index 0000000..e1b6e79 --- /dev/null +++ b/src/api/java/baubles/api/cap/IBaublesItemHandler.java @@ -0,0 +1,19 @@ +package baubles.api.cap; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.IItemHandlerModifiable; + +public interface IBaublesItemHandler extends IItemHandlerModifiable { + boolean isItemValidForSlot(int var1, ItemStack var2, EntityLivingBase var3); + + boolean isEventBlocked(); + + void setEventBlock(boolean var1); + + boolean isChanged(int var1); + + void setChanged(int var1, boolean var2); + + void setPlayer(EntityLivingBase var1); +} \ No newline at end of file diff --git a/src/api/java/baubles/api/inv/BaublesInventoryWrapper.java b/src/api/java/baubles/api/inv/BaublesInventoryWrapper.java new file mode 100644 index 0000000..4389d90 --- /dev/null +++ b/src/api/java/baubles/api/inv/BaublesInventoryWrapper.java @@ -0,0 +1,100 @@ +package baubles.api.inv; + +import baubles.api.cap.IBaublesItemHandler; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.TextComponentString; + +public class BaublesInventoryWrapper implements IInventory { + final IBaublesItemHandler handler; + final EntityPlayer player; + + public BaublesInventoryWrapper(IBaublesItemHandler handler) { + this.handler = handler; + this.player = null; + } + + public BaublesInventoryWrapper(IBaublesItemHandler handler, EntityPlayer player) { + this.handler = handler; + this.player = player; + } + + public String getName() { + return "BaublesInventory"; + } + + public boolean hasCustomName() { + return false; + } + + public ITextComponent getDisplayName() { + return new TextComponentString(this.getName()); + } + + public int getSizeInventory() { + return this.handler.getSlots(); + } + + public boolean isEmpty() { + return false; + } + + public ItemStack getStackInSlot(int index) { + return this.handler.getStackInSlot(index); + } + + public ItemStack decrStackSize(int index, int count) { + return this.handler.extractItem(index, count, false); + } + + public ItemStack removeStackFromSlot(int index) { + ItemStack out = this.getStackInSlot(index); + this.handler.setStackInSlot(index, ItemStack.EMPTY); + return out; + } + + public void setInventorySlotContents(int index, ItemStack stack) { + this.handler.setStackInSlot(index, stack); + } + + public int getInventoryStackLimit() { + return 64; + } + + public void markDirty() { + } + + public boolean isUsableByPlayer(EntityPlayer player) { + return true; + } + + public void openInventory(EntityPlayer player) { + } + + public void closeInventory(EntityPlayer player) { + } + + public boolean isItemValidForSlot(int index, ItemStack stack) { + return this.handler.isItemValidForSlot(index, stack, this.player); + } + + public int getField(int id) { + return 0; + } + + public void setField(int id, int value) { + } + + public int getFieldCount() { + return 0; + } + + public void clear() { + for(int i = 0; i < this.getSizeInventory(); ++i) { + this.setInventorySlotContents(i, ItemStack.EMPTY); + } + + } +} \ No newline at end of file diff --git a/src/api/java/yalter/mousetweaks/api/IMTModGuiContainer2.java b/src/api/java/yalter/mousetweaks/api/IMTModGuiContainer2.java new file mode 100644 index 0000000..a05494c --- /dev/null +++ b/src/api/java/yalter/mousetweaks/api/IMTModGuiContainer2.java @@ -0,0 +1,87 @@ +package yalter.mousetweaks.api; + +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; + +/** + * This is the interface you want to implement in your GuiScreen to make it compatible with Mouse Tweaks. + * If this interface is not enough (for example, you need a custom slot click function, or if you use a custom Container + * which happens to be incompatible), check IMTModGuiContainer2Ex instead. + * If you just need to disable Mouse Tweaks or the wheel tweak, see the MouseTweaksIgnore + * or the MouseTweaksDisableWheelTweak annotations. + */ +public interface IMTModGuiContainer2 { + /** + * If you want to disable Mouse Tweaks in your GuiScreen, return true from this method. + * + * @return True if Mouse Tweaks should be disabled, false otherwise. + */ + boolean MT_isMouseTweaksDisabled(); + + /** + * If you want to disable the Wheel Tweak in your GuiScreen, return true from this method. + * + * @return True if the Wheel Tweak should be disabled, false otherwise. + */ + boolean MT_isWheelTweakDisabled(); + + /** + * Returns the Container. + * + * @return Container that is currently in use. + */ + Container MT_getContainer(); + + /** + * Returns the Slot that is currently selected by the player, or null if no Slot is selected. + * + * @return Slot that is located under the mouse, or null if no Slot it currently under the mouse. + */ + Slot MT_getSlotUnderMouse(); + + /** + * Return true if the given Slot behaves like the vanilla crafting output slots (inside the crafting table, + * or the furnace output slot, or the anvil output slot, etc.). These slots are handled differently by Mouse Tweaks. + * + * @param slot the slot to check + * @return True if slot is a crafting output slot. + */ + boolean MT_isCraftingOutput(Slot slot); + + /** + * Return true if the given Slot should be ignored by Mouse Tweaks. Examples of ignored slots are the item select + * slots and the Destroy Item slot in the vanilla creative inventory. + * + * @param slot the slot to check + * @return Tru if slot should be ignored by Mouse Tweaks. + */ + boolean MT_isIgnored(Slot slot); + + /** + * If your container has an RMB dragging functionality (like vanilla containers), disable it inside this method. + * This method is called every frame (render tick), which is after all mouseClicked / mouseClickMove / mouseReleased + * events are handled (although note these events are handled every game tick, which is far less frequent than every + * render tick).

+ * + * If true is returned from this method, Mouse Tweaks (after checking other conditions like isIgnored) will click + * the slot on which the right mouse button was initially pressed (in most cases this is the slot currently under + * mouse). This is needed because the vanilla RMB dragging functionality prevents the initial slot click.

+ * + * For vanilla containers this method looks like this: + *
+     * this.ignoreMouseUp = true;
+     *
+     * if (this.dragSplitting) {
+     *     if (this.dragSplittingButton == 1) {
+     *         this.dragSplitting = false;
+     *         return true;
+     *     }
+     * }
+     *
+     * return false;
+     * 
+ * + * @return True if Mouse Tweaks should click the slot on which the RMB was pressed. + */ + boolean MT_disableRMBDraggingFunctionality(); +} \ No newline at end of file diff --git a/src/api/java/yalter/mousetweaks/api/MouseTweaksIgnore.java b/src/api/java/yalter/mousetweaks/api/MouseTweaksIgnore.java new file mode 100644 index 0000000..8cbb32d --- /dev/null +++ b/src/api/java/yalter/mousetweaks/api/MouseTweaksIgnore.java @@ -0,0 +1,14 @@ +package yalter.mousetweaks.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Put this on your GuiScreen to disable Mouse Tweaks. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface MouseTweaksIgnore { +} diff --git a/src/main/java/com/mekeng/github/MekEng.java b/src/main/java/com/mekeng/github/MekEng.java new file mode 100644 index 0000000..3b638a3 --- /dev/null +++ b/src/main/java/com/mekeng/github/MekEng.java @@ -0,0 +1,54 @@ +package com.mekeng.github; + +import appeng.api.config.TunnelType; +import com.mekeng.github.proxy.CommonProxy; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.util.EnumHelper; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.SidedProxy; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import org.apache.logging.log4j.Logger; + +@Mod(modid = MekEng.MODID, useMetadata = true, dependencies = "required-after:appliedenergistics2@[v0.56.5,);required-after:mekanism") +public class MekEng { + + public static final TunnelType GAS; + + static { + // add P2P type + GAS = EnumHelper.addEnum(TunnelType.class, "GAS", new Class[0]); + } + + public static final String MODID = "mekeng"; + + @Mod.Instance(MODID) + public static MekEng INSTANCE; + + @SidedProxy(clientSide = "com.mekeng.github.proxy.ClientProxy", serverSide = "com.mekeng.github.proxy.CommonProxy") + public static CommonProxy proxy; + + public static Logger log; + + @Mod.EventHandler + public void onPreInit(FMLPreInitializationEvent event) { + log = event.getModLog(); + proxy.preInit(event); + } + + @Mod.EventHandler + public void onInit(FMLInitializationEvent event) { + proxy.init(event); + } + + @Mod.EventHandler + public void onPostInit(FMLPostInitializationEvent event) { + proxy.postInit(event); + } + + public static ResourceLocation id(String path) { + return new ResourceLocation(MODID, path); + } + +} diff --git a/src/main/java/com/mekeng/github/client/ClientRegistryHandler.java b/src/main/java/com/mekeng/github/client/ClientRegistryHandler.java new file mode 100644 index 0000000..c05b72f --- /dev/null +++ b/src/main/java/com/mekeng/github/client/ClientRegistryHandler.java @@ -0,0 +1,58 @@ +package com.mekeng.github.client; + +import appeng.api.AEApi; +import com.mekeng.github.MekEng; +import com.mekeng.github.client.model.SpecialModel; +import com.mekeng.github.client.render.DummyGasModel; +import com.mekeng.github.common.RegistryHandler; +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.PartGasStorageBus; +import com.mekeng.github.common.part.PartGasTerminal; +import com.mekeng.github.common.part.p2p.PartP2PGases; +import net.minecraft.block.Block; +import net.minecraft.client.renderer.block.model.ModelResourceLocation; +import net.minecraft.item.Item; +import net.minecraftforge.client.event.ModelRegistryEvent; +import net.minecraftforge.client.model.ModelLoader; +import net.minecraftforge.client.model.ModelLoaderRegistry; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import org.apache.commons.lang3.tuple.Pair; + +public class ClientRegistryHandler extends RegistryHandler { + + @SubscribeEvent + public void onRegisterModels(ModelRegistryEvent event) { + ModelLoaderRegistry.registerLoader(new DummyGasModel.Loader()); + for (Pair entry : blocks) { + registerModel(entry.getLeft(), Item.getItemFromBlock(entry.getRight())); + } + for (Pair entry : items) { + registerModel(entry.getLeft(), entry.getRight()); + } + AEApi.instance().registries().partModels().registerModels(PartGasTerminal.MODEL_ON); + AEApi.instance().registries().partModels().registerModels(PartGasTerminal.MODEL_OFF); + AEApi.instance().registries().partModels().registerModels(PartGasImportBus.MODELS_ON.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasImportBus.MODELS_OFF.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasImportBus.MODELS_HAS_CHANNEL.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasExportBus.MODELS_ON.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasExportBus.MODELS_OFF.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasExportBus.MODELS_HAS_CHANNEL.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasInterface.MODELS_ON.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasInterface.MODELS_OFF.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasInterface.MODELS_HAS_CHANNEL.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasStorageBus.MODELS_ON.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasStorageBus.MODELS_OFF.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasStorageBus.MODELS_HAS_CHANNEL.getModels()); + AEApi.instance().registries().partModels().registerModels(PartGasInterfaceConfigurationTerminal.MODEL_ON); + AEApi.instance().registries().partModels().registerModels(PartGasInterfaceConfigurationTerminal.MODEL_OFF); + AEApi.instance().registries().partModels().registerModels(PartP2PGases.getModels()); + } + + private static void registerModel(String key, Item item) { + ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(item instanceof SpecialModel ? ((SpecialModel) item).getModelPath() : MekEng.id(key), "inventory")); + } + +} diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasIO.java b/src/main/java/com/mekeng/github/client/gui/GuiGasIO.java new file mode 100644 index 0000000..fe4f4f9 --- /dev/null +++ b/src/main/java/com/mekeng/github/client/gui/GuiGasIO.java @@ -0,0 +1,45 @@ +package com.mekeng.github.client.gui; + +import com.mekeng.github.client.slots.SlotGas; +import com.mekeng.github.client.slots.SlotOptionalGas; +import com.mekeng.github.common.container.ContainerGasIO; +import com.mekeng.github.common.me.inventory.IGasInventory; +import com.mekeng.github.common.part.PartSharedGasBus; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; + +public class GuiGasIO extends GuiGasUpgradeable { + private final PartSharedGasBus bus; + + public GuiGasIO(InventoryPlayer inventoryPlayer, PartSharedGasBus te) { + super(new ContainerGasIO(inventoryPlayer, te)); + this.bus = te; + } + + @Override + public void initGui() { + super.initGui(); + + final ContainerGasIO container = (ContainerGasIO) this.inventorySlots; + final IGasInventory inv = this.bus.getConfig(); + final int y = 40; + final int x = 80; + + this.guiSlots.add(new SlotGas(inv, 0, 0, x, y)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 1, 1, 1, x, y, -1, 0)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 2, 2, 1, x, y, 1, 0)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 3, 3, 1, x, y, 0, -1)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 4, 4, 1, x, y, 0, 1)); + + this.guiSlots.add(new SlotOptionalGas(inv, container, 5, 5, 2, x, y, -1, -1)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 6, 6, 2, x, y, 1, -1)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 7, 7, 2, x, y, -1, 1)); + this.guiSlots.add(new SlotOptionalGas(inv, container, 8, 8, 2, x, y, 1, 1)); + } + + @Override + protected String getGuiName() { + return this.bus.isExport() ? I18n.format("gui.mekeng.gas_export") : I18n.format("gui.mekeng.gas_import"); + } + +} diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasInterface.java b/src/main/java/com/mekeng/github/client/gui/GuiGasInterface.java new file mode 100644 index 0000000..7327d38 --- /dev/null +++ b/src/main/java/com/mekeng/github/client/gui/GuiGasInterface.java @@ -0,0 +1,107 @@ +package com.mekeng.github.client.gui; + +import appeng.api.util.IConfigManager; +import appeng.client.gui.widgets.GuiCustomSlot; +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.PacketSwitchGuis; +import appeng.util.IConfigManagerHost; +import com.mekeng.github.MekEng; +import com.mekeng.github.client.slots.SlotGas; +import com.mekeng.github.client.slots.SlotGasTank; +import com.mekeng.github.common.container.ContainerGasInterface; +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.duality.impl.DualityGasInterface; +import com.mekeng.github.common.me.inventory.IGasInventory; +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 GuiGasInterface extends GuiGasUpgradeable implements IConfigManagerHost { + + private final IGasInterfaceHost host; + private final ContainerGasInterface container; + private GuiTabButton priority; + + public GuiGasInterface(final InventoryPlayer ip, final IGasInterfaceHost te) { + super(new ContainerGasInterface(ip, te)); + this.ySize = 231; + this.xSize = 245; + this.host = te; + (this.container = (ContainerGasInterface) this.inventorySlots).setGui(this); + } + + @Override + public void initGui() { + super.initGui(); + + final IGasInventory configGases = this.host.getDualityGasInterface().getConfig(); + final IGasInventory gasTank = this.host.getDualityGasInterface().getTanks(); + + for (int i = 0; i < DualityGasInterface.NUMBER_OF_TANKS; ++i) { + this.guiSlots.add(new SlotGasTank(gasTank, i, DualityGasInterface.NUMBER_OF_TANKS + i, 8 + 18 * i, 53, 16, 68)); + this.guiSlots.add(new SlotGas(configGases, i, i, 8 + 18 * i, 35)); + } + + this.priority = new GuiTabButton(this.getGuiLeft() + 154, this.getGuiTop(), 2 + 4 * 16, GuiText.Priority.getLocal(), this.itemRender); + this.buttonList.add(this.priority); + } + + @Override + protected void addButtons() { + } + + @Override + public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) { + this.fontRenderer.drawString(this.getGuiDisplayName(this.getGuiName()), 8, 6, 4210752); + this.fontRenderer.drawString(GuiText.Config.getLocal(), 8, 6 + 11 + 7, 4210752); + this.fontRenderer.drawString(I18n.format("tooltip.mekeng.stored_gas"), 8, 6 + 112 + 7, 4210752); + this.fontRenderer.drawString(GuiText.inventory.getLocal(), 8, this.ySize - 96 + 3, 4210752); + } + + @Override + protected String getGuiName() { + return I18n.format("gui.mekeng.gas_interface"); + } + + @Override + protected String getBackground() { + return "guis/interfacefluidextendedlife.png"; + } + + @Override + protected void actionPerformed(final GuiButton btn) throws IOException { + super.actionPerformed(btn); + if (btn == this.priority) { + NetworkHandler.instance().sendToServer(new PacketSwitchGuis(GuiBridge.GUI_PRIORITY)); + } + } + + @Override + protected void mouseClicked(int xCoord, int yCoord, int btn) throws IOException { + 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)) { + IAEGasStack gas = AEGasStack.of(((SlotGasTank) slot).getGasStack()); + this.container.setTargetGasStack(gas); + slot.slotClicked(this.mc.player.inventory.getItemStack(), btn); + MekEng.proxy.netHandler.sendToServer(new CGenericPacket("set_target", gas)); + return; + } + } + } + super.mouseClicked(xCoord, yCoord, btn); + } + + @Override + public void updateSetting(IConfigManager manager, Enum settingName, Enum newValue) { + + } +} diff --git a/src/main/java/com/mekeng/github/client/gui/GuiGasInterfaceConfigurationTerminal.java b/src/main/java/com/mekeng/github/client/gui/GuiGasInterfaceConfigurationTerminal.java new file mode 100644 index 0000000..38b2782 --- /dev/null +++ b/src/main/java/com/mekeng/github/client/gui/GuiGasInterfaceConfigurationTerminal.java @@ -0,0 +1,499 @@ +package com.mekeng.github.client.gui; + +import appeng.api.config.ActionItems; +import appeng.api.config.Settings; +import appeng.client.gui.AEBaseGui; +import appeng.client.gui.widgets.GuiCustomSlot; +import appeng.client.gui.widgets.GuiImgButton; +import appeng.client.gui.widgets.GuiScrollbar; +import appeng.client.gui.widgets.MEGuiTextField; +import appeng.container.interfaces.IJEIGhostIngredients; +import appeng.core.localization.GuiText; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketInventoryAction; +import appeng.helpers.InventoryAction; +import appeng.util.BlockPosUtils; +import com.google.common.collect.HashMultimap; +import com.mekeng.github.MekEng; +import com.mekeng.github.client.slots.SlotGasTank; +import com.mekeng.github.common.container.ContainerGasInterfaceConfigurationTerminal; +import com.mekeng.github.common.me.client.ClientDCInternalGasInv; +import com.mekeng.github.common.me.data.impl.AEGasStack; +import com.mekeng.github.common.me.duality.impl.DualityGasInterface; +import com.mekeng.github.common.part.PartGasInterfaceConfigurationTerminal; +import com.mekeng.github.network.packet.CGenericPacket; +import com.mekeng.github.network.packet.sync.IActionHolder; +import com.mekeng.github.network.packet.sync.Paras; +import com.mekeng.github.util.Utils; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import mekanism.api.gas.GasStack; +import mezz.jei.api.gui.IGhostIngredientHandler; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.resources.I18n; +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 net.minecraft.util.text.TextComponentString; +import net.minecraftforge.common.DimensionManager; +import org.lwjgl.input.Mouse; + +import javax.annotation.Nonnull; +import java.awt.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.function.Consumer; + +import static appeng.client.render.BlockPosHighlighter.hilightBlock; + +public class GuiGasInterfaceConfigurationTerminal extends AEBaseGui implements IJEIGhostIngredients, IActionHolder { + + private static final int LINES_ON_PAGE = 6; + + // TODO: copied from GuiMEMonitorable. It looks not changed, maybe unneeded? + private final int offsetX = 21; + + private final HashMap byId = new HashMap<>(); + private final HashMultimap byName = HashMultimap.create(); + private final HashMap blockPosHashMap = new HashMap<>(); + private final HashMap guiButtonHashMap = new HashMap<>(); + + private final Map guiGasTankClientDCInternalGasInvMap = new Object2ObjectOpenHashMap<>(); + private final Map numUpgradesMap = new HashMap<>(); + private final ArrayList names = new ArrayList<>(); + private final ArrayList lines = new ArrayList<>(); + private final Set matchedStacks = new HashSet<>(); + private final Set matchedInterfaces = new HashSet<>(); + + private final Map> cachedSearches = new WeakHashMap<>(); + + private boolean refreshList = false; + private MEGuiTextField searchFieldInputs; + private final PartGasInterfaceConfigurationTerminal partInterfaceTerminal; + private final HashMap dimHashMap = new HashMap<>(); + public Map, Object> mapTargetSlot = new HashMap<>(); + private final Map> 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 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 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 in = this.getInternalHandler(); + IItemList before = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList(); + if (in != null) { + if (accessChanged) { + AccessRestriction currentAccess = (AccessRestriction) this.getConfigManager().getSetting(Settings.ACCESS); + AccessRestriction oldAccess = (AccessRestriction) ((ConfigManager) this.getConfigManager()).getOldSetting(Settings.ACCESS); + if (oldAccess.hasPermission(AccessRestriction.READ) && !currentAccess.hasPermission(AccessRestriction.READ)) { + readOncePass = true; + } + in.setBaseAccess(oldAccess); + before = in.getAvailableItems(before); + in.setBaseAccess(currentAccess); + accessChanged = false; + } else { + before = in.getAvailableItems(before); + } + } + + this.cached = false; + if (fullReset) { + this.handlerHash = 0; + } + + final MEInventoryHandler out = this.getInternalHandler(); + IItemList after = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList(); + + if (in != out) { + if (out != null) { + after = out.getAvailableItems(after); + } + Platform.postListChanges(before, after, this, this.source); + } + } + + private IMEInventory getInventoryWrapper(TileEntity target) { + EnumFacing targetSide = this.getSide().getFacing().getOpposite(); + // Prioritize a handler to directly link to another ME network + IStorageMonitorableAccessor accessor = target.getCapability(Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide); + if (accessor != null) { + IStorageMonitorable inventory = accessor.getInventory(this.source); + if (inventory != null) { + return inventory.getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)); + } + + // So this could / can be a design decision. If the tile does support our custom capability, + // but it does not return an inventory for the action source, we do NOT fall back to using + // IItemHandler's, as that might circumvent the security setings, and might also cause + // performance issues. + return null; + } + + // Check via cap for IItemHandler + IGasHandler handlerExt = target.getCapability(mekanism.common.capabilities.Capabilities.GAS_HANDLER_CAPABILITY, targetSide); + if (handlerExt != null) { + return new GasHandlerAdapter(handlerExt, this, targetSide); + } + return null; + } + + private int createHandlerHash(TileEntity target) { + if (target == null) { + return 0; + } + + final EnumFacing targetSide = this.getSide().getFacing().getOpposite(); + + if (target.hasCapability(Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide)) { + return Objects.hash(target, target.getCapability(Capabilities.STORAGE_MONITORABLE_ACCESSOR, targetSide)); + } + + final IGasHandler gasHandler = target.getCapability(mekanism.common.capabilities.Capabilities.GAS_HANDLER_CAPABILITY, targetSide); + + if (gasHandler != null) { + return Objects.hash(target, gasHandler, gasHandler.getTankInfo().length); + } + + return 0; + } + + @SuppressWarnings("unchecked") + public MEInventoryHandler getInternalHandler() { + if (this.cached) { + return this.handler; + } + + final boolean wasSleeping = this.monitor == null; + + this.cached = true; + final TileEntity self = this.getHost().getTile(); + final TileEntity target = self.getWorld().getTileEntity(self.getPos().offset(this.getSide().getFacing())); + final int newHandlerHash = this.createHandlerHash(target); + + if (newHandlerHash != 0 && newHandlerHash == this.handlerHash) { + return this.handler; + } + + this.handlerHash = newHandlerHash; + this.handler = null; + if (this.monitor != null) { + ((IBaseMonitor) monitor).removeListener(this); + } + this.monitor = null; + if (target != null) { + IMEInventory inv = this.getInventoryWrapper(target); + if (inv instanceof ITickingMonitor) { + this.monitor = (ITickingMonitor) inv; + this.monitor.setActionSource(this.source); + this.monitor.setMode((StorageFilter) this.getConfigManager().getSetting(Settings.STORAGE_FILTER)); + } + + if (inv != null) { + this.handler = new MEInventoryHandler<>(inv, AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)); + + this.handler.setBaseAccess((AccessRestriction) this.getConfigManager().getSetting(Settings.ACCESS)); + this.handler.setWhitelist(this.getInstalledUpgrades(Upgrades.INVERTER) > 0 ? IncludeExclude.BLACKLIST : IncludeExclude.WHITELIST); + this.handler.setPriority(this.getPriority()); + this.handler.setStorageFilter((StorageFilter) this.getConfigManager().getSetting(Settings.STORAGE_FILTER)); + + final IItemList priorityList = AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class).createList(); + + final int slotsToUse = 18 + this.getInstalledUpgrades(Upgrades.CAPACITY) * 9; + for (int x = 0; x < this.config.size() && x < slotsToUse; x++) { + final IAEGasStack is = AEGasStack.of(this.config.getGasStack(x)); + if (is != null) { + priorityList.add(is); + } + } + + if (this.getInstalledUpgrades(Upgrades.STICKY) > 0) { + this.handler.setSticky(true); + } + + if (this.getInstalledUpgrades(Upgrades.FUZZY) > 0) { + this.handler.setPartitionList(new FuzzyPriorityList<>(priorityList, (FuzzyMode) this.getConfigManager().getSetting(Settings.FUZZY_MODE))); + } else { + this.handler.setPartitionList(new PrecisePriorityList<>(priorityList)); + } + + if (inv instanceof IBaseMonitor) { + if (((AccessRestriction) this.getConfigManager().getSetting(Settings.ACCESS)).hasPermission(AccessRestriction.READ)) { + ((IBaseMonitor) inv).addListener(this, this.handler); + } + } + } + } + + // update sleep state... + if (wasSleeping != (this.monitor == null)) { + try { + final ITickManager tm = this.getProxy().getTick(); + if (this.monitor == null) { + tm.sleepDevice(this.getProxy().getNode()); + } else { + tm.wakeDevice(this.getProxy().getNode()); + } + } catch (final GridAccessException ignore) { + // :( + } + } + + try { + // force grid to update handlers... + ((GridStorageCache) this.getProxy().getGrid().getCache(IStorageGrid.class)).cellUpdate(null); + } catch (final GridAccessException e) { + // :3 + } + + return this.handler; + } + + @Override + public List getCellArray(final IStorageChannel channel) { + if (channel == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)) { + final IMEInventoryHandler out = this.getInternalHandler(); + if (out != null) { + return Collections.singletonList(out); + } + } + return Collections.emptyList(); + } + + @Override + public int getPriority() { + return this.priority; + } + + @Override + public void setPriority(int newValue) { + this.priority = newValue; + this.getHost().markForSave(); + this.resetCache(true); + } + + @Override + public void blinkCell(int slot) { + + } + + @Override + public void saveChanges(@Nullable ICellInventory cellInventory) { + + } + + public IGasInventory getConfig() { + return this.config; + } + + @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 ItemStack getItemStackRepresentation() { + return new ItemStack(ItemAndBlocks.GAS_STORAGE_BUS); + } + + @Override + public GuiBridge getGuiBridge() { + return GuiWrapper.INSTANCE.wrap(AEGuiBridge.GAS_STORAGE_BUS); + } + + // TODO: 1/28/2024 Unify both methods. + /** + * Filters the changes to only include items that pass the storage filter. + * Optimally, this should be handled by the underlying monitor. + * + * @see appeng.parts.misc.PartStorageBus#filterChanges + */ + protected Iterable filterChanges(Iterable change) { + Enum storageFilter = this.getConfigManager().getSetting(Settings.STORAGE_FILTER); + if (storageFilter == StorageFilter.EXTRACTABLE_ONLY && this.handler != null) { + ArrayList filteredList = new ArrayList<>(); + for (final IAEGasStack stack : change) { + if (this.handler.passesBlackOrWhitelist(stack)) { + filteredList.add(stack); + } + } + return filteredList; + } + return change; + } +} diff --git a/src/main/java/com/mekeng/github/common/part/PartGasTerminal.java b/src/main/java/com/mekeng/github/common/part/PartGasTerminal.java new file mode 100644 index 0000000..e3b252e --- /dev/null +++ b/src/main/java/com/mekeng/github/common/part/PartGasTerminal.java @@ -0,0 +1,124 @@ +package com.mekeng.github.common.part; + +import appeng.api.config.Settings; +import appeng.api.config.SortDir; +import appeng.api.config.SortOrder; +import appeng.api.config.ViewItems; +import appeng.api.implementations.tiles.IViewCellStorage; +import appeng.api.parts.IPartModel; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IStorageChannel; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEStack; +import appeng.api.util.IConfigManager; +import appeng.me.GridAccessException; +import appeng.parts.PartModel; +import appeng.parts.reporting.AbstractPartDisplay; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.util.ConfigManager; +import appeng.util.IConfigManagerHost; +import appeng.util.Platform; +import appeng.util.inv.IAEAppEngInventory; +import appeng.util.inv.InvOperation; +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.nbt.NBTTagCompound; +import net.minecraft.util.EnumHand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.Nonnull; +import java.util.List; + +public class PartGasTerminal extends AbstractPartDisplay implements ITerminalHost, IConfigManagerHost, IViewCellStorage, IAEAppEngInventory { + + private final IConfigManager cm = new ConfigManager(this); + private final AppEngInternalInventory viewCell = new AppEngInternalInventory(this, 5); + public static final ResourceLocation MODEL_OFF = new ResourceLocation(MekEng.MODID, "part/gas_terminal_off"); + public static final ResourceLocation MODEL_ON = new ResourceLocation(MekEng.MODID, "part/gas_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 PartGasTerminal(final ItemStack is) { + super(is); + this.cm.registerSetting(Settings.SORT_BY, SortOrder.NAME); + this.cm.registerSetting(Settings.VIEW_MODE, ViewItems.ALL); + this.cm.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING); + } + + @Override + public void getDrops(final List drops, final boolean wrenched) { + super.getDrops(drops, wrenched); + for (final ItemStack is : this.viewCell) { + if (!is.isEmpty()) { + drops.add(is); + } + } + } + + @Override + public IConfigManager getConfigManager() { + return this.cm; + } + + @Override + public void readFromNBT(final NBTTagCompound data) { + super.readFromNBT(data); + this.cm.readFromNBT(data); + this.viewCell.readFromNBT(data, "viewCell"); + } + + @Override + public void writeToNBT(final NBTTagCompound data) { + super.writeToNBT(data); + this.cm.writeToNBT(data); + this.viewCell.writeToNBT(data, "viewCell"); + } + + @Nonnull + @Override + public IPartModel getStaticModels() { + return this.selectModel(MODELS_OFF, MODELS_ON, MODELS_HAS_CHANNEL); + } + + @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_TERMINAL); + } + } + return true; + } + + @Override + public > IMEMonitor getInventory(IStorageChannel channel) { + try { + return this.getProxy().getStorage().getInventory(channel); + } catch (final GridAccessException e) { + // err nope? + } + return null; + } + + @Override + public void updateSetting(final IConfigManager manager, final Enum settingName, final Enum newValue) { + + } + + @Override + public IItemHandler getViewCellStorage() { + return this.viewCell; + } + + @Override + public void onChangeInventory(final IItemHandler inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack) { + this.getHost().markForSave(); + } + +} diff --git a/src/main/java/com/mekeng/github/common/part/PartGasUpgradeable.java b/src/main/java/com/mekeng/github/common/part/PartGasUpgradeable.java new file mode 100644 index 0000000..7bdc918 --- /dev/null +++ b/src/main/java/com/mekeng/github/common/part/PartGasUpgradeable.java @@ -0,0 +1,57 @@ +package com.mekeng.github.common.part; + +import appeng.parts.automation.PartUpgradeable; +import appeng.util.SettingsFrom; +import com.mekeng.github.common.me.inventory.IConfigurableGasInventory; +import com.mekeng.github.common.me.inventory.IGasInventory; +import com.mekeng.github.common.me.inventory.impl.GasInventory; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +public abstract class PartGasUpgradeable extends PartUpgradeable { + + public PartGasUpgradeable(ItemStack is) { + super(is); + } + + @Override + public void uploadSettings(final SettingsFrom from, final NBTTagCompound compound, EntityPlayer player) { + super.uploadSettings(from, compound, player); + if (this instanceof IConfigurableGasInventory) { + final IGasInventory tank = ((IConfigurableGasInventory) this).getGasInventoryByName("config"); + if (tank instanceof GasInventory) { + final GasInventory target = (GasInventory) tank; + final GasInventory tmp = new GasInventory(target.size()); + tmp.load(compound.getCompoundTag("config")); + for (int x = 0; x < tmp.size(); x++) { + target.setGas(x, tmp.getGasStack(x)); + } + } + if (this instanceof PartGasLevelEmitter) { + final PartGasLevelEmitter partGasLevelEmitter = (PartGasLevelEmitter) this; + partGasLevelEmitter.setReportingValue(compound.getLong("reportingValue")); + } + } + } + + @Override + protected NBTTagCompound downloadSettings(final SettingsFrom from) { + NBTTagCompound output = super.downloadSettings(from); + if (output == null) { + output = new NBTTagCompound(); + } + if (this instanceof IConfigurableGasInventory) { + final IGasInventory tank = ((IConfigurableGasInventory) this).getGasInventoryByName("config"); + if (tank instanceof GasInventory) { + output.setTag("config", ((GasInventory) tank).save()); + } + if (this instanceof PartGasLevelEmitter) { + final PartGasLevelEmitter partGasLevelEmitter = (PartGasLevelEmitter) this; + output.setLong("reportingValue", partGasLevelEmitter.getReportingValue()); + } + } + return output.isEmpty() ? null : output; + } + +} diff --git a/src/main/java/com/mekeng/github/common/part/PartSharedGasBus.java b/src/main/java/com/mekeng/github/common/part/PartSharedGasBus.java new file mode 100644 index 0000000..ff00789 --- /dev/null +++ b/src/main/java/com/mekeng/github/common/part/PartSharedGasBus.java @@ -0,0 +1,157 @@ +package com.mekeng.github.common.part; + +import appeng.api.AEApi; +import appeng.api.config.RedstoneMode; +import appeng.api.config.Upgrades; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.util.AECableType; +import appeng.me.GridAccessException; +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.inventory.IConfigurableGasInventory; +import com.mekeng.github.common.me.inventory.IGasInventory; +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.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; + +public abstract class PartSharedGasBus extends PartGasUpgradeable implements IGridTickable, IConfigurableGasInventory, IPartGroup { + + private final GasInventory config = new GasInventory(9); + private boolean lastRedstone; + + public PartSharedGasBus(ItemStack is) { + super(is); + } + + @Override + public void upgradesChanged() { + this.updateState(); + } + + @Override + public void onNeighborChanged(IBlockAccess w, BlockPos pos, BlockPos neighbor) { + this.updateState(); + if (this.lastRedstone != this.getHost().hasRedstone(this.getSide())) { + this.lastRedstone = !this.lastRedstone; + if (this.lastRedstone && this.getRSMode() == RedstoneMode.SIGNAL_PULSE) { + this.doBusWork(); + } + } + } + + private void updateState() { + try { + if (!this.isSleeping()) { + this.getProxy().getTick().wakeDevice(this.getProxy().getNode()); + } else { + this.getProxy().getTick().sleepDevice(this.getProxy().getNode()); + } + } catch (final GridAccessException e) { + // :P + } + } + + @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_IO_BUS); + } + return true; + } + + @Override + public void getBoxes(IPartCollisionHelper bch) { + bch.addBox(6, 6, 11, 10, 10, 13); + bch.addBox(5, 5, 13, 11, 11, 14); + bch.addBox(4, 4, 14, 12, 12, 16); + } + + protected TileEntity getConnectedTE() { + TileEntity self = this.getHost().getTile(); + return this.getTileEntity(self, self.getPos().offset(this.getSide().getFacing())); + } + + private TileEntity getTileEntity(final TileEntity self, final BlockPos pos) { + final World w = self.getWorld(); + + if (w.getChunkProvider().getLoadedChunk(pos.getX() >> 4, pos.getZ() >> 4) != null) { + return w.getTileEntity(pos); + } + + return null; + } + + protected int calculateAmountToSend() { + double amount = this.getChannel().transferFactor(); + switch (this.getInstalledUpgrades(Upgrades.SPEED)) { + case 4: + amount = amount * 1.5; + case 3: + amount = amount * 2; + case 2: + amount = amount * 4; + case 1: + amount = amount * 8; + case 0: + default: + return MathHelper.floor(amount); + } + } + + @Override + public void readFromNBT(NBTTagCompound extra) { + super.readFromNBT(extra); + this.config.load(extra.getCompoundTag("io_config")); + } + + @Override + public void writeToNBT(NBTTagCompound extra) { + super.writeToNBT(extra); + extra.setTag("io_config", this.config.save()); + } + + public IGasInventory getConfig() { + return this.config; + } + + @Override + public IGasInventory getGasInventoryByName(final String name) { + if (name.equals("config")) { + return this.config; + } + return null; + } + + protected IGasStorageChannel getChannel() { + return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class); + } + + @Override + public float getCableConnectionLength(AECableType cable) { + return 5; + } + + @Override + public String getUnlocalizedGroupName() { + return "group.mekeng.gas_io_bus"; + } + + protected abstract TickRateModulation doBusWork(); + + protected abstract boolean canDoBusWork(); + + public abstract boolean isExport(); + +} diff --git a/src/main/java/com/mekeng/github/common/part/p2p/PartP2PGases.java b/src/main/java/com/mekeng/github/common/part/p2p/PartP2PGases.java new file mode 100644 index 0000000..67de130 --- /dev/null +++ b/src/main/java/com/mekeng/github/common/part/p2p/PartP2PGases.java @@ -0,0 +1,259 @@ +package com.mekeng.github.common.part.p2p; + +import appeng.api.parts.IPartModel; +import appeng.me.GridAccessException; +import appeng.parts.p2p.PartP2PTunnel; +import com.google.common.collect.Maps; +import com.mekeng.github.MekEng; +import com.mekeng.github.util.Ae2Reflect; +import mekanism.api.gas.Gas; +import mekanism.api.gas.GasStack; +import mekanism.api.gas.GasTank; +import mekanism.api.gas.GasTankInfo; +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 net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.common.capabilities.Capability; + +import javax.annotation.Nonnull; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class PartP2PGases extends PartP2PTunnel implements IGasHandler { + + private static final Object MODELS = Ae2Reflect.createP2PModel("part/p2p/p2p_tunnel_gases"); + + private static final ThreadLocal> DEPTH = new ThreadLocal<>(); + private static final GasTankInfo[] ACTIVE_TANK = {new GasTank(100000)}; + private static final GasTankInfo[] INACTIVE_TANK = {new GasTank(0)}; + + private Map.Entry cachedGasTank; + private int tmpUsed; + + public PartP2PGases(final ItemStack is) { + super(is); + } + + public static Collection getModels() { + Set all = new HashSet<>(); + Ae2Reflect.getP2PModel(MODELS).forEach(m -> all.addAll(m.getModels())); + return all; + } + + @Override + public void onTunnelNetworkChange() { + this.cachedGasTank = null; + } + + @Override + public void onNeighborChanged(IBlockAccess w, BlockPos pos, BlockPos neighbor) { + this.cachedGasTank = null; + if (this.isOutput()) { + try { + for (PartP2PGases in : this.getInputs()) { + if (in != null) { + in.onTunnelNetworkChange(); + } + } + } catch (GridAccessException e) { + MekEng.log.error(e); + } + } + } + + @Override + public boolean hasCapability(Capability capabilityClass) { + if (capabilityClass == Capabilities.GAS_HANDLER_CAPABILITY) { + return true; + } + return super.hasCapability(capabilityClass); + } + + @SuppressWarnings("unchecked") + @Override + public T getCapability(Capability capabilityClass) { + if (capabilityClass == Capabilities.GAS_HANDLER_CAPABILITY) { + return (T) this; + } + return super.getCapability(capabilityClass); + } + + @Nonnull + @Override + public IPartModel getStaticModels() { + return Ae2Reflect.getP2PModel(MODELS, this.isPowered(), this.isActive()); + } + + @Nonnull + @Override + public GasTankInfo[] getTankInfo() { + if (!this.isOutput()) { + try { + for (PartP2PGases tun : this.getInputs()) { + if (tun != null) { + return ACTIVE_TANK; + } + } + } catch (GridAccessException e) { + MekEng.log.error(e); + } + } + return INACTIVE_TANK; + } + + @Override + public int receiveGas(EnumFacing side, GasStack resource, boolean doTransfer) { + if (side != null && side != this.getSide().getFacing()) { + return 0; + } + final Deque stack = this.getDepth(); + + for (final PartP2PGases t : stack) { + if (t == this) { + return 0; + } + } + + stack.push(this); + + final List list = this.getGasOutputs(); + int requestTotal = 0; + + Iterator i = list.iterator(); + + while (i.hasNext()) { + final PartP2PGases l = i.next(); + final Map.Entry gasTank = l.getTarget(); + if (gasTank != null) { + l.tmpUsed = gasTank.getKey().receiveGas(gasTank.getValue(), resource.copy(), false); + } else { + l.tmpUsed = 0; + } + + if (l.tmpUsed <= 0) { + i.remove(); + } else { + requestTotal += l.tmpUsed; + } + } + + if (requestTotal <= 0) { + if (stack.pop() != this) { + throw new IllegalStateException("Invalid Recursion detected."); + } + return 0; + } + + if (!doTransfer) { + if (stack.pop() != this) { + throw new IllegalStateException("Invalid Recursion detected."); + } + return Math.min(resource.amount, requestTotal); + } + + int available = resource.amount; + + i = list.iterator(); + int used = 0; + + while (i.hasNext() && available > 0) { + final PartP2PGases l = i.next(); + + final GasStack insert = resource.copy(); + insert.amount = (int) Math.ceil(insert.amount * ((double) l.tmpUsed / (double) requestTotal)); + if (insert.amount > available) { + insert.amount = available; + } + + final Map.Entry gasTank = l.getTarget(); + if (gasTank != null) { + l.tmpUsed = gasTank.getKey().receiveGas(gasTank.getValue(), insert.copy(), true); + } else { + l.tmpUsed = 0; + } + + available -= insert.amount; + used += l.tmpUsed; + } + + if (stack.pop() != this) { + throw new IllegalStateException("Invalid Recursion detected."); + } + + return used; + } + + @Override + public GasStack drawGas(EnumFacing side, int amount, boolean doTransfer) { + return null; + } + + @Override + public boolean canReceiveGas(EnumFacing side, Gas type) { + return true; + } + + @Override + public boolean canDrawGas(EnumFacing side, Gas type) { + return false; + } + + private Deque getDepth() { + Deque s = DEPTH.get(); + if (s == null) { + DEPTH.set(s = new ArrayDeque<>()); + } + return s; + } + + private List getGasOutputs() { + final List outs = new ArrayList<>(); + + try { + for (final PartP2PGases l : this.getOutputs()) { + final Map.Entry handler = l.getTarget(); + if (handler != null) { + outs.add(l); + } + } + } catch (final GridAccessException e) { + // :P + } + + return outs; + } + + private Map.Entry getTarget() { + if (!this.getProxy().isActive()) { + return null; + } + + if (this.cachedGasTank != null) { + return this.cachedGasTank; + } + + final TileEntity te = this.getTile().getWorld().getTileEntity(this.getTile().getPos().offset(this.getSide().getFacing())); + + if (te != null && te.hasCapability(Capabilities.GAS_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite())) { + return this.cachedGasTank = Maps.immutableEntry( + te.getCapability(Capabilities.GAS_HANDLER_CAPABILITY, this.getSide().getFacing().getOpposite()), + this.getSide().getFacing().getOpposite() + ); + } + + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/common/tile/TileGasInterface.java b/src/main/java/com/mekeng/github/common/tile/TileGasInterface.java new file mode 100644 index 0000000..d8e6f4f --- /dev/null +++ b/src/main/java/com/mekeng/github/common/tile/TileGasInterface.java @@ -0,0 +1,167 @@ +package com.mekeng.github.common.tile; + +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.util.AECableType; +import appeng.api.util.AEPartLocation; +import appeng.api.util.DimensionalCoord; +import appeng.api.util.IConfigManager; +import appeng.core.sync.GuiBridge; +import appeng.core.sync.GuiWrapper; +import appeng.helpers.IPriorityHost; +import appeng.tile.grid.AENetworkTile; +import com.mekeng.github.common.ItemAndBlocks; +import com.mekeng.github.common.container.handler.AEGuiBridge; +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.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.world.World; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.EnumSet; +import java.util.List; + +public class TileGasInterface extends AENetworkTile implements IGridTickable, IGasInterfaceHost, IPriorityHost, IConfigurableGasInventory { + private final DualityGasInterface duality = new DualityGasInterface(this.getProxy(), this); + + @MENetworkEventSubscribe + public void stateChange(final MENetworkChannelsChanged c) { + this.duality.notifyNeighbors(); + } + + @MENetworkEventSubscribe + public void stateChange(final MENetworkPowerStatusChange c) { + this.duality.notifyNeighbors(); + } + + @Nonnull + @Override + public TickingRequest getTickingRequest(@Nonnull IGridNode node) { + return this.duality.getTickingRequest(node); + } + + @Nonnull + @Override + public TickRateModulation tickingRequest(@Nonnull IGridNode node, int ticksSinceLastCall) { + return this.duality.tickingRequest(node, ticksSinceLastCall); + } + + @Override + public DualityGasInterface getDualityGasInterface() { + return this.duality; + } + + @Override + public TileEntity getTileEntity() { + return this; + } + + @Override + public void getDrops(final World w, final BlockPos pos, final List drops) { + this.duality.addDrops(drops); + } + + @Override + public void gridChanged() { + this.duality.gridChanged(); + } + + @Override + public NBTTagCompound writeToNBT(final NBTTagCompound data) { + super.writeToNBT(data); + this.duality.writeToNBT(data); + return data; + } + + @Override + public void readFromNBT(final NBTTagCompound data) { + super.readFromNBT(data); + this.duality.readFromNBT(data); + } + + @Nonnull + @Override + public AECableType getCableConnectionType(@Nonnull final AEPartLocation dir) { + return this.duality.getCableConnectionType(); + } + + @Override + public DimensionalCoord getLocation() { + return this.duality.getLocation(); + } + + @Override + public EnumSet getTargets() { + return EnumSet.allOf(EnumFacing.class); + } + + @Override + public int getPriority() { + return this.duality.getPriority(); + } + + @Override + public void setPriority(final int newValue) { + this.duality.setPriority(newValue); + } + + @Override + public boolean hasCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { + return this.duality.hasCapability(capability, facing) || super.hasCapability(capability, facing); + } + + @Override + public T getCapability(@Nonnull Capability capability, @Nullable EnumFacing facing) { + T result = this.duality.getCapability(capability, facing); + if (result != null) { + return result; + } + return super.getCapability(capability, facing); + } + + @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); + } + + @Override + public GuiBridge getGuiBridge() { + return GuiWrapper.INSTANCE.wrap(AEGuiBridge.GAS_INTERFACE); + } + +} diff --git a/src/main/java/com/mekeng/github/core/CoreHooks.java b/src/main/java/com/mekeng/github/core/CoreHooks.java new file mode 100644 index 0000000..302bf9a --- /dev/null +++ b/src/main/java/com/mekeng/github/core/CoreHooks.java @@ -0,0 +1,110 @@ +package com.mekeng.github.core; + +import appeng.api.AEApi; +import appeng.api.config.SecurityPermissions; +import appeng.api.config.TunnelType; +import appeng.api.parts.IPart; +import appeng.api.storage.IStorageChannel; +import appeng.api.util.AEPartLocation; +import appeng.core.sync.GuiBridge; +import appeng.core.sync.GuiWrapper; +import appeng.me.GridAccessException; +import appeng.me.cache.P2PCache; +import appeng.parts.p2p.PartP2PTunnel; +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.me.inventory.IExtendedGasHandler; +import com.mekeng.github.common.me.storage.IGasStorageChannel; +import com.mekeng.github.util.Ae2Reflect; +import mekanism.api.gas.GasStack; +import mekanism.api.gas.IGasHandler; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.Vec3d; + +/** + * You shouldn't use anything from here.
+ * All hookers are only used in ASM injection + */ + +@SuppressWarnings("unused") +public final class CoreHooks { + + public static GasStack hooker$wrapGasHandler(IGasHandler handler, EnumFacing side, int amount, boolean doTransfer, GasStack stack) { + if (handler instanceof IExtendedGasHandler) { + GasStack draw = stack.copy().withAmount(amount); + return ((IExtendedGasHandler) handler).drawGas(side, draw, doTransfer); + } + return handler.drawGas(side, amount, doTransfer); + } + + public static GuiBridge hooker$getGasBridge(IStorageChannel channel) { + if (channel == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)) { + return GuiWrapper.INSTANCE.wrap(AEGuiBridge.GAS_TERMINAL); + } + return null; + } + + public static boolean hooker$customTunnel(PartP2PTunnel p2p, final EntityPlayer player, final EnumHand hand, final Vec3d pos) { + if (Platform.isClient()) { + return true; + } + + if (hand == EnumHand.OFF_HAND) { + return false; + } + + ItemStack is = player.getHeldItem(hand); + if (is.isEmpty()) { + is = player.getHeldItemOffhand(); + } + + try { + boolean hasPerms = p2p.getProxy().getSecurity().hasPermission(player, SecurityPermissions.BUILD); + if (!hasPerms) { + return false; + } + } catch (GridAccessException e) { + return false; + } + + final TunnelType tt = AEApi.instance().registries().p2pTunnel().getTunnelTypeByItem(is); + if (tt == MekEng.GAS) { + ItemStack newType = new ItemStack(ItemAndBlocks.GAS_P2P); + if (!newType.isEmpty() && !ItemStack.areItemsEqual(newType, p2p.getItemStack())) { + final boolean oldOutput = p2p.isOutput(); + final short myFreq = p2p.getFrequency(); + + try { + p2p.getProxy().getP2P().removeTunnel(p2p, p2p.getFrequency()); + } catch (GridAccessException e) { + MekEng.log.error(e); + } + + p2p.getHost().removePart(p2p.getSide(), false); + + final AEPartLocation dir = p2p.getHost().addPart(newType, p2p.getSide(), player, hand); + final IPart newBus = p2p.getHost().getPart(dir); + + if (newBus instanceof PartP2PTunnel) { + final PartP2PTunnel newTunnel = (PartP2PTunnel) newBus; + Ae2Reflect.setP2POutput(newTunnel, oldOutput); + try { + final P2PCache p2pC = newTunnel.getProxy().getP2P(); + p2pC.updateFreq(newTunnel, myFreq); + } catch (final GridAccessException e) { + // :P + } + newTunnel.onTunnelNetworkChange(); + } + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/mekeng/github/core/MkEClassTransformer.java b/src/main/java/com/mekeng/github/core/MkEClassTransformer.java new file mode 100644 index 0000000..bfeb9bc --- /dev/null +++ b/src/main/java/com/mekeng/github/core/MkEClassTransformer.java @@ -0,0 +1,57 @@ +package com.mekeng.github.core; + +import com.mekeng.github.core.transformers.PartP2PTunnelTransformer; +import com.mekeng.github.core.transformers.TileChestTransformer; +import com.mekeng.github.core.transformers.TileEntityPressurizedTubeTransformer; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +public class MkEClassTransformer implements IClassTransformer { + + @Override + public byte[] transform(String name, String transformedName, byte[] code) { + Transform tform; + switch (transformedName) { + case "mekanism.common.tile.transmitter.TileEntityPressurizedTube": + tform = TileEntityPressurizedTubeTransformer.INSTANCE; + break; + case "appeng.tile.storage.TileChest": + tform = TileChestTransformer.INSTANCE; + break; + case "appeng.parts.p2p.PartP2PTunnel": + tform = PartP2PTunnelTransformer.INSTANCE; + break; + default: + return code; + } + System.out.println("[MekEng] Transforming class: " + transformedName); + return tform.transformClass(code); + } + + public interface Transform { + + byte[] transformClass(byte[] code); + + } + + public static abstract class ClassMapper implements Transform { + + @Override + public byte[] transformClass(byte[] code) { + ClassReader reader = new ClassReader(code); + ClassWriter writer = new ClassWriter(reader, getWriteFlags()); + reader.accept(getClassMapper(writer), 0); + return writer.toByteArray(); + } + + protected int getWriteFlags() { + return 0; + } + + protected abstract ClassVisitor getClassMapper(ClassVisitor downstream); + + } + +} diff --git a/src/main/java/com/mekeng/github/core/MkECore.java b/src/main/java/com/mekeng/github/core/MkECore.java new file mode 100644 index 0000000..9b8617f --- /dev/null +++ b/src/main/java/com/mekeng/github/core/MkECore.java @@ -0,0 +1,41 @@ +package com.mekeng.github.core; + +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; + +import javax.annotation.Nullable; +import java.util.Map; + +@IFMLLoadingPlugin.Name("MekEngCore") +@IFMLLoadingPlugin.MCVersion("1.12.2") +@IFMLLoadingPlugin.TransformerExclusions("com.mekeng.github.core") +public class MkECore implements IFMLLoadingPlugin { + + @Override + public String[] getASMTransformerClass() { + return new String[] { MkECore.class.getPackage().getName() + ".MkEClassTransformer" }; + } + + @Nullable + @Override + public String getModContainerClass() { + return null; + } + + @Nullable + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) { + // NO-OP + } + + @Nullable + @Override + public String getAccessTransformerClass() { + return null; + } + +} diff --git a/src/main/java/com/mekeng/github/core/transformers/PartP2PTunnelTransformer.java b/src/main/java/com/mekeng/github/core/transformers/PartP2PTunnelTransformer.java new file mode 100644 index 0000000..eea83bd --- /dev/null +++ b/src/main/java/com/mekeng/github/core/transformers/PartP2PTunnelTransformer.java @@ -0,0 +1,63 @@ +package com.mekeng.github.core.transformers; + +import com.mekeng.github.core.MkEClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class PartP2PTunnelTransformer extends MkEClassTransformer.ClassMapper { + + public static PartP2PTunnelTransformer INSTANCE = new PartP2PTunnelTransformer(); + + private PartP2PTunnelTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformPartP2PTunnel(Opcodes.ASM5, downstream); + } + + private static class TransformPartP2PTunnel extends ClassVisitor { + + TransformPartP2PTunnel(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("onPartActivate")) { + return new TransformOnPartActivate(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformOnPartActivate extends MethodVisitor { + + Label L = new Label(); + + TransformOnPartActivate(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitCode() { + super.visitCode(); + super.visitVarInsn(Opcodes.ALOAD, 0); + super.visitVarInsn(Opcodes.ALOAD, 1); + super.visitVarInsn(Opcodes.ALOAD, 2); + super.visitVarInsn(Opcodes.ALOAD, 3); + super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mekeng/github/core/CoreHooks", "hooker$customTunnel", "(Lappeng/parts/p2p/PartP2PTunnel;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/EnumHand;Lnet/minecraft/util/math/Vec3d;)Z", false); + super.visitJumpInsn(Opcodes.IFEQ, L); + super.visitInsn(Opcodes.ICONST_1); + super.visitInsn(Opcodes.IRETURN); + super.visitLabel(L); + super.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + + } + +} diff --git a/src/main/java/com/mekeng/github/core/transformers/TileChestTransformer.java b/src/main/java/com/mekeng/github/core/transformers/TileChestTransformer.java new file mode 100644 index 0000000..b41223e --- /dev/null +++ b/src/main/java/com/mekeng/github/core/transformers/TileChestTransformer.java @@ -0,0 +1,80 @@ +package com.mekeng.github.core.transformers; + +import com.mekeng.github.core.MkEClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class TileChestTransformer extends MkEClassTransformer.ClassMapper { + + public static TileChestTransformer INSTANCE = new TileChestTransformer(); + + private TileChestTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformTileChest(Opcodes.ASM5, downstream); + } + + @Override + protected int getWriteFlags() { + return ClassWriter.COMPUTE_FRAMES; + } + + private static class TransformTileChest extends ClassVisitor { + + TransformTileChest(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("getGuiBridge")) { + return new TransformGetGuiBridge(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformGetGuiBridge extends MethodVisitor { + + boolean ready = false; + Label L = new Label(); + + TransformGetGuiBridge(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitJumpInsn(int opcode, Label label) { + super.visitJumpInsn(opcode, label); + if (opcode == Opcodes.IFNULL) { + ready = true; + } + } + + @Override + public void visitVarInsn(int opcode, int var) { + super.visitVarInsn(opcode, var); + if (ready && opcode == Opcodes.ALOAD) { + ready = false; + super.visitFieldInsn(Opcodes.GETFIELD, "appeng/tile/storage/TileChest", "cellHandler", "Lappeng/tile/storage/TileChest$ChestMonitorHandler;"); + super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "appeng/tile/storage/TileChest$ChestMonitorHandler", "getChannel", "()Lappeng/api/storage/IStorageChannel;", false); + super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/mekeng/github/core/CoreHooks", "hooker$getGasBridge", "(Lappeng/api/storage/IStorageChannel;)Lappeng/core/sync/GuiBridge;", false); + super.visitInsn(Opcodes.DUP); + super.visitJumpInsn(Opcodes.IFNULL, L); + super.visitInsn(Opcodes.ARETURN); + super.visitLabel(L); + super.visitInsn(Opcodes.POP); + super.visitVarInsn(Opcodes.ALOAD, 0); + } + } + + } + +} diff --git a/src/main/java/com/mekeng/github/core/transformers/TileEntityPressurizedTubeTransformer.java b/src/main/java/com/mekeng/github/core/transformers/TileEntityPressurizedTubeTransformer.java new file mode 100644 index 0000000..0c71b3c --- /dev/null +++ b/src/main/java/com/mekeng/github/core/transformers/TileEntityPressurizedTubeTransformer.java @@ -0,0 +1,68 @@ +package com.mekeng.github.core.transformers; + +import com.mekeng.github.core.MkEClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class TileEntityPressurizedTubeTransformer extends MkEClassTransformer.ClassMapper { + + public static TileEntityPressurizedTubeTransformer INSTANCE = new TileEntityPressurizedTubeTransformer(); + + private TileEntityPressurizedTubeTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformTileEntityPressurizedTube(Opcodes.ASM5, downstream); + } + + private static class TransformTileEntityPressurizedTube extends ClassVisitor { + + TransformTileEntityPressurizedTube(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("update")) { + return new TransformUpdate(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformUpdate extends MethodVisitor { + + boolean ready = false; + + TransformUpdate(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == Opcodes.INVOKEINTERFACE && name.equals("drawGas")) { + if (ready) { + super.visitVarInsn(Opcodes.ALOAD, 5); + super.visitMethodInsn( + Opcodes.INVOKESTATIC, + "com/mekeng/github/core/CoreHooks", + "hooker$wrapGasHandler", + "(Lmekanism/api/gas/IGasHandler;Lnet/minecraft/util/EnumFacing;IZLmekanism/api/gas/GasStack;)Lmekanism/api/gas/GasStack;", + false + ); + ready = false; + return; + } else { + ready = true; + } + } + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + + } + +} diff --git a/src/main/java/com/mekeng/github/network/Packets.java b/src/main/java/com/mekeng/github/network/Packets.java new file mode 100644 index 0000000..efeaa24 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/Packets.java @@ -0,0 +1,31 @@ +package com.mekeng.github.network; + +import com.mekeng.github.MekEng; +import com.mekeng.github.network.packet.CGasSlotSync; +import com.mekeng.github.network.packet.CGenericPacket; +import com.mekeng.github.network.packet.CSwitchGuis; +import com.mekeng.github.network.packet.MkEMessage; +import com.mekeng.github.network.packet.SGasSlotSync; +import com.mekeng.github.network.packet.SGenericPacket; +import com.mekeng.github.network.packet.SMEGasInventoryUpdate; +import net.minecraftforge.fml.relauncher.Side; + +public class Packets { + + private static int nextID = 1; + + @SuppressWarnings({"rawtypes", "unchecked"}) + public static void register(MkEMessage packet) { + MekEng.proxy.netHandler.registerMessage(packet.getHandler(), packet.getClass(), nextID++, packet.isClient() ? Side.CLIENT : Side.SERVER); + } + + public static void init() { + register(new CGenericPacket()); + register(new SGenericPacket()); + register(new CSwitchGuis()); + register(new SMEGasInventoryUpdate()); + register(new CGasSlotSync()); + register(new SGasSlotSync()); + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/CGasSlotSync.java b/src/main/java/com/mekeng/github/network/packet/CGasSlotSync.java new file mode 100644 index 0000000..7b65be2 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/CGasSlotSync.java @@ -0,0 +1,80 @@ +package com.mekeng.github.network.packet; + +import com.mekeng.github.MekEng; +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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.ByteBufUtils; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +import java.io.IOException; +import java.util.Map; + +public class CGasSlotSync extends MkEMessage { + + protected Map list; + + public CGasSlotSync() { + // NO-OP + } + + public CGasSlotSync(Map list) { + this.list = list; + } + + @Override + void toBytes(PacketBuffer buf) { + NBTTagCompound HOLDER = new NBTTagCompound(); + for (Map.Entry data : this.list.entrySet()) { + NBTTagCompound gas = new NBTTagCompound(); + if (data.getValue() != null) { + data.getValue().writeToNBT(gas); + } + HOLDER.setTag(data.getKey().toString(), gas); + } + ByteBufUtils.writeTag(buf, HOLDER); + } + + @Override + void fromBytes(PacketBuffer buf) { + this.list = new Int2ObjectOpenHashMap<>(); + try { + NBTTagCompound HOLDER = buf.readCompoundTag(); + if (HOLDER != null) { + for (String key : HOLDER.getKeySet()) { + int id = Integer.parseInt(key); + IAEGasStack gas = AEGasStack.of(HOLDER.getCompoundTag(key)); + this.list.put(id, gas); + } + } + } catch (IOException e) { + MekEng.log.error("Fail to sync gas slot, it may cause ghost gas display"); + MekEng.log.error(e); + } + } + + @Override + public boolean isClient() { + return false; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + EntityPlayerMP player = ctx.getServerHandler().player; + player.getServerWorld().addScheduledTask(() -> { + if (player.openContainer instanceof IGasSyncContainer) { + IGasSyncContainer c = (IGasSyncContainer) player.openContainer; + c.receiveGasSlots(message.list); + } + }); + return null; + }; + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/CGenericPacket.java b/src/main/java/com/mekeng/github/network/packet/CGenericPacket.java new file mode 100644 index 0000000..a280531 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/CGenericPacket.java @@ -0,0 +1,75 @@ +package com.mekeng.github.network.packet; + +import com.mekeng.github.network.packet.sync.IActionHolder; +import com.mekeng.github.network.packet.sync.ParaSerializer; +import com.mekeng.github.network.packet.sync.Paras; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +import java.util.function.Consumer; + +public class CGenericPacket extends MkEMessage { + + private String name; + private Object[] paras; + + public CGenericPacket() { + // NO-OP + } + + public CGenericPacket(String name) { + this.name = name; + this.paras = null; + } + + public CGenericPacket(String name, Object... paras) { + this.name = name; + this.paras = paras; + } + + @Override + void toBytes(PacketBuffer buf) { + buf.writeString(this.name); + if (this.paras == null) { + buf.writeBoolean(false); + } else { + buf.writeBoolean(true); + ParaSerializer.to(this.paras, buf); + } + } + + @Override + void fromBytes(PacketBuffer buf) { + this.name = buf.readString(1024); + if (buf.readBoolean()) { + this.paras = ParaSerializer.from(buf); + } else { + this.paras = null; + } + } + + @Override + public boolean isClient() { + return false; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + EntityPlayerMP player = ctx.getServerHandler().player; + player.getServerWorld().addScheduledTask(() -> { + if (player.openContainer instanceof IActionHolder) { + IActionHolder ah = (IActionHolder) player.openContainer; + Consumer fun = ah.getActionMap().get(message.name); + if (fun != null) { + fun.accept(new Paras(message.paras)); + } + } + }); + return null; + }; + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/CSwitchGuis.java b/src/main/java/com/mekeng/github/network/packet/CSwitchGuis.java new file mode 100644 index 0000000..c41e2c4 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/CSwitchGuis.java @@ -0,0 +1,77 @@ +package com.mekeng.github.network.packet; + +import appeng.container.AEBaseContainer; +import appeng.container.ContainerOpenContext; +import com.mekeng.github.common.container.handler.GuiFactory; +import com.mekeng.github.common.container.handler.GuiHandler; +import com.mekeng.github.common.container.handler.MkEGuis; +import com.mekeng.github.util.Ae2Reflect; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.Container; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +public class CSwitchGuis extends MkEMessage { + + private GuiFactory factory; + private GuiFactory.GuiMode mode; + + public CSwitchGuis(GuiFactory guiType, GuiFactory.GuiMode mode) { + this.factory = guiType; + this.mode = mode; + } + + public CSwitchGuis() { + // NO-OP + } + + @Override + void fromBytes(PacketBuffer byteBuf) { + factory = MkEGuis.getFactory(byteBuf.readByte()); + mode = GuiFactory.GuiMode.values()[byteBuf.readByte()]; + } + + @Override + void toBytes(PacketBuffer byteBuf) { + byteBuf.writeByte(factory != null ? factory.getId() : 0); + byteBuf.writeByte(mode.ordinal()); + } + + @Override + public boolean isClient() { + return false; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + if (message.factory == null) { + return null; + } + EntityPlayerMP player = ctx.getServerHandler().player; + Container cont = player.openContainer; + if (!(cont instanceof AEBaseContainer)) { + return null; + } + ContainerOpenContext context = ((AEBaseContainer) cont).getOpenContext(); + if (context == null) { + return null; + } + player.getServerWorld().addScheduledTask( + () -> GuiHandler.openGui( + player, + player.world, + Ae2Reflect.getContextX(context), + Ae2Reflect.getContextY(context), + Ae2Reflect.getContextZ(context), + message.mode, + context.getSide().getFacing(), + message.factory + ) + ); + return null; + }; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/network/packet/MkEMessage.java b/src/main/java/com/mekeng/github/network/packet/MkEMessage.java new file mode 100644 index 0000000..ded6a88 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/MkEMessage.java @@ -0,0 +1,32 @@ +package com.mekeng.github.network.packet; + +import io.netty.buffer.ByteBuf; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +public abstract class MkEMessage implements IMessage { + + public MkEMessage() { + // NO-OP + } + + public abstract boolean isClient(); + + public abstract IMessageHandler getHandler(); + + abstract void toBytes(PacketBuffer buf); + + abstract void fromBytes(PacketBuffer buf); + + @Override + public final void toBytes(ByteBuf buf) { + this.toBytes(new PacketBuffer(buf)); + } + + @Override + public final void fromBytes(ByteBuf buf) { + this.fromBytes(new PacketBuffer(buf)); + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/SGasSlotSync.java b/src/main/java/com/mekeng/github/network/packet/SGasSlotSync.java new file mode 100644 index 0000000..f5f933f --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/SGasSlotSync.java @@ -0,0 +1,41 @@ +package com.mekeng.github.network.packet; + +import com.mekeng.github.common.container.sync.IGasSyncContainer; +import com.mekeng.github.common.me.data.IAEGasStack; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +import java.util.Map; + +public class SGasSlotSync extends CGasSlotSync { + + public SGasSlotSync() { + // NO-OP + } + + public SGasSlotSync(Map list) { + super(list); + } + + @Override + public boolean isClient() { + return true; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + EntityPlayer player = Minecraft.getMinecraft().player; + Minecraft.getMinecraft().addScheduledTask(() -> { + if (player.openContainer instanceof IGasSyncContainer) { + IGasSyncContainer c = (IGasSyncContainer) player.openContainer; + c.receiveGasSlots(message.list); + } + }); + return null; + }; + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/SGenericPacket.java b/src/main/java/com/mekeng/github/network/packet/SGenericPacket.java new file mode 100644 index 0000000..6a66cd1 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/SGenericPacket.java @@ -0,0 +1,75 @@ +package com.mekeng.github.network.packet; + +import com.mekeng.github.network.packet.sync.IActionHolder; +import com.mekeng.github.network.packet.sync.ParaSerializer; +import com.mekeng.github.network.packet.sync.Paras; +import net.minecraft.client.Minecraft; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +import java.util.function.Consumer; + +public class SGenericPacket extends MkEMessage { + + private String name; + private Object[] paras; + + public SGenericPacket() { + // NO-OP + } + + public SGenericPacket(String name) { + this.name = name; + this.paras = null; + } + + public SGenericPacket(String name, Object... paras) { + this.name = name; + this.paras = paras; + } + + @Override + void toBytes(PacketBuffer buf) { + buf.writeString(this.name); + if (this.paras == null) { + buf.writeBoolean(false); + } else { + buf.writeBoolean(true); + ParaSerializer.to(this.paras, buf); + } + } + + @Override + void fromBytes(PacketBuffer buf) { + this.name = buf.readString(1024); + if (buf.readBoolean()) { + this.paras = ParaSerializer.from(buf); + } else { + this.paras = null; + } + } + + @Override + public boolean isClient() { + return true; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + Minecraft.getMinecraft().addScheduledTask(() -> { + if (Minecraft.getMinecraft().currentScreen instanceof IActionHolder) { + IActionHolder ah = (IActionHolder) Minecraft.getMinecraft().currentScreen; + Consumer fun = ah.getActionMap().get(message.name); + if (fun != null) { + fun.accept(new Paras(message.paras)); + } + } + }); + return null; + }; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/network/packet/SMEGasInventoryUpdate.java b/src/main/java/com/mekeng/github/network/packet/SMEGasInventoryUpdate.java new file mode 100644 index 0000000..458e909 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/SMEGasInventoryUpdate.java @@ -0,0 +1,83 @@ +package com.mekeng.github.network.packet; + +import com.mekeng.github.MekEng; +import com.mekeng.github.client.gui.GuiGasTerminal; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.common.me.data.impl.AEGasStack; +import io.netty.buffer.Unpooled; +import net.minecraft.client.Minecraft; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class SMEGasInventoryUpdate extends MkEMessage { + + private final PacketBuffer buffer = new PacketBuffer(Unpooled.buffer(1024)); + private final List list = new ArrayList<>(); + private int cnt = 0; + + public SMEGasInventoryUpdate() { + // NO-OP + } + + public SMEGasInventoryUpdate addGas(IAEGasStack stack) { + try { + stack.writeToPacket(buffer); + cnt ++; + } catch (IOException e) { + buffer.clear(); + cnt = 0; + MekEng.log.error(e); + } + return this; + } + + public boolean needFlush() { + return buffer.readableBytes() > 0xE00000; + } + + public void clear() { + buffer.clear(); + cnt = 0; + } + + public boolean isEmpty() { + return cnt == 0; + } + + @Override + void toBytes(PacketBuffer buf) { + buf.writeInt(cnt); + buf.writeBytes(buffer); + } + + @Override + void fromBytes(PacketBuffer buf) { + cnt = buf.readInt(); + for (int i = 0; i < cnt; i ++) { + list.add(AEGasStack.of(buf)); + } + } + + @Override + public boolean isClient() { + return true; + } + + @Override + public IMessageHandler getHandler() { + return (message, ctx) -> { + Minecraft.getMinecraft().addScheduledTask(() -> { + if (Minecraft.getMinecraft().currentScreen instanceof GuiGasTerminal) { + ((GuiGasTerminal) Minecraft.getMinecraft().currentScreen).postUpdate(message.list); + } + }); + return null; + }; + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/sync/IActionHolder.java b/src/main/java/com/mekeng/github/network/packet/sync/IActionHolder.java new file mode 100644 index 0000000..dc3e6cd --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/sync/IActionHolder.java @@ -0,0 +1,17 @@ +package com.mekeng.github.network.packet.sync; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +import javax.annotation.Nonnull; +import java.util.Map; +import java.util.function.Consumer; + +public interface IActionHolder { + @Nonnull + Map> getActionMap(); + + default Map> createHolder() { + return new Object2ObjectOpenHashMap<>(); + } + +} diff --git a/src/main/java/com/mekeng/github/network/packet/sync/ParaSerializer.java b/src/main/java/com/mekeng/github/network/packet/sync/ParaSerializer.java new file mode 100644 index 0000000..4d33607 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/sync/ParaSerializer.java @@ -0,0 +1,116 @@ +package com.mekeng.github.network.packet.sync; + +import com.mekeng.github.MekEng; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.common.me.data.impl.AEGasStack; +import io.netty.buffer.ByteBuf; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; + +import java.io.IOException; + +public final class ParaSerializer { + + ////////////////////////////// + // // + // Serializer Zone // + // // + ////////////////////////////// + + public static void to(Object[] obj, ByteBuf buf) { + PacketBuffer buffer = new PacketBuffer(buf); + buffer.writeByte(obj.length); + for (Object o : obj) { + if (o == null) { + buffer.writeByte(PT.VOID.ordinal()); + } else if (o instanceof Integer) { + buffer.writeByte(PT.INT.ordinal()); + buffer.writeInt((int) o); + } else if (o instanceof Long) { + buffer.writeByte(PT.LONG.ordinal()); + buffer.writeLong((long) o); + } else if (o instanceof Short) { + buffer.writeByte(PT.SHORT.ordinal()); + buffer.writeShort((short) o); + } else if (o instanceof Boolean) { + buffer.writeByte(PT.BOOLEN.ordinal()); + buffer.writeBoolean((boolean) o); + } else if (o instanceof String) { + buffer.writeByte(PT.STRING.ordinal()); + buffer.writeString((String) o); + } else if (o instanceof ItemStack) { + buffer.writeByte(PT.STACK.ordinal()); + buffer.writeItemStack((ItemStack) o); + } else if (o instanceof NBTTagCompound) { + buffer.writeByte(PT.NBT.ordinal()); + buffer.writeCompoundTag((NBTTagCompound) o); + } else if (o instanceof IAEGasStack) { + buffer.writeByte(PT.AE_GAS_STACK.ordinal()); + try { + ((IAEGasStack) o).writeToPacket(buf); + } catch (IOException e) { + MekEng.log.debug(e); + } + } else { + throw new IllegalArgumentException("Args contains invalid type: " + o.getClass().getName()); + } + } + } + + public static Object[] from(ByteBuf buf) { + PacketBuffer buffer = new PacketBuffer(buf); + Object[] objs = new Object[buffer.readByte()]; + try { + for (int i = 0; i < objs.length; i ++) { + switch (PT.values()[buffer.readByte()]) { + case VOID: + objs[i] = null; + break; + case INT : + objs[i] = buffer.readInt(); + break; + case LONG : + objs[i] = buffer.readLong(); + break; + case SHORT : + objs[i] = buffer.readShort(); + break; + case BOOLEN : + objs[i] = buffer.readBoolean(); + break; + case STRING : + objs[i] = buffer.readString(1024); + break; + case STACK : + objs[i] = buffer.readItemStack(); + break; + case NBT: + objs[i] = buffer.readCompoundTag(); + break; + case AE_GAS_STACK: + objs[i] = AEGasStack.of(buffer); + break; + default : + throw new IllegalArgumentException("Args contains unknown type."); + } + } + } catch (IOException e) { + MekEng.log.debug(e); + } + return objs; + } + + private enum PT { + VOID, + INT, + LONG, + SHORT, + BOOLEN, + STRING, + STACK, + NBT, + AE_GAS_STACK + } + +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/network/packet/sync/Paras.java b/src/main/java/com/mekeng/github/network/packet/sync/Paras.java new file mode 100644 index 0000000..cb415a1 --- /dev/null +++ b/src/main/java/com/mekeng/github/network/packet/sync/Paras.java @@ -0,0 +1,25 @@ +package com.mekeng.github.network.packet.sync; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +@SuppressWarnings("unchecked") +public final class Paras { + private final @Nonnull Object[] paras; + + public Paras(Object[] objs) { + this.paras = objs == null ? new Object[0] : objs; + } + + public T get(int index) { + return (T) this.paras[index]; + } + + public @Nullable T getSoft(int index) { + return index >= 0 && index < this.paras.length ? (T) this.paras[index] : null; + } + + public int getParaAmount() { + return this.paras.length; + } +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/proxy/ClientProxy.java b/src/main/java/com/mekeng/github/proxy/ClientProxy.java new file mode 100644 index 0000000..3df2741 --- /dev/null +++ b/src/main/java/com/mekeng/github/proxy/ClientProxy.java @@ -0,0 +1,41 @@ +package com.mekeng.github.proxy; + +import appeng.api.util.AEColor; +import com.mekeng.github.client.ClientRegistryHandler; +import com.mekeng.github.common.ItemAndBlocks; +import com.mekeng.github.common.RegistryHandler; +import mekanism.api.gas.GasStack; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +public class ClientProxy extends CommonProxy { + + @Override + public RegistryHandler createRegistryHandler() { + return new ClientRegistryHandler(); + } + + @Override + public void preInit(FMLPreInitializationEvent event) { + super.preInit(event); + } + + @Override + public void init(FMLInitializationEvent event) { + super.init(event); + Minecraft.getMinecraft().getItemColors().registerItemColorHandler((s, i) -> AEColor.TRANSPARENT.getVariantByTintIndex(i), ItemAndBlocks.GAS_TERMINAL); + Minecraft.getMinecraft().getItemColors().registerItemColorHandler((s, i) -> AEColor.TRANSPARENT.getVariantByTintIndex(i), ItemAndBlocks.GAS_INTERFACE_TERMINAL); + Minecraft.getMinecraft().getItemColors().registerItemColorHandler((s, i) -> { + GasStack gas = ItemAndBlocks.DUMMY_GAS.getGasStack(s); + return gas != null ? gas.getGas().getTint() : 0xFFFFFFFF; + }, ItemAndBlocks.DUMMY_GAS); + } + + @Override + public void postInit(FMLPostInitializationEvent event){ + super.postInit(event); + } + +} \ No newline at end of file diff --git a/src/main/java/com/mekeng/github/proxy/CommonProxy.java b/src/main/java/com/mekeng/github/proxy/CommonProxy.java new file mode 100644 index 0000000..f62de77 --- /dev/null +++ b/src/main/java/com/mekeng/github/proxy/CommonProxy.java @@ -0,0 +1,84 @@ +package com.mekeng.github.proxy; + +import appeng.api.AEApi; +import appeng.api.config.Upgrades; +import com.mekeng.github.MekEng; +import com.mekeng.github.common.ItemAndBlocks; +import com.mekeng.github.common.RegistryHandler; +import com.mekeng.github.common.container.handler.GuiHandler; +import com.mekeng.github.common.me.storage.IGasStorageChannel; +import com.mekeng.github.common.me.storage.impl.GasCellGuiHandler; +import com.mekeng.github.common.me.storage.impl.GasStorageChannel; +import com.mekeng.github.network.Packets; +import mekanism.common.MekanismBlocks; +import mekanism.common.MekanismItems; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper; + +public class CommonProxy { + + public final RegistryHandler regHandler = createRegistryHandler(); + public final SimpleNetworkWrapper netHandler = NetworkRegistry.INSTANCE.newSimpleChannel(MekEng.MODID); + + public RegistryHandler createRegistryHandler() { + return new RegistryHandler(); + } + + public void preInit(FMLPreInitializationEvent event) { + MinecraftForge.EVENT_BUS.register(regHandler); + ItemAndBlocks.init(regHandler); + AEApi.instance().storage().registerStorageChannel(IGasStorageChannel.class, GasStorageChannel.INSTANCE); + Packets.init(); + } + + public void init(FMLInitializationEvent event) { + regHandler.onInit(); + AEApi.instance().registries().cell().addCellGuiHandler(GasCellGuiHandler.INSTANCE); + AEApi.instance().registries().wireless().registerWirelessHandler(ItemAndBlocks.WIRELESS_GAS_TERMINAL); + } + + public void postInit(FMLPostInitializationEvent event) { + NetworkRegistry.INSTANCE.registerGuiHandler(MekEng.INSTANCE, GuiHandler.INSTANCE); + this.loadP2P(); + this.loadUpgrades(); + } + + private void loadP2P() { + AEApi.instance().registries().p2pTunnel().addNewAttunement(new ItemStack(MekanismBlocks.GasTank), MekEng.GAS); + AEApi.instance().registries().p2pTunnel().addNewAttunement(new ItemStack(MekanismItems.Flamethrower), MekEng.GAS); + AEApi.instance().registries().p2pTunnel().addNewAttunement(new ItemStack(MekanismItems.GaugeDropper), MekEng.GAS); + AEApi.instance().registries().p2pTunnel().addNewAttunement(new ItemStack(MekanismItems.Jetpack), MekEng.GAS); + AEApi.instance().registries().p2pTunnel().addNewAttunement(new ItemStack(MekanismItems.ScubaTank), MekEng.GAS); + } + + private void loadUpgrades() { + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_1k), 1); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_1k), 1); + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_4k), 1); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_4k), 1); + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_16k), 1); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_16k), 1); + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_64k), 1); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.GAS_CELL_64k), 1); + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.PORTABLE_GAS_CELL), 1); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.PORTABLE_GAS_CELL), 1); + Upgrades.CAPACITY.registerItem(new ItemStack(ItemAndBlocks.GAS_IMPORT_BUS), 2); + Upgrades.REDSTONE.registerItem(new ItemStack(ItemAndBlocks.GAS_IMPORT_BUS), 1); + Upgrades.SPEED.registerItem(new ItemStack(ItemAndBlocks.GAS_IMPORT_BUS), 4); + Upgrades.CAPACITY.registerItem(new ItemStack(ItemAndBlocks.GAS_EXPORT_BUS), 2); + Upgrades.REDSTONE.registerItem(new ItemStack(ItemAndBlocks.GAS_EXPORT_BUS), 1); + Upgrades.SPEED.registerItem(new ItemStack(ItemAndBlocks.GAS_EXPORT_BUS), 4); + Upgrades.CAPACITY.registerItem(new ItemStack(ItemAndBlocks.GAS_INTERFACE), 2); + Upgrades.CAPACITY.registerItem(new ItemStack(ItemAndBlocks.GAS_INTERFACE_PART), 2); + Upgrades.INVERTER.registerItem(new ItemStack(ItemAndBlocks.GAS_STORAGE_BUS), 1); + Upgrades.CAPACITY.registerItem(new ItemStack(ItemAndBlocks.GAS_STORAGE_BUS), 5); + Upgrades.STICKY.registerItem(new ItemStack(ItemAndBlocks.GAS_STORAGE_BUS), 1); + Upgrades.MAGNET.registerItem(new ItemStack(ItemAndBlocks.WIRELESS_GAS_TERMINAL), 1); + } + +} diff --git a/src/main/java/com/mekeng/github/util/Ae2Reflect.java b/src/main/java/com/mekeng/github/util/Ae2Reflect.java new file mode 100644 index 0000000..293bbd8 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/Ae2Reflect.java @@ -0,0 +1,137 @@ +package com.mekeng.github.util; + +import appeng.api.parts.IPartModel; +import appeng.container.ContainerOpenContext; +import appeng.parts.p2p.PartP2PTunnel; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; + +public class Ae2Reflect { + + private static final Field fContainerOpenContext_x; + private static final Field fContainerOpenContext_y; + private static final Field fContainerOpenContext_z; + private static final Method mPartP2PTunnel_setOutput; + private static final Method mP2PModels_getModel; + private static final Method mP2PModels_getModels; + private static final Constructor cP2PModels; + + static { + try { + fContainerOpenContext_x = reflectField(ContainerOpenContext.class, "x"); + fContainerOpenContext_y = reflectField(ContainerOpenContext.class, "y"); + fContainerOpenContext_z = reflectField(ContainerOpenContext.class, "z"); + mPartP2PTunnel_setOutput = reflectMethod(PartP2PTunnel.class, "setOutput", boolean.class); + mP2PModels_getModel = reflectMethod(Class.forName("appeng.parts.p2p.P2PModels"), "getModel", boolean.class, boolean.class); + mP2PModels_getModels = reflectMethod(Class.forName("appeng.parts.p2p.P2PModels"), "getModels"); + cP2PModels = Class.forName("appeng.parts.p2p.P2PModels").getDeclaredConstructor(String.class); + cP2PModels.setAccessible(true); + } catch (Exception e) { + throw new IllegalStateException("Failed to initialize AE2 reflection hacks!", e); + } + } + + public static Method reflectMethod(Class owner, String name, Class... paramTypes) throws NoSuchMethodException { + return reflectMethod(owner, new String[]{name}, paramTypes); + } + + @SuppressWarnings("all") + public static Method reflectMethod(Class owner, String[] names, Class... paramTypes) throws NoSuchMethodException { + Method m = null; + for (String name : names) { + try { + m = owner.getDeclaredMethod(name, paramTypes); + if (m != null) break; + } + catch (NoSuchMethodException ignore) { + } + } + if (m == null) throw new NoSuchMethodException("Can't find field from " + Arrays.toString(names)); + m.setAccessible(true); + return m; + } + + @SuppressWarnings("all") + public static Field reflectField(Class owner, String ...names) throws NoSuchFieldException { + Field f = null; + for (String name : names) { + try { + f = owner.getDeclaredField(name); + if (f != null) break; + } + catch (NoSuchFieldException ignore) { + } + } + if (f == null) throw new NoSuchFieldException("Can't find field from " + Arrays.toString(names)); + f.setAccessible(true); + return f; + } + + @SuppressWarnings("unchecked") + public static T readField(Object owner, Field field) { + try { + return (T)field.get(owner); + } catch (Exception e) { + throw new IllegalStateException("Failed to read field: " + field); + } + } + + public static void writeField(Object owner, Field field, Object value) { + try { + field.set(owner, value); + } catch (Exception e) { + throw new IllegalStateException("Failed to write field: " + field); + } + } + + @SuppressWarnings("unchecked") + public static T create(Constructor c, Object... para) { + try { + return (T) c.newInstance(para); + } catch (Exception e) { + throw new IllegalStateException("Failed to create instance: " + c); + } + } + + @SuppressWarnings("unchecked") + public static T executeMethod(Object owner, Method method, Object... para) { + try { + return (T) method.invoke(owner, para); + } catch (Exception e) { + throw new IllegalStateException("Failed to execute method: " + method); + } + } + + public static int getContextX(ContainerOpenContext owner) { + return readField(owner, fContainerOpenContext_x); + } + + public static int getContextY(ContainerOpenContext owner) { + return readField(owner, fContainerOpenContext_y); + } + + public static int getContextZ(ContainerOpenContext owner) { + return readField(owner, fContainerOpenContext_z); + } + + public static Object createP2PModel(String name) { + return create(cP2PModels, name); + } + + public static IPartModel getP2PModel(Object owner, boolean hasPower, boolean hasChannel) { + return executeMethod(owner, mP2PModels_getModel, hasPower, hasChannel); + } + + public static List getP2PModel(Object owner) { + return executeMethod(owner, mP2PModels_getModels); + } + + public static void setP2POutput(Object owner, boolean value) { + executeMethod(owner, mPartP2PTunnel_setOutput, value); + } + +} diff --git a/src/main/java/com/mekeng/github/util/Utils.java b/src/main/java/com/mekeng/github/util/Utils.java new file mode 100644 index 0000000..bb0ca1f --- /dev/null +++ b/src/main/java/com/mekeng/github/util/Utils.java @@ -0,0 +1,87 @@ +package com.mekeng.github.util; + +import appeng.container.interfaces.IInventorySlotAware; +import com.mekeng.github.common.container.handler.GuiFactory; +import com.mekeng.github.common.container.handler.GuiHandler; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.common.me.inventory.IExtendedGasHandler; +import com.mekeng.github.util.helpers.ItemGasHandler; +import mekanism.api.gas.Gas; +import mekanism.api.gas.GasStack; +import mekanism.api.gas.IGasHandler; +import mekanism.api.gas.IGasItem; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; + +public class Utils { + + public static GasStack getGasFromItem(ItemStack stack) { + IGasItem gh = getGasHandler(stack); + if (gh != null) { + return gh.getGas(stack); + } + return null; + } + + public static ItemGasHandler getItemGasHandler(ItemStack stack) { + IGasItem handler = getGasHandler(stack); + if (handler != null) { + return new ItemGasHandler(handler, stack); + } + return null; + } + + public static IGasItem getGasHandler(ItemStack stack) { + if (!stack.isEmpty() && stack.getItem() instanceof IGasItem) { + return (IGasItem) stack.getItem(); + } + return null; + } + + public static String getGasModID(IAEGasStack stack) { + return stack == null ? "** Null" : getGasModID(stack.getGasStack()); + } + + public static String getGasModID(GasStack stack) { + if (stack != null) { + Gas gas = stack.getGas(); + if (gas != null) { + return gas.getIcon().getNamespace(); + } + } + return "** Null"; + } + + public static String getGasDisplayName(IAEGasStack stack) { + return stack == null ? "** Null" : getGasDisplayName(stack.getGasStack()); + } + + public static String getGasDisplayName(GasStack stack) { + if (stack != null) { + Gas gas = stack.getGas(); + if (gas != null) { + return gas.getLocalizedName(); + } + } + return "** Null"; + } + + public static GasStack drawGas(IGasHandler handler, GasStack stack, EnumFacing side, int amount, boolean doTransfer) { + if (handler instanceof IExtendedGasHandler) { + GasStack draw = stack.copy().withAmount(amount); + return ((IExtendedGasHandler) handler).drawGas(side, draw, doTransfer); + } + return handler.drawGas(side, amount, doTransfer); + } + + public static void openItemGui(EntityPlayer player, GuiFactory gui) { + if (player.openContainer instanceof IInventorySlotAware) { + IInventorySlotAware c = (IInventorySlotAware) player.openContainer; + GuiHandler.openItemGui(player, player.world, c.getInventorySlot(), c.isBaubleSlot(), gui); + } else { + GuiHandler.openItemGui(player, player.world, player.inventory.currentItem, false, gui); + } + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/GasCellConfig.java b/src/main/java/com/mekeng/github/util/helpers/GasCellConfig.java new file mode 100644 index 0000000..1ef8f90 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/GasCellConfig.java @@ -0,0 +1,67 @@ +package com.mekeng.github.util.helpers; + +import appeng.items.contents.CellConfig; +import com.mekeng.github.common.ItemAndBlocks; +import com.mekeng.github.common.item.ItemDummyGas; +import com.mekeng.github.util.Utils; +import mekanism.api.gas.GasStack; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nonnull; + +public class GasCellConfig extends CellConfig { + public GasCellConfig(ItemStack is) { + super(is); + } + + @Override + @Nonnull + public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) { + if (stack.isEmpty() || stack.getItem() == ItemAndBlocks.DUMMY_GAS) { + super.insertItem(slot, stack, simulate); + } + GasStack gas = Utils.getGasFromItem(stack); + if (gas == null) { + return stack; + } + gas.amount = 1000; + ItemStack is = new ItemStack(ItemAndBlocks.DUMMY_GAS); + ItemDummyGas item = (ItemDummyGas) is.getItem(); + item.setGasStack(is, gas); + return super.insertItem(slot, is, simulate); + } + + @Override + public void setStackInSlot(int slot, @Nonnull ItemStack stack) { + if (stack.isEmpty() || stack.getItem() == ItemAndBlocks.DUMMY_GAS) { + super.setStackInSlot(slot, stack); + } + GasStack gas = Utils.getGasFromItem(stack); + if (gas == null) { + return; + } + + gas.amount = 1000; + ItemStack is = new ItemStack(ItemAndBlocks.DUMMY_GAS); + ItemDummyGas item = (ItemDummyGas) is.getItem(); + item.setGasStack(is, gas); + super.setStackInSlot(slot, is); + } + + @Override + public boolean isItemValid(int slot, ItemStack stack) { + if (stack.isEmpty() || stack.getItem() == ItemAndBlocks.DUMMY_GAS) { + super.isItemValid(slot, stack); + } + GasStack gas = Utils.getGasFromItem(stack); + if (gas == null) { + return false; + } + gas.amount = 1000; + ItemStack is = new ItemStack(ItemAndBlocks.DUMMY_GAS); + ItemDummyGas item = (ItemDummyGas) is.getItem(); + item.setGasStack(is, gas); + return super.isItemValid(slot, is); + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/GasCellInfo.java b/src/main/java/com/mekeng/github/util/helpers/GasCellInfo.java new file mode 100644 index 0000000..92110a3 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/GasCellInfo.java @@ -0,0 +1,115 @@ +package com.mekeng.github.util.helpers; + +import appeng.api.config.IncludeExclude; +import appeng.api.storage.ICellInventory; +import appeng.api.storage.ICellInventoryHandler; +import appeng.api.storage.data.IAEStack; +import appeng.api.storage.data.IItemList; +import appeng.core.localization.GuiText; +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.inventory.IGasInventory; +import com.mekeng.github.util.Utils; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; +import net.minecraftforge.items.IItemHandler; +import org.lwjgl.input.Keyboard; + +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; + +public class GasCellInfo { + + private static final String[] NUMBER_FORMATS = new String[]{"#.000", "#.00", "#.0", "#"}; + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static > void addCellInformation(ICellInventoryHandler handler, List lines) { + if (handler == null) { + return; + } + + final ICellInventory cellInventory = handler.getCellInv(); + + if (cellInventory != null) { + lines.add(cellInventory.getUsedBytes() + " " + GuiText.Of.getLocal() + ' ' + cellInventory.getTotalBytes() + ' ' + GuiText.BytesUsed.getLocal()); + lines.add(cellInventory.getStoredItemTypes() + " " + GuiText.Of.getLocal() + ' ' + cellInventory.getTotalItemTypes() + ' ' + GuiText.Types + .getLocal()); + } + + if (cellInventory == null) { + return; + } + + IItemList itemList = cellInventory.getChannel().createList(); + + if (handler.isPreformatted()) { + final String list = (handler.getIncludeExcludeMode() == IncludeExclude.WHITELIST ? GuiText.Included : GuiText.Excluded).getLocal(); + + if (handler.isFuzzy()) { + lines.add("[" + GuiText.Partitioned.getLocal() + "]" + " - " + list + ' ' + GuiText.Fuzzy.getLocal()); + } else { + lines.add("[" + GuiText.Partitioned.getLocal() + "]" + " - " + list + ' ' + GuiText.Precise.getLocal()); + } + + if (handler.isSticky()) { + lines.add(GuiText.Sticky.getLocal()); + } + + if (Minecraft.getMinecraft().gameSettings.advancedItemTooltips || Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + IItemHandler inv = cellInventory.getConfigInventory(); + cellInventory.getAvailableItems((IItemList) itemList); + for (int i = 0; i < inv.getSlots(); i++) { + final ItemStack is = inv.getStackInSlot(i); + if (!is.isEmpty()) { + if (cellInventory.getChannel() instanceof IGasInventory) { + final AEGasStack ais; + if (is.getItem() == ItemAndBlocks.DUMMY_GAS) { + ais = AEGasStack.of(ItemAndBlocks.DUMMY_GAS.getGasStack(is)); + } else { + ais = AEGasStack.of(Utils.getGasFromItem(is)); + } + IAEGasStack stocked = ((IItemList) itemList).findPrecise(ais); + lines.add("[" + is.getDisplayName() + "]" + ": " + (stocked == null ? "0" : gasStackSize(stocked.getStackSize()))); + } + } + } + } + } else { + if (Minecraft.getMinecraft().gameSettings.advancedItemTooltips || Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT)) { + cellInventory.getAvailableItems((IItemList) itemList); + for (IAEStack s : itemList) { + if (s instanceof IAEGasStack) { + lines.add(((IAEGasStack) s).getGasStack().getGas().getLocalizedName() + ": " + gasStackSize(s.getStackSize())); + } + } + } + } + } + + public static String gasStackSize(long size) { + String unit; + if (size >= 1000) { + unit = "B"; + } else { + unit = "mB"; + } + + final int log = (int) Math.floor(Math.log10(size)) / 2; + + 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); + + String formatted = format.format(size / 1000d); + + return formatted.concat(unit); + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/GasSorters.java b/src/main/java/com/mekeng/github/util/helpers/GasSorters.java new file mode 100644 index 0000000..27ef3f3 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/GasSorters.java @@ -0,0 +1,55 @@ +package com.mekeng.github.util.helpers; + +import appeng.api.config.SortDir; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.util.Utils; + +import java.util.Comparator; + +public class GasSorters { + + private static SortDir Direction = SortDir.ASCENDING; + + public static final Comparator CONFIG_BASED_SORT_BY_NAME = (o1, o2) -> + { + if (getDirection() == SortDir.ASCENDING) { + return Utils.getGasDisplayName(o1).compareToIgnoreCase(Utils.getGasDisplayName(o2)); + } + return Utils.getGasDisplayName(o2).compareToIgnoreCase(Utils.getGasDisplayName(o1)); + }; + + public static final Comparator CONFIG_BASED_SORT_BY_MOD = new Comparator() { + + @Override + public int compare(final IAEGasStack o1, final IAEGasStack o2) { + if (getDirection() == SortDir.ASCENDING) { + return this.secondarySort(Utils.getGasModID(o1).compareToIgnoreCase(Utils.getGasModID(o2)), o2, o1); + } + return this.secondarySort(Utils.getGasModID(o1).compareToIgnoreCase(Utils.getGasModID(o2)), o1, o2); + } + + private int secondarySort(final int compareToIgnoreCase, final IAEGasStack o1, final IAEGasStack o2) { + if (compareToIgnoreCase == 0) { + return Utils.getGasDisplayName(o2).compareToIgnoreCase(Utils.getGasDisplayName(o1)); + } + return compareToIgnoreCase; + } + }; + + public static final Comparator CONFIG_BASED_SORT_BY_SIZE = (o1, o2) -> + { + if (getDirection() == SortDir.ASCENDING) { + return Long.compare(o2.getStackSize(), o1.getStackSize()); + } + return Long.compare(o1.getStackSize(), o2.getStackSize()); + }; + + private static SortDir getDirection() { + return Direction; + } + + public static void setDirection(final SortDir direction) { + Direction = direction; + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/GasSyncHelper.java b/src/main/java/com/mekeng/github/util/helpers/GasSyncHelper.java new file mode 100644 index 0000000..5577dc4 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/GasSyncHelper.java @@ -0,0 +1,89 @@ +package com.mekeng.github.util.helpers; + +import com.mekeng.github.MekEng; +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.common.me.inventory.impl.GasInventory; +import com.mekeng.github.network.packet.SGasSlotSync; +import mekanism.api.gas.GasStack; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IContainerListener; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public final class GasSyncHelper { + + private final IGasInventory inv; + private final IGasInventory cache; + private final int idOffset; + + public static GasSyncHelper create(final IGasInventory inv, final int idOffset) { + return new GasSyncHelper(inv, idOffset); + } + + private GasSyncHelper(final IGasInventory inv, final int idOffset) { + this.inv = inv; + this.cache = new GasInventory(inv.size()); + this.idOffset = idOffset; + } + + public void sendFull(final Iterable listeners) { + this.sendDiffMap(this.createDiffMap(true), listeners); + } + + public void sendDiff(final Iterable listeners) { + this.sendDiffMap(this.createDiffMap(false), listeners); + } + + public void readPacket(final Map data) { + for (int i = 0; i < this.inv.size(); ++i) { + if (data.containsKey(i + this.idOffset)) { + this.inv.setGas(i, data.get(i + this.idOffset) == null ? null : data.get(i + this.idOffset).getGasStack()); + } + } + } + + private void sendDiffMap(final Map data, final Iterable listeners) { + if (data.isEmpty()) { + return; + } + for (final IContainerListener l : listeners) { + if (l instanceof EntityPlayerMP) { + Container c = ((EntityPlayerMP) l).openContainer; + if (c instanceof IGasSyncContainer) { + ((IGasSyncContainer) c).receiveGasSlots(data); + } + MekEng.proxy.netHandler.sendTo(new SGasSlotSync(data), (EntityPlayerMP) l); + } + } + } + + private Map createDiffMap(final boolean full) { + final Map ret = new HashMap<>(); + for (int i = 0; i < this.inv.size(); ++i) { + if (full || !this.equalsSlot(i)) { + ret.put(i + this.idOffset, AEGasStack.of(this.inv.getGasStack(i))); + } + if (!full) { + this.cache.setGas(i, this.inv.getGasStack(i)); + } + } + return ret; + } + + private boolean equalsSlot(int slot) { + final GasStack stackA = this.inv.getGasStack(slot); + final GasStack stackB = this.cache.getGasStack(slot); + + if (!Objects.equals(stackA, stackB)) { + return false; + } + return stackA == null || stackA.amount == stackB.amount; + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/ItemGasHandler.java b/src/main/java/com/mekeng/github/util/helpers/ItemGasHandler.java new file mode 100644 index 0000000..b10122d --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/ItemGasHandler.java @@ -0,0 +1,82 @@ +package com.mekeng.github.util.helpers; + +import mekanism.api.gas.GasStack; +import mekanism.api.gas.IGasItem; +import mekanism.common.base.ITierItem; +import mekanism.common.tier.BaseTier; +import net.minecraft.item.ItemStack; + +import javax.annotation.Nullable; + +public class ItemGasHandler { + + private final IGasItem handler; + private final ItemStack stack; + + public ItemGasHandler(IGasItem handler, ItemStack stack) { + this.handler = handler; + this.stack = stack; + } + + public int addGas(@Nullable GasStack gas, boolean add) { + if (gas == null || !this.handler.canReceiveGas(this.stack, gas.getGas())) { + return 0; + } + int space = this.isCreative() ? Integer.MAX_VALUE : this.capacity() - this.gasAmount(); + int toAdd = Math.min(space, gas.amount); + if (toAdd <= 0) { + return 0; + } + if (add) { + int newAmt = isCreative() ? Integer.MAX_VALUE : toAdd + this.gasAmount(); + this.handler.setGas(this.stack, gas.copy().withAmount(newAmt)); + } + return toAdd; + } + + @Nullable + public GasStack removeGas(@Nullable GasStack gas, boolean drain) { + if (gas == null || !this.handler.canProvideGas(this.stack, gas.getGas())) { + return null; + } + return removeGas(gas.amount, drain); + } + + public GasStack removeGas(int gas, boolean drain) { + int left = isCreative() ? Integer.MAX_VALUE : this.gasAmount(); + int toRemove = Math.min(gas, left); + if (toRemove <= 0 || this.handler.getGas(this.stack) == null) { + return null; + } + GasStack removed = this.handler.getGas(this.stack).copy().withAmount(toRemove); + if (drain && !isCreative()) { + GasStack leftGas = removed.copy().withAmount(left - toRemove); + if (leftGas.amount == 0) { + leftGas = null; + } + this.handler.setGas(this.stack, leftGas); + } + return removed; + } + + public int gasAmount() { + GasStack gas = this.handler.getGas(this.stack); + return gas == null ? 0 : gas.amount; + } + + public int capacity() { + return this.handler.getMaxGas(this.stack); + } + + public ItemStack getContainer() { + return this.stack; + } + + private boolean isCreative() { + if (this.stack.getItem() instanceof ITierItem) { + return ((ITierItem) this.stack.getItem()).getBaseTier(this.stack) == BaseTier.CREATIVE; + } + return false; + } + +} diff --git a/src/main/java/com/mekeng/github/util/helpers/PortableGasCellViewer.java b/src/main/java/com/mekeng/github/util/helpers/PortableGasCellViewer.java new file mode 100644 index 0000000..3fe92f2 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/PortableGasCellViewer.java @@ -0,0 +1,110 @@ +package com.mekeng.github.util.helpers; + +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.items.IAEItemPowerStorage; +import appeng.api.networking.security.IActionSource; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.IStorageChannel; +import appeng.api.storage.data.IAEStack; +import appeng.api.util.IConfigManager; +import appeng.container.interfaces.IInventorySlotAware; +import appeng.me.helpers.MEMonitorHandler; +import appeng.util.ConfigManager; +import appeng.util.Platform; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.common.me.storage.IGasStorageChannel; +import com.mekeng.github.common.me.storage.IPortableGasCell; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import javax.annotation.Nonnull; +import java.util.Collections; + +public class PortableGasCellViewer extends MEMonitorHandler implements IPortableGasCell, IInventorySlotAware { + + private final ItemStack target; + private final IAEItemPowerStorage ips; + private final int inventorySlot; + + public PortableGasCellViewer(final ItemStack is, final int slot) { + super(AEApi.instance().registries().cell().getCellInventory(is, null, AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class))); + this.ips = (IAEItemPowerStorage) is.getItem(); + this.target = is; + this.inventorySlot = slot; + } + + @Override + public int getInventorySlot() { + return this.inventorySlot; + } + + @Override + public ItemStack getItemStack() { + return this.target; + } + + @Override + public double extractAEPower(double amt, @Nonnull final Actionable mode, final PowerMultiplier usePowerMultiplier) { + amt = usePowerMultiplier.multiply(amt); + + if (mode == Actionable.SIMULATE) { + return usePowerMultiplier.divide(Math.min(amt, this.ips.getAECurrentPower(this.target))); + } + + return usePowerMultiplier.divide(this.ips.extractAEPower(this.target, amt, Actionable.MODULATE)); + } + + @Override + public IAEGasStack injectItems(IAEGasStack input, Actionable mode, IActionSource src) { + final long size = input.getStackSize(); + + final IAEGasStack injected = super.injectItems(input, mode, src); + + if (mode == Actionable.MODULATE && (injected == null || injected.getStackSize() != size)) { + this.notifyListenersOfChange(Collections.singletonList(input.copy().setStackSize(input.getStackSize() - (injected == null ? 0 : injected.getStackSize()))), null); + } + + return injected; + } + + @Override + public IAEGasStack extractItems(IAEGasStack request, Actionable mode, IActionSource src) { + final IAEGasStack extractable = super.extractItems(request, mode, src); + + if (mode == Actionable.MODULATE && extractable != null) { + this.notifyListenersOfChange(Collections.singletonList(request.copy().setStackSize(-extractable.getStackSize())), null); + } + + return extractable; + } + + @Override + public > IMEMonitor getInventory(IStorageChannel channel) { + if (channel == AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)) { + return (IMEMonitor) this; + } + return null; + } + + @Override + public IConfigManager getConfigManager() { + final ConfigManager out = new ConfigManager((manager, settingName, newValue) -> + { + final NBTTagCompound data = Platform.openNbtData(PortableGasCellViewer.this.target); + manager.writeToNBT(data); + }); + + out.registerSetting(Settings.SORT_BY, SortOrder.NAME); + out.registerSetting(Settings.VIEW_MODE, ViewItems.ALL); + out.registerSetting(Settings.SORT_DIRECTION, SortDir.ASCENDING); + + out.readFromNBT(Platform.openNbtData(this.target).copy()); + return out; + } +} diff --git a/src/main/java/com/mekeng/github/util/helpers/WirelessGasGuiObj.java b/src/main/java/com/mekeng/github/util/helpers/WirelessGasGuiObj.java new file mode 100644 index 0000000..1788fd1 --- /dev/null +++ b/src/main/java/com/mekeng/github/util/helpers/WirelessGasGuiObj.java @@ -0,0 +1,352 @@ +package com.mekeng.github.util.helpers; + +import appeng.api.AEApi; +import appeng.api.config.AccessRestriction; +import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; +import appeng.api.features.ILocatable; +import appeng.api.features.IWirelessTermHandler; +import appeng.api.implementations.IUpgradeableCellHost; +import appeng.api.implementations.tiles.IWirelessAccessPoint; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IMachineSet; +import appeng.api.networking.security.IActionHost; +import appeng.api.networking.security.IActionSource; +import appeng.api.networking.storage.IStorageGrid; +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.DimensionalCoord; +import appeng.api.util.IConfigManager; +import appeng.container.interfaces.IInventorySlotAware; +import appeng.me.cluster.implementations.QuantumCluster; +import appeng.parts.automation.StackUpgradeInventory; +import appeng.parts.automation.UpgradeInventory; +import appeng.tile.networking.TileWireless; +import appeng.tile.qnb.TileQuantumBridge; +import appeng.util.inv.IAEAppEngInventory; +import appeng.util.inv.InvOperation; +import com.mekeng.github.common.me.data.IAEGasStack; +import com.mekeng.github.common.me.storage.IGasStorageChannel; +import com.mekeng.github.common.me.storage.IPortableGasCell; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.Nonnull; + +public class WirelessGasGuiObj implements IPortableGasCell, IActionHost, IInventorySlotAware, IAEAppEngInventory, IUpgradeableCellHost { + + private final ItemStack effectiveItem; + private final IWirelessTermHandler wth; + private final String encryptionKey; + private final EntityPlayer myPlayer; + private final boolean isBaubleSlot; + private IGrid targetGrid; + private IStorageGrid sg; + private IMEMonitor gasStorage; + private IWirelessAccessPoint myWap; + private double sqRange = Double.MAX_VALUE; + private double myRange = Double.MAX_VALUE; + private final int inventorySlot; + + private final UpgradeInventory upgrades; + private QuantumCluster myQC; + + public WirelessGasGuiObj(final IWirelessTermHandler wh, final ItemStack is, final EntityPlayer ep, final World w, final int x, final int y, final int z) { + this.encryptionKey = wh.getEncryptionKey(is); + this.effectiveItem = is; + this.myPlayer = ep; + this.wth = wh; + this.inventorySlot = x; + this.isBaubleSlot = y == 1; + + ILocatable obj = null; + + try { + final long encKey = Long.parseLong(this.encryptionKey); + obj = AEApi.instance().registries().locatable().getLocatableBy(encKey); + } catch (final NumberFormatException err) { + // :P + } + + if (obj instanceof IActionHost) { + final IGridNode n = ((IActionHost) obj).getActionableNode(); + if (n != null) { + this.targetGrid = n.getGrid(); + if (this.targetGrid != null) { + this.sg = this.targetGrid.getCache(IStorageGrid.class); + if (this.sg != null) { + this.gasStorage = this.sg.getInventory(AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class)); + } + } + } + } + + upgrades = new StackUpgradeInventory(effectiveItem, this, 2); + + this.loadFromNBT(); + } + + public double getRange() { + return this.myRange; + } + + @Override + public > IMEMonitor getInventory(IStorageChannel channel) { + return this.sg.getInventory(channel); + } + + @Override + public void addListener(final IMEMonitorHandlerReceiver l, final Object verificationToken) { + if (this.gasStorage != null) { + this.gasStorage.addListener(l, verificationToken); + } + } + + @Override + public void removeListener(final IMEMonitorHandlerReceiver l) { + if (this.gasStorage != null) { + this.gasStorage.removeListener(l); + } + } + + @Override + public IItemList getAvailableItems(final IItemList out) { + if (this.gasStorage != null) { + return this.gasStorage.getAvailableItems(out); + } + return out; + } + + @Override + public IItemList getStorageList() { + if (this.gasStorage != null) { + return this.gasStorage.getStorageList(); + } + return null; + } + + @Override + public AccessRestriction getAccess() { + if (this.gasStorage != null) { + return this.gasStorage.getAccess(); + } + return AccessRestriction.NO_ACCESS; + } + + @Override + public boolean isPrioritized(final IAEGasStack input) { + if (this.gasStorage != null) { + return this.gasStorage.isPrioritized(input); + } + return false; + } + + @Override + public boolean canAccept(final IAEGasStack input) { + if (this.gasStorage != null) { + return this.gasStorage.canAccept(input); + } + return false; + } + + @Override + public int getPriority() { + if (this.gasStorage != null) { + return this.gasStorage.getPriority(); + } + return 0; + } + + @Override + public int getSlot() { + if (this.gasStorage != null) { + return this.gasStorage.getSlot(); + } + return 0; + } + + @Override + public boolean validForPass(final int i) { + return this.gasStorage.validForPass(i); + } + + @Override + public IAEGasStack injectItems(final IAEGasStack input, final Actionable type, final IActionSource src) { + if (this.gasStorage != null) { + return this.gasStorage.injectItems(input, type, src); + } + return input; + } + + @Override + public IAEGasStack extractItems(final IAEGasStack request, final Actionable mode, final IActionSource src) { + if (this.gasStorage != null) { + return this.gasStorage.extractItems(request, mode, src); + } + return null; + } + + @Override + public IStorageChannel getChannel() { + if (this.gasStorage != null) { + return this.gasStorage.getChannel(); + } + return AEApi.instance().storage().getStorageChannel(IGasStorageChannel.class); + } + + @Override + public double extractAEPower(final double amt, @Nonnull final Actionable mode, @Nonnull final PowerMultiplier usePowerMultiplier) { + if (this.wth != null && this.effectiveItem != null) { + if (mode == Actionable.SIMULATE) { + return this.wth.hasPower(this.myPlayer, amt, this.effectiveItem) ? amt : 0; + } + return this.wth.usePower(this.myPlayer, amt, this.effectiveItem) ? amt : 0; + } + return 0.0; + } + + @Override + public ItemStack getItemStack() { + return this.effectiveItem; + } + + @Override + public IConfigManager getConfigManager() { + return this.wth.getConfigManager(this.effectiveItem); + } + + @Nonnull + @Override + public IGridNode getActionableNode() { + this.rangeCheck(); + if (this.myWap != null) { + return this.myWap.getActionableNode(); + } else if (this.myQC != null && this.myQC.getCenter().isPowered()) { + return this.myQC.getCenter().getActionableNode(); + } + return null; + } + + public boolean rangeCheck() { + this.sqRange = this.myRange = Double.MAX_VALUE; + + if (this.targetGrid != null && this.gasStorage != null) { + if (this.myWap != null) { + if (this.myWap.getGrid() == this.targetGrid) { + return this.testWap(this.myWap); + } + return false; + } + + IMachineSet tw = this.targetGrid.getMachines(TileWireless.class); + + this.myWap = null; + this.myQC = null; + + for (final IGridNode n : tw) { + final IWirelessAccessPoint wap = (IWirelessAccessPoint) n.getMachine(); + if (this.testWap(wap)) { + this.myWap = wap; + } + } + + if (myWap != null) return true; + + tw = this.targetGrid.getMachines(TileQuantumBridge.class); + for (final IGridNode n : tw) { + TileQuantumBridge tqb = (TileQuantumBridge) n.getMachine(); + if (tqb.getCluster() != null) { + TileQuantumBridge center = ((QuantumCluster) tqb.getCluster()).getCenter(); + if (center != null) { + if (center.getInternalInventory().getStackInSlot(1).isItemEqual(AEApi.instance().definitions().materials().cardQuantumLink().maybeStack(1).get())) { + myQC = (QuantumCluster) tqb.getCluster(); + myRange = 1; + return true; + } + } + } + } + + return this.myWap != null || this.myQC != null; + } + return false; + } + + private boolean testWap(final IWirelessAccessPoint wap) { + double rangeLimit = wap.getRange(); + rangeLimit *= rangeLimit; + + final DimensionalCoord dc = wap.getLocation(); + + if (dc.getWorld() == this.myPlayer.world) { + final double offX = dc.x - this.myPlayer.posX; + final double offY = dc.y - this.myPlayer.posY; + final double offZ = dc.z - this.myPlayer.posZ; + + final double r = offX * offX + offY * offY + offZ * offZ; + if (r < rangeLimit && this.sqRange > r) { + if (wap.isActive()) { + this.sqRange = r; + this.myRange = Math.sqrt(r); + return true; + } + } + } + return false; + } + + @Override + public int getInventorySlot() { + return this.inventorySlot; + } + + @Override + public boolean isBaubleSlot() { + return isBaubleSlot; + } + + @Override + public void saveChanges() { + NBTTagCompound data = effectiveItem.getTagCompound(); + if (data == null) { + data = new NBTTagCompound(); + } + upgrades.writeToNBT(data, "upgrades"); + } + + public void saveChanges(NBTTagCompound data) { + if (effectiveItem.getTagCompound() != null) { + effectiveItem.getTagCompound().merge(data); + } else { + effectiveItem.setTagCompound(data); + } + } + + public void loadFromNBT() { + NBTTagCompound data = effectiveItem.getTagCompound(); + if (data != null) { + upgrades.readFromNBT(data); + } + } + + @Override + public void onChangeInventory(IItemHandler inv, int slot, InvOperation mc, ItemStack removedStack, ItemStack newStack) { + + } + + @Override + public IItemHandler getInventoryByName(String name) { + if (name.equals("upgrades")) { + return upgrades; + } + return null; + } + +} diff --git a/src/main/java/com/mekeng/github/xmod/jei/GasCellGuiHandler.java b/src/main/java/com/mekeng/github/xmod/jei/GasCellGuiHandler.java new file mode 100644 index 0000000..f8cea53 --- /dev/null +++ b/src/main/java/com/mekeng/github/xmod/jei/GasCellGuiHandler.java @@ -0,0 +1,60 @@ +package com.mekeng.github.xmod.jei; + +import appeng.client.gui.implementations.GuiCellWorkbench; +import appeng.container.slot.SlotFake; +import appeng.fluids.client.gui.widgets.GuiFluidSlot; +import com.mekeng.github.common.ItemAndBlocks; +import mekanism.api.gas.GasStack; +import mezz.jei.api.gui.IGhostIngredientHandler; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.item.ItemStack; +import org.lwjgl.input.Mouse; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; + +@SuppressWarnings({"unchecked", "rawtypes"}) +public class GasCellGuiHandler implements IGhostIngredientHandler { + + public static final GasCellGuiHandler INSTANCE = new GasCellGuiHandler(); + + private GasCellGuiHandler() { + // NO-OP + } + + @Nonnull + @Override + public List> getTargets(@Nonnull GuiCellWorkbench gui, @Nonnull I ingredient, boolean doStart) { + ArrayList targets = new ArrayList<>(); + if (ingredient instanceof GasStack) { + ItemStack stack = new ItemStack(ItemAndBlocks.DUMMY_GAS); + ItemAndBlocks.DUMMY_GAS.setGasStack(stack, (GasStack) ingredient); + targets.addAll(gui.getPhantomTargets(stack)); + } else { + targets.addAll(gui.getPhantomTargets(ingredient)); + } + if (doStart && GuiScreen.isShiftKeyDown() && Mouse.isButtonDown(0)) { + for (Object o : targets) { + Target target = (Target) o; + if (gui.getFakeSlotTargetMap().get(target) instanceof SlotFake) { + if (((SlotFake) gui.getFakeSlotTargetMap().get(target)).getStack().isEmpty()) { + target.accept(ingredient); + break; + } + } else if (gui.getFakeSlotTargetMap().get(target) instanceof GuiFluidSlot) { + if (((GuiFluidSlot) gui.getFakeSlotTargetMap().get(target)).getFluidStack() == null) { + target.accept(ingredient); + break; + } + } + } + } + return targets; + } + + @Override + public void onComplete() { + + } +} diff --git a/src/main/java/com/mekeng/github/xmod/jei/MkEJei.java b/src/main/java/com/mekeng/github/xmod/jei/MkEJei.java new file mode 100644 index 0000000..3d989d2 --- /dev/null +++ b/src/main/java/com/mekeng/github/xmod/jei/MkEJei.java @@ -0,0 +1,24 @@ +package com.mekeng.github.xmod.jei; + +import appeng.client.gui.implementations.GuiCellWorkbench; +import com.mekeng.github.common.ItemAndBlocks; +import mezz.jei.api.IModPlugin; +import mezz.jei.api.IModRegistry; +import mezz.jei.api.JEIPlugin; +import mezz.jei.api.ingredients.IIngredientBlacklist; +import net.minecraft.item.ItemStack; + +import javax.annotation.ParametersAreNonnullByDefault; + +@JEIPlugin +@ParametersAreNonnullByDefault +public class MkEJei implements IModPlugin { + + @Override + public void register(IModRegistry registry) { + IIngredientBlacklist blacklist = registry.getJeiHelpers().getIngredientBlacklist(); + blacklist.addIngredientToBlacklist(new ItemStack(ItemAndBlocks.DUMMY_GAS)); + registry.addGhostIngredientHandler(GuiCellWorkbench.class, GasCellGuiHandler.INSTANCE); + } + +} diff --git a/src/main/resources/assets/appliedenergistics2/models/part/p2p/p2p_tunnel_gases.json b/src/main/resources/assets/appliedenergistics2/models/part/p2p/p2p_tunnel_gases.json new file mode 100644 index 0000000..808ccd2 --- /dev/null +++ b/src/main/resources/assets/appliedenergistics2/models/part/p2p/p2p_tunnel_gases.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:part/p2p/p2p_tunnel_base", + "textures": { + "type": "mekeng:parts/p2p/gases_p2p" + } +} diff --git a/src/main/resources/assets/appliedenergistics2/textures/guis/gas_bus.png b/src/main/resources/assets/appliedenergistics2/textures/guis/gas_bus.png new file mode 100644 index 0000000000000000000000000000000000000000..dad729fff664c1b94615987cab262051e7a599b7 GIT binary patch literal 1381 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJD%9Zwg>kcv5P@Al{3a*$|G zv_2J*(rdsG^esWam|e2_)gl+)PG+{|(|6xjE?KoIB=*O1b35~Yx612ftyVbKKMU0R z8EN-DV%OospU$h_pSN4Xe}4Xc`G3coTe82@)>{-l(ESx@_c=oD-sI-_x88jJ{cigE zPu=y?{B5RbeL1dw>-X>KwdY#}ZomEZxRdYj1=-r~v41w#Jla@OA5dN|b9`AI#Ha@SEMhq){aO}5paJvZFUNwYtF{rdHHPoF-0a$ocJ-<7}LyDKmp(O~}| zlb@fTzFzlA$%pc73=@1A6lO3u7%(;@Ffkki>gHr%5oX{}V-WDclrZLR;7Ap7*g1cG zJtu=;+3vf?*IW9qG3oXI9K*I1})W$M{V1teZ?=J=+*u3ihVWVGuaz zvY?R3fk(Kpu80?_$LjbP-o2SvZ+1Wu?CCUahVqbiygRmAM)NQ>oa%K=%M!_9gayw! zMyP=-!gG#8tzu#bzm>QB>i&{-j29;Dd;j}z-TwSX4`P~(8KN$)4_oaz|B?aE)9t&C zG1eEcIduO@lz8{w`A220UG{a%UcZ?ytld}7f4pHqnnbnvgp9@~IoL3XP?7rAxgDL#)kAJysBy9h-xaL*q zOe@hFE())0{#XIi!}??;0fTO0(dB&Fk-UxVG^e>uX?g+SdnUcSam{ zS@5ychlMFk5Q~4XrVU_j@L}0>?(q7{w?3~-QxdRQ{J4?}YXa$&OW)AD$P2Q+%l5htJ#Q`3ASf#HTZ)&RW&acy&j~FWtv|@dut{ ruDWvcAV09ABOZLjskCACt6lH-Gbi4cVk?qu2PyM(^>bP0l+XkK){X#M literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/blockstates/gas_interface.json b/src/main/resources/assets/mekeng/blockstates/gas_interface.json new file mode 100644 index 0000000..c688d59 --- /dev/null +++ b/src/main/resources/assets/mekeng/blockstates/gas_interface.json @@ -0,0 +1,7 @@ +{ + "variants": { + "normal": { + "model": "mekeng:gas_interface" + } + } +} diff --git a/src/main/resources/assets/mekeng/lang/en_us.lang b/src/main/resources/assets/mekeng/lang/en_us.lang new file mode 100644 index 0000000..6e98770 --- /dev/null +++ b/src/main/resources/assets/mekeng/lang/en_us.lang @@ -0,0 +1,38 @@ +itemGroup.mekeng=Mekanism Energistics + +item.mekeng:dummy_gas.name=Dummy Gas +item.mekeng:dummy_gas.error.name=Invalid Gas +item.mekeng:gas_core_1k.name=§61k§r ME Gas Storage Component +item.mekeng:gas_core_4k.name=§e4k§r ME Gas Storage Component +item.mekeng:gas_core_16k.name=§a16k§r ME Gas Storage Component +item.mekeng:gas_core_64k.name=§b64k§r ME Gas Storage Component +item.mekeng:gas_cell_1k.name=§61k§r ME Gas Storage Cell +item.mekeng:gas_cell_4k.name=§e4k§r ME Gas Storage Cell +item.mekeng:gas_cell_16k.name=§a16k§r ME Gas Storage Cell +item.mekeng:gas_cell_64k.name=§b64k§r ME Gas Storage Cell +item.mekeng:portable_gas_cell.name=Portable Gas Cell +item.mekeng:gas_terminal.name=ME Gas Terminal +item.mekeng:gas_import_bus.name=ME Gas Import Bus +item.mekeng:gas_export_bus.name=ME Gas Export Bus +item.mekeng:gas_interface_part.name=ME Gas Interface +item.mekeng:gas_storage_bus.name=ME Gas Storage Bus +item.mekeng:gas_level_emitter.name=ME Gas Level Emitter +item.mekeng:gas_interface_terminal.name=Gas Interface Configuration Terminal +item.mekeng:wireless_gas_terminal.name=Wireless Gas Terminal +item.mekeng:gas_p2p.name=P2P Tunnel - Gas + +tile.mekeng:gas_interface.name=ME Gas Interface + +gui.mekeng.gas_terminal=Gas Terminal +gui.mekeng.gas_import=ME Gas Import Bus +gui.mekeng.gas_export=ME Gas Export Bus +gui.mekeng.gas_interface=ME Gas Interface +gui.mekeng.gas_storage_bus=ME Gas Storage Bus +gui.mekeng.gas_level_emitter=ME Gas Level Emitter +gui.mekeng.gas_interface_terminal=Gas Interface Configuration Terminal +gui.mekeng.gas_portable_cell=Portable Cell +gui.mekeng.wireless_gas_terminal=Wireless Term + +tooltip.mekeng.stored_gas=Stored Gas + +group.mekeng.gas_io_bus=ME Gas Import/Export Bus diff --git a/src/main/resources/assets/mekeng/models/block/gas_interface.json b/src/main/resources/assets/mekeng/models/block/gas_interface.json new file mode 100644 index 0000000..bd4338e --- /dev/null +++ b/src/main/resources/assets/mekeng/models/block/gas_interface.json @@ -0,0 +1,6 @@ +{ + "parent": "block/cube_all", + "textures": { + "all": "mekeng:blocks/gas_interface" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_cell_16k.json b/src/main/resources/assets/mekeng/models/item/gas_cell_16k.json new file mode 100644 index 0000000..6eeb8f9 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_cell_16k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_cell_16k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_cell_1k.json b/src/main/resources/assets/mekeng/models/item/gas_cell_1k.json new file mode 100644 index 0000000..34a463a --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_cell_1k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_cell_1k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_cell_4k.json b/src/main/resources/assets/mekeng/models/item/gas_cell_4k.json new file mode 100644 index 0000000..51df1ab --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_cell_4k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_cell_4k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_cell_64k.json b/src/main/resources/assets/mekeng/models/item/gas_cell_64k.json new file mode 100644 index 0000000..36fbecc --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_cell_64k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_cell_64k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_core_16k.json b/src/main/resources/assets/mekeng/models/item/gas_core_16k.json new file mode 100644 index 0000000..70539b9 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_core_16k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_core_16k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_core_1k.json b/src/main/resources/assets/mekeng/models/item/gas_core_1k.json new file mode 100644 index 0000000..9345b24 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_core_1k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_core_1k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_core_4k.json b/src/main/resources/assets/mekeng/models/item/gas_core_4k.json new file mode 100644 index 0000000..87039a5 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_core_4k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_core_4k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_core_64k.json b/src/main/resources/assets/mekeng/models/item/gas_core_64k.json new file mode 100644 index 0000000..26839b5 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_core_64k.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/gas_core_64k" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_export_bus.json b/src/main/resources/assets/mekeng/models/item/gas_export_bus.json new file mode 100644 index 0000000..f88ca4a --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_export_bus.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:item/part/fluid_export_bus", + "textures": { + "front": "mekeng:parts/io_bus/gas_export_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/item/gas_import_bus.json b/src/main/resources/assets/mekeng/models/item/gas_import_bus.json new file mode 100644 index 0000000..1aa5a45 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_import_bus.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:item/part/fluid_import_bus", + "textures": { + "front": "mekeng:parts/io_bus/gas_import_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/item/gas_interface.json b/src/main/resources/assets/mekeng/models/item/gas_interface.json new file mode 100644 index 0000000..e03b32e --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_interface.json @@ -0,0 +1,3 @@ +{ + "parent": "mekeng:block/gas_interface" +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_interface_part.json b/src/main/resources/assets/mekeng/models/item/gas_interface_part.json new file mode 100644 index 0000000..ef08696 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_interface_part.json @@ -0,0 +1,8 @@ +{ + "parent": "appliedenergistics2:item/part/fluid_interface", + "textures": { + "sides": "mekeng:parts/interface/sides", + "back": "mekeng:parts/interface/back", + "front": "mekeng:parts/interface/front" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_interface_terminal.json b/src/main/resources/assets/mekeng/models/item/gas_interface_terminal.json new file mode 100644 index 0000000..54be464 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_interface_terminal.json @@ -0,0 +1,9 @@ +{ + "parent": "appliedenergistics2:item/part/display", + "textures": { + "front": "appliedenergistics2:items/part/interface_terminal", + "front_bright": "mekeng:parts/interface_terminal/bright", + "front_medium": "mekeng:parts/interface_terminal/medium", + "front_dark": "mekeng:parts/interface_terminal/dark" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_level_emitter.json b/src/main/resources/assets/mekeng/models/item/gas_level_emitter.json new file mode 100644 index 0000000..fa75ac5 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_level_emitter.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:item/part/fluid_level_emitter" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/item/gas_p2p.json b/src/main/resources/assets/mekeng/models/item/gas_p2p.json new file mode 100644 index 0000000..c3656ca --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_p2p.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:item/part/p2p_tunnel", + "textures": { + "type": "mekeng:parts/p2p/gases_p2p" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/gas_storage_bus.json b/src/main/resources/assets/mekeng/models/item/gas_storage_bus.json new file mode 100644 index 0000000..ae7f7fa --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_storage_bus.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:item/part/fluid_storage_bus", + "textures": { + "front": "mekeng:parts/gas_storage_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/item/gas_terminal.json b/src/main/resources/assets/mekeng/models/item/gas_terminal.json new file mode 100644 index 0000000..a9debb3 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/gas_terminal.json @@ -0,0 +1,9 @@ +{ + "parent": "appliedenergistics2:item/part/display", + "textures": { + "front": "appliedenergistics2:items/part/crafting_terminal", + "front_bright": "mekeng:parts/gas_terminal/lights_bright", + "front_medium": "mekeng:parts/gas_terminal/lights_medium", + "front_dark": "mekeng:parts/gas_terminal/lights_dark" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/portable_gas_cell.json b/src/main/resources/assets/mekeng/models/item/portable_gas_cell.json new file mode 100644 index 0000000..b069cd2 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/portable_gas_cell.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/portable_gas_cell" + } +} diff --git a/src/main/resources/assets/mekeng/models/item/wireless_gas_terminal.json b/src/main/resources/assets/mekeng/models/item/wireless_gas_terminal.json new file mode 100644 index 0000000..7c578b9 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/item/wireless_gas_terminal.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "mekeng:items/wireless_gas_terminal" + } +} diff --git a/src/main/resources/assets/mekeng/models/part/gas_export_bus_base.json b/src/main/resources/assets/mekeng/models/part/gas_export_bus_base.json new file mode 100644 index 0000000..43426ef --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_export_bus_base.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:part/fluid_export_bus_base", + "textures": { + "front": "mekeng:parts/io_bus/gas_export_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_export_bus_has_channel.json b/src/main/resources/assets/mekeng/models/part/gas_export_bus_has_channel.json new file mode 100644 index 0000000..8d2c19f --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_export_bus_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_export_bus_has_channel" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_export_bus_off.json b/src/main/resources/assets/mekeng/models/part/gas_export_bus_off.json new file mode 100644 index 0000000..824c09f --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_export_bus_off.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_export_bus_off" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_export_bus_on.json b/src/main/resources/assets/mekeng/models/part/gas_export_bus_on.json new file mode 100644 index 0000000..b944993 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_export_bus_on.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_export_bus_on" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_import_bus_base.json b/src/main/resources/assets/mekeng/models/part/gas_import_bus_base.json new file mode 100644 index 0000000..8b9da49 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_import_bus_base.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:part/fluid_import_bus_base", + "textures": { + "front": "mekeng:parts/io_bus/gas_import_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_import_bus_has_channel.json b/src/main/resources/assets/mekeng/models/part/gas_import_bus_has_channel.json new file mode 100644 index 0000000..708f752 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_import_bus_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_import_bus_has_channel" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_import_bus_off.json b/src/main/resources/assets/mekeng/models/part/gas_import_bus_off.json new file mode 100644 index 0000000..1d0934f --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_import_bus_off.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_import_bus_off" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_import_bus_on.json b/src/main/resources/assets/mekeng/models/part/gas_import_bus_on.json new file mode 100644 index 0000000..4cbd55a --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_import_bus_on.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_import_bus_on" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_base.json b/src/main/resources/assets/mekeng/models/part/gas_interface_base.json new file mode 100644 index 0000000..a6f0440 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_base.json @@ -0,0 +1,9 @@ +{ + "parent": "appliedenergistics2:part/fluid_interface_base", + "textures": { + "sides": "mekeng:parts/interface/sides", + "back": "mekeng:parts/interface/back", + "front": "mekeng:parts/interface/front", + "particle": "mekeng:parts/interface/back" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_off.json b/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_off.json new file mode 100644 index 0000000..f8a9965 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_off.json @@ -0,0 +1,8 @@ +{ + "parent": "appliedenergistics2:part/display_off", + "textures": { + "lightsBright": "mekeng:parts/interface_terminal/bright", + "lightsMedium": "mekeng:parts/interface_terminal/medium", + "lightsDark": "mekeng:parts/interface_terminal/dark" + } +} diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_on.json b/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_on.json new file mode 100644 index 0000000..06e9276 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_configuration_terminal_on.json @@ -0,0 +1,76 @@ +{ + "ae2_uvl_marker": true, + "textures": { + "lightsBright": "mekeng:parts/interface_terminal/bright", + "lightsMedium": "mekeng:parts/interface_terminal/medium", + "lightsDark": "mekeng:parts/interface_terminal/dark" + }, + "elements": [ + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsBright", + "tintindex": 3, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + }, + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsMedium", + "tintindex": 2, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + }, + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsDark", + "tintindex": 1, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_has_channel.json b/src/main/resources/assets/mekeng/models/part/gas_interface_has_channel.json new file mode 100644 index 0000000..c50dfea --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_interface_has_channel" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_off.json b/src/main/resources/assets/mekeng/models/part/gas_interface_off.json new file mode 100644 index 0000000..f745238 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_off.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_interface_off" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_interface_on.json b/src/main/resources/assets/mekeng/models/part/gas_interface_on.json new file mode 100644 index 0000000..20c6ad5 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_interface_on.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_interface_on" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_storage_bus_base.json b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_base.json new file mode 100644 index 0000000..a3c27e7 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_base.json @@ -0,0 +1,6 @@ +{ + "parent": "appliedenergistics2:part/fluid_storage_bus_base", + "textures": { + "front": "mekeng:parts/gas_storage_bus" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_storage_bus_has_channel.json b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_has_channel.json new file mode 100644 index 0000000..dd2560f --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_storage_bus_has_channel" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_storage_bus_off.json b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_off.json new file mode 100644 index 0000000..606cf22 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_off.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_storage_bus_off" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_storage_bus_on.json b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_on.json new file mode 100644 index 0000000..7bb4360 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_storage_bus_on.json @@ -0,0 +1,3 @@ +{ + "parent": "appliedenergistics2:part/fluid_storage_bus_on" +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/models/part/gas_terminal_off.json b/src/main/resources/assets/mekeng/models/part/gas_terminal_off.json new file mode 100644 index 0000000..1141f47 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_terminal_off.json @@ -0,0 +1,8 @@ +{ + "parent": "appliedenergistics2:part/display_off", + "textures": { + "lightsBright": "mekeng:parts/gas_terminal/lights_bright", + "lightsMedium": "mekeng:parts/gas_terminal/lights_medium", + "lightsDark": "mekeng:parts/gas_terminal/lights_dark" + } +} diff --git a/src/main/resources/assets/mekeng/models/part/gas_terminal_on.json b/src/main/resources/assets/mekeng/models/part/gas_terminal_on.json new file mode 100644 index 0000000..2d2a3b1 --- /dev/null +++ b/src/main/resources/assets/mekeng/models/part/gas_terminal_on.json @@ -0,0 +1,76 @@ +{ + "ae2_uvl_marker": true, + "textures": { + "lightsBright": "mekeng:parts/gas_terminal/lights_bright", + "lightsMedium": "mekeng:parts/gas_terminal/lights_medium", + "lightsDark": "mekeng:parts/gas_terminal/lights_dark" + }, + "elements": [ + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsBright", + "tintindex": 3, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + }, + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsMedium", + "tintindex": 2, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + }, + { + "from": [ + 2, + 2, + 0 + ], + "to": [ + 14, + 14, + 2 + ], + "faces": { + "north": { + "texture": "#lightsDark", + "tintindex": 1, + "uvlightmap": { + "sky": 0.007, + "block": 0.007 + } + } + } + } + ] +} diff --git a/src/main/resources/assets/mekeng/recipes/_constants.json b/src/main/resources/assets/mekeng/recipes/_constants.json new file mode 100644 index 0000000..af2426e --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/_constants.json @@ -0,0 +1,30 @@ +[ + { + "name": "appliedenergistics2:certusCrystal", + "ingredient": [ + { + "type": "forge:ore_dict", + "ore": "crystalCertusQuartz" + }, + { + "type": "appliedenergistics2:part", + "part": "material.certus_quartz_crystal_charged" + }, + { + "type": "appliedenergistics2:part", + "part": "material.purified_certus_quartz_crystal" + } + ] + }, + { + "name": "appliedenergistics2:gas_interface", + "ingredient": [ + { + "item": "mekeng:gas_interface" + }, + { + "item": "mekeng:gas_interface_part" + } + ] + } +] \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/recipes/cell/16k.json b/src/main/resources/assets/mekeng/recipes/cell/16k.json new file mode 100644 index 0000000..0e182f2 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/16k.json @@ -0,0 +1,28 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:gas_core_16k", + "count": 1 + }, + "pattern": [ + "aba", + "cdc", + "aca" + ], + "key": { + "d": { + "item": "appliedenergistics2:quartz_glass" + }, + "a": { + "type": "forge:ore_dict", + "ore": "dustRefinedObsidian" + }, + "c": { + "item": "mekeng:gas_core_4k" + }, + "b": { + "type": "appliedenergistics2:part", + "part": "material.calculation_processor" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/16k_asm.json b/src/main/resources/assets/mekeng/recipes/cell/16k_asm.json new file mode 100644 index 0000000..b483f32 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/16k_asm.json @@ -0,0 +1,15 @@ +{ + "type": "forge:ore_shapeless", + "result": { + "item": "mekeng:gas_cell_16k" + }, + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "material.empty_storage_cell" + }, + { + "item": "mekeng:gas_core_16k" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/recipes/cell/16k_cell.json b/src/main/resources/assets/mekeng/recipes/cell/16k_cell.json new file mode 100644 index 0000000..21fde06 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/16k_cell.json @@ -0,0 +1,28 @@ +{ + "type": "forge:ore_shaped", + "result": { + "item": "mekeng:gas_cell_16k", + "count": 1 + }, + "pattern": [ + "aba", + "bcb", + "ddd" + ], + "key": { + "a": { + "item": "appliedenergistics2:quartz_glass" + }, + "d": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "b": { + "type": "forge:ore_dict", + "ore": "dustRedstone" + }, + "c": { + "item": "mekeng:gas_core_16k" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/1k.json b/src/main/resources/assets/mekeng/recipes/cell/1k.json new file mode 100644 index 0000000..9dd57f1 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/1k.json @@ -0,0 +1,25 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:gas_core_1k", + "count": 1 + }, + "pattern": [ + "aba", + "bcb", + "aba" + ], + "key": { + "a": { + "type": "forge:ore_dict", + "ore": "dustGold" + }, + "b": { + "item": "#appliedenergistics2:certusCrystal" + }, + "c": { + "type": "appliedenergistics2:part", + "part": "material.logic_processor" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/1k_asm.json b/src/main/resources/assets/mekeng/recipes/cell/1k_asm.json new file mode 100644 index 0000000..67b201a --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/1k_asm.json @@ -0,0 +1,15 @@ +{ + "type": "forge:ore_shapeless", + "result": { + "item": "mekeng:gas_cell_1k" + }, + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "material.empty_storage_cell" + }, + { + "item": "mekeng:gas_core_1k" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/recipes/cell/1k_cell.json b/src/main/resources/assets/mekeng/recipes/cell/1k_cell.json new file mode 100644 index 0000000..bb512ba --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/1k_cell.json @@ -0,0 +1,28 @@ +{ + "type": "forge:ore_shaped", + "result": { + "item": "mekeng:gas_cell_1k", + "count": 1 + }, + "pattern": [ + "aba", + "bcb", + "ddd" + ], + "key": { + "a": { + "item": "appliedenergistics2:quartz_glass" + }, + "d": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "b": { + "type": "forge:ore_dict", + "ore": "dustRedstone" + }, + "c": { + "item": "mekeng:gas_core_1k" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/4k.json b/src/main/resources/assets/mekeng/recipes/cell/4k.json new file mode 100644 index 0000000..bc7bd1b --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/4k.json @@ -0,0 +1,28 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:gas_core_4k", + "count": 1 + }, + "pattern": [ + "aba", + "cdc", + "aca" + ], + "key": { + "d": { + "item": "appliedenergistics2:quartz_glass" + }, + "a": { + "type": "forge:ore_dict", + "ore": "dustGold" + }, + "c": { + "item": "mekeng:gas_core_1k" + }, + "b": { + "type": "appliedenergistics2:part", + "part": "material.calculation_processor" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/4k_asm.json b/src/main/resources/assets/mekeng/recipes/cell/4k_asm.json new file mode 100644 index 0000000..254286e --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/4k_asm.json @@ -0,0 +1,15 @@ +{ + "type": "forge:ore_shapeless", + "result": { + "item": "mekeng:gas_cell_4k" + }, + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "material.empty_storage_cell" + }, + { + "item": "mekeng:gas_core_4k" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/recipes/cell/4k_cell.json b/src/main/resources/assets/mekeng/recipes/cell/4k_cell.json new file mode 100644 index 0000000..bb4650c --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/4k_cell.json @@ -0,0 +1,28 @@ +{ + "type": "forge:ore_shaped", + "result": { + "item": "mekeng:gas_cell_4k", + "count": 1 + }, + "pattern": [ + "aba", + "bcb", + "ddd" + ], + "key": { + "a": { + "item": "appliedenergistics2:quartz_glass" + }, + "d": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "b": { + "type": "forge:ore_dict", + "ore": "dustRedstone" + }, + "c": { + "item": "mekeng:gas_core_4k" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/64k.json b/src/main/resources/assets/mekeng/recipes/cell/64k.json new file mode 100644 index 0000000..f905a53 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/64k.json @@ -0,0 +1,28 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:gas_core_64k", + "count": 1 + }, + "pattern": [ + "aba", + "cdc", + "aca" + ], + "key": { + "d": { + "item": "appliedenergistics2:quartz_glass" + }, + "a": { + "type": "forge:ore_dict", + "ore": "dustRefinedObsidian" + }, + "c": { + "item": "mekeng:gas_core_16k" + }, + "b": { + "type": "appliedenergistics2:part", + "part": "material.calculation_processor" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/64k_asm.json b/src/main/resources/assets/mekeng/recipes/cell/64k_asm.json new file mode 100644 index 0000000..28f47f2 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/64k_asm.json @@ -0,0 +1,15 @@ +{ + "type": "forge:ore_shapeless", + "result": { + "item": "mekeng:gas_cell_64k" + }, + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "material.empty_storage_cell" + }, + { + "item": "mekeng:gas_core_64k" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/mekeng/recipes/cell/64k_cell.json b/src/main/resources/assets/mekeng/recipes/cell/64k_cell.json new file mode 100644 index 0000000..09a814f --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/64k_cell.json @@ -0,0 +1,28 @@ +{ + "type": "forge:ore_shaped", + "result": { + "item": "mekeng:gas_cell_64k", + "count": 1 + }, + "pattern": [ + "aba", + "bcb", + "ddd" + ], + "key": { + "a": { + "item": "appliedenergistics2:quartz_glass" + }, + "d": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "b": { + "type": "forge:ore_dict", + "ore": "dustRedstone" + }, + "c": { + "item": "mekeng:gas_core_64k" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/cell/portable.json b/src/main/resources/assets/mekeng/recipes/cell/portable.json new file mode 100644 index 0000000..305dea6 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/cell/portable.json @@ -0,0 +1,21 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:portable_gas_cell", + "count": 1 + }, + "pattern": [ + "tce" + ], + "key": { + "t": { + "item": "appliedenergistics2:chest" + }, + "c": { + "item": "mekeng:gas_core_1k" + }, + "e": { + "item": "appliedenergistics2:energy_cell" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal.json b/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal.json new file mode 100644 index 0000000..751e558 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal.json @@ -0,0 +1,15 @@ +{ + "result": { + "item": "mekeng:gas_interface_terminal" + }, + "type": "appliedenergistics2:part_shapeless", + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "part.interface_terminal" + }, + { + "item": "mekanism:enrichedalloy" + } + ] +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal_revert.json b/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal_revert.json new file mode 100644 index 0000000..584e4e4 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_configuration_terminal_revert.json @@ -0,0 +1,12 @@ +{ + "result": { + "type": "appliedenergistics2:part", + "part": "part.interface_terminal" + }, + "type": "appliedenergistics2:part_shapeless", + "ingredients": [ + { + "item": "mekeng:gas_interface_terminal" + } + ] +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_export_bus.json b/src/main/resources/assets/mekeng/recipes/gas_export_bus.json new file mode 100644 index 0000000..7f8c3ab --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_export_bus.json @@ -0,0 +1,27 @@ +{ + "result": { + "item": "mekeng:gas_export_bus" + }, + "type": "appliedenergistics2:part_shaped", + "pattern": [ + "aba", + "gcg" + ], + "key": { + "b": { + "type": "appliedenergistics2:part", + "part": "material.formation_core" + }, + "a": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "c": { + "item": "minecraft:piston" + }, + "g": { + "type": "forge:ore_dict", + "ore": "dustGold" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_import_bus.json b/src/main/resources/assets/mekeng/recipes/gas_import_bus.json new file mode 100644 index 0000000..07fe2db --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_import_bus.json @@ -0,0 +1,27 @@ +{ + "result": { + "item": "mekeng:gas_import_bus" + }, + "type": "appliedenergistics2:part_shaped", + "pattern": [ + "gag", + "bcb" + ], + "key": { + "c": { + "item": "minecraft:sticky_piston" + }, + "b": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "a": { + "type": "appliedenergistics2:part", + "part": "material.annihilation_core" + }, + "g": { + "type": "forge:ore_dict", + "ore": "dustGold" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_interface.json b/src/main/resources/assets/mekeng/recipes/gas_interface.json new file mode 100644 index 0000000..23852cb --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_interface.json @@ -0,0 +1,30 @@ +{ + "type": "appliedenergistics2:part_shaped", + "result": { + "item": "mekeng:gas_interface", + "count": 1 + }, + "pattern": [ + "igi", + "a f", + "igi" + ], + "key": { + "f": { + "type": "appliedenergistics2:part", + "part": "material.formation_core" + }, + "i": { + "type": "forge:ore_dict", + "ore": "ingotIron" + }, + "g": { + "type": "forge:ore_dict", + "ore": "dustGold" + }, + "a": { + "type": "appliedenergistics2:part", + "part": "material.annihilation_core" + } + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_interface_alt.json b/src/main/resources/assets/mekeng/recipes/gas_interface_alt.json new file mode 100644 index 0000000..49ea5e8 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_interface_alt.json @@ -0,0 +1,11 @@ +{ + "type": "forge:ore_shapeless", + "ingredients": [ + { + "item": "mekeng:gas_interface_part" + } + ], + "result": { + "item": "mekeng:gas_interface" + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_interface_part.json b/src/main/resources/assets/mekeng/recipes/gas_interface_part.json new file mode 100644 index 0000000..dec0393 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_interface_part.json @@ -0,0 +1,11 @@ +{ + "type": "forge:ore_shapeless", + "ingredients": [ + { + "item": "mekeng:gas_interface" + } + ], + "result": { + "item": "mekeng:gas_interface_part" + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_interface_terminal.json b/src/main/resources/assets/mekeng/recipes/gas_interface_terminal.json new file mode 100644 index 0000000..1bd1cc4 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_interface_terminal.json @@ -0,0 +1,20 @@ +{ + "type": "forge:ore_shapeless", + "ingredients": [ + { + "type": "appliedenergistics2:part", + "part": "part.terminal" + }, + { + "type": "forge:ore_dict", + "ore": "dyeYellow" + }, + { + "type": "appliedenergistics2:part", + "part": "material.logic_processor" + } + ], + "result": { + "item": "mekeng:gas_terminal" + } +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_level_emitter.json b/src/main/resources/assets/mekeng/recipes/gas_level_emitter.json new file mode 100644 index 0000000..eda920f --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_level_emitter.json @@ -0,0 +1,19 @@ +{ + "result": { + "item": "mekeng:gas_level_emitter" + }, + "type": "appliedenergistics2:part_shapeless", + "ingredients": [ + { + "item": "minecraft:redstone_torch" + }, + { + "type": "forge:ore_dict", + "ore": "dustGold" + }, + { + "type": "appliedenergistics2:part", + "part": "material.calculation_processor" + } + ] +} diff --git a/src/main/resources/assets/mekeng/recipes/gas_storage_bus.json b/src/main/resources/assets/mekeng/recipes/gas_storage_bus.json new file mode 100644 index 0000000..78f56a1 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/gas_storage_bus.json @@ -0,0 +1,21 @@ +{ + "result": { + "item": "mekeng:gas_storage_bus" + }, + "type": "appliedenergistics2:part_shapeless", + "ingredients": [ + { + "item": "minecraft:sticky_piston" + }, + { + "item": "#appliedenergistics2:gas_interface" + }, + { + "item": "minecraft:piston" + }, + { + "type": "forge:ore_dict", + "ore": "dustGold" + } + ] +} diff --git a/src/main/resources/assets/mekeng/recipes/wireless_gas_terminal.json b/src/main/resources/assets/mekeng/recipes/wireless_gas_terminal.json new file mode 100644 index 0000000..7cee1b6 --- /dev/null +++ b/src/main/resources/assets/mekeng/recipes/wireless_gas_terminal.json @@ -0,0 +1,23 @@ +{ + "result": { + "item": "mekeng:wireless_gas_terminal" + }, + "type": "forge:ore_shaped", + "pattern": [ + "a", + "b", + "c" + ], + "key": { + "b": { + "item": "mekeng:gas_terminal" + }, + "c": { + "item": "appliedenergistics2:dense_energy_cell" + }, + "a": { + "type": "appliedenergistics2:part", + "part": "material.wireless" + } + } +} diff --git a/src/main/resources/assets/mekeng/textures/blocks/gas_interface.png b/src/main/resources/assets/mekeng/textures/blocks/gas_interface.png new file mode 100644 index 0000000000000000000000000000000000000000..9d2955eb7e10ab2c0eaf8d7264900e039c04eca6 GIT binary patch literal 311 zcmV-70m%M|P)H-x+*^q8P{l+cvIbVEORzGgzyJh6YJS+_-TA>}nQd!Bsnoh|(P9(FdkMUS=Z8 z?MQ$H6e#Gx%1#EW;$w%NpztYn0XSAbrr-ju_C{cg3xJ}W37Z#40WA1ju>0US!rlNm zgIw_Y_iqLkP!dH4=%I}_5J734Z2z;cpr%Av3L_SP!jFZQhYPG8l(tC)pd88QV5$HA z>-V1wpTB$qXH$@uKz{r4=MR_-0yIUHbY6azp26nN-{ple2t9k2mUpM1n=2rkXYdN2YJ7EsvO=z++H zZV0k1=;?p~OIlz-HUJ{A1S1uKfU8sogI<^nMnXafM3^D4FhB;_G-G%H-8-vHSfBMe=#^?qj8^Q!4K$5Zwf;e4<4nS%_df*1Y{0Z_Sj1BVt zy@&N+3}PdP1B{Jg00@8r58V)S&FHZMHsJAt_c#r~r5UCN9wq2;kE{V*GjepI#3{NV z=;;7mGYGIC8vv15jFAdKz*D}F!8l3@BO###BFqq27$5^|nlZe9?j2a#00kJ{B#a9j jxRm!FgmEfhU|;|Mwp+XdUA31#00000NkvXXu0mjf{<3R? literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/items/gas_cell_64k.png b/src/main/resources/assets/mekeng/textures/items/gas_cell_64k.png new file mode 100644 index 0000000000000000000000000000000000000000..d63abfa325069586a28ac478bb521007f44d3624 GIT binary patch literal 259 zcmV+e0sQ`nP)ghr$Pn>1^_ph4?x)(X-@zE002ov JPDHLkV1g&nVs-!k literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/items/gas_core_16k.png b/src/main/resources/assets/mekeng/textures/items/gas_core_16k.png new file mode 100644 index 0000000000000000000000000000000000000000..fd21b4e8cf04685f2bff1b5d400ee532d9697185 GIT binary patch literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`D?MEtLo9le7g)`(EPhsB>|AQV zw(;W67vBsOi>0Kb{uLJ&AO5LeW+lIbZ8hTyp&tyJqTJY4Y)&-0yR*36(bbjp92;Yb zUC-aIufyBEd?|4g6G*?;W4EJr=h3Y^*W_+1%#n>t;F@W_bGu}@!~|xIZ%jw^B-j!e z%otb_I+>R+JoA}Q?!YjU-+0Tyg^X*&EmaTkDzi+uy68Ep+l0#r3`g9aPfij{JaFIu a1A~}g&HL+zUljly&EVCG$ zC5{|>8myMn#LLV3f9KAf4{HRZRotd9?htu2DcR*&!yK;7KhMpz=F!s9l6bQ|e!tO@ z<;xF$-KZdKknbixXKr-N>4di%c@N!uc+_C$cFEPNH?%QoHYDjAEQ)61)|kLHW3%Hu zMoFfr?iZVN#SD0ks<&&)8Z^z}VpcG-Yi>76NJwB{@So**@UqT^H9+?v1!Y#eMHSB+a;Un|04S5~?xRO@;sf literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/items/gas_core_64k.png b/src/main/resources/assets/mekeng/textures/items/gas_core_64k.png new file mode 100644 index 0000000000000000000000000000000000000000..4fae19d4a9304f90b6e569fe0341a180a781e73e GIT binary patch literal 304 zcmV-00nh%4P)7zTm7_{wD!0fAs=Aej!z}K%|8Pe0!8CgJ_c{BRJg1N=DLj^!6meuPpu_?>0PF%#9D`_2|45=@ zVf)7AVD`!LHz9!t10Y4P&}Kl21yEQZiz5TD0ZAD}C<%rT0L2z43{Iw}Q=X9Efd~_T z#WT#GAOK<`$33!I^t`|bavBVP*ywy@&6EQM1_l6N{h1SLh|y&L0000Yeu#i1lG*m3g?5^Th1;f4>UgHgW{?_;K)ieBHHIM|4KRO$3`Exq(}NO-$O>ATmoOkFBy>4gAfm)6 zx*;%sg2D%zW|Tk#0f@vB6hmOZR-uByJy{2TLV_6rvIQMr(~RK-bnn2@1}MOAWhT7f j&i#)6AdFKH0|Ns9T?C_AeDH}C00000NkvXXu0mjfi^GA{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/items/wireless_gas_terminal.png b/src/main/resources/assets/mekeng/textures/items/wireless_gas_terminal.png new file mode 100644 index 0000000000000000000000000000000000000000..b4453c09ecb875b0fcbea1a809836a06dae7841f GIT binary patch literal 325 zcmV-L0lNN)P)}^y%~08Btuo$iT>O;^b)t365-rEj#uzh_Yodh_OTP=I#5y z7$jCv(}LX#qGDoTdfvtmh9v(rD2iZwklfj`XBouB#BqlKvSMuFVAnD+V;aDSS&aHw?di|AC}L1_lNI XPSb99b+#eb00000NkvXXu0mjf52B1l literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/gas_storage_bus.png b/src/main/resources/assets/mekeng/textures/parts/gas_storage_bus.png new file mode 100644 index 0000000000000000000000000000000000000000..e394b573c70ba91d760bf5b84fb85802fc3920a2 GIT binary patch literal 242 zcmV zV}_oTQXr)St##xw3Ltqe>fDJARQY;&rogl`av`Kzw|nc5K?g+Ug2mMn+&IV=6fIJ_+SqFmflQ{C_&S4Dq}&Z1;15HT scW$S>&a>9WJ0Z*KL!B20UOpx!ozjK6#xJL07*qoM6N<$g4;G_RR910 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_bright.png b/src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_bright.png new file mode 100644 index 0000000000000000000000000000000000000000..f09eaa1440f6f67404a2353c3f36e35307c08d4a GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`QJyZ2Ar`%NCmrN#P~dS6fBgS{ zQLnhskX>+p wl_$T!>nE(4OpDbn{V-tq+nn)l@v2u$Usni-EnEM6A!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a5n0T@z_$#98Qbry z%m)fembgZg1m~xflqVLYG880c=9TE>rIsj|=o#o48GcmAoC;JV?znGnfDX literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_medium.png b/src/main/resources/assets/mekeng/textures/parts/gas_terminal/lights_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..c89bdf63fb96b6d28a97dae4b8b845a47649d853 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`37#&FAr`&K62BS*@3Q~@|Nr>Y zmHz|2{+E=LeE2KkgGS57M25pg0tttd+I)?TZ&{i zZp<6m9&|CTVSD_EY1<3t%ds1^JqsUvVCUiCVQ9X_zbyOb_GLi37(8A5T-G@yGywn% C?Kctt literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/interface/back.png b/src/main/resources/assets/mekeng/textures/parts/interface/back.png new file mode 100644 index 0000000000000000000000000000000000000000..5bc00ce29e08814cc1a37aa3a283522ec38fa7bb GIT binary patch literal 374 zcmV-+0g3*JP)}pzJ(Mczyy#;hS~%mI*;&XYZ}XCD zAYbUZ&adk#<2cH`?-B%oF7LhWL{StHMUm8Xt;UcC2m%+a`VA3+yB>%TnSvmT8(~ zS(YPX2Pa5i4bH(N7=}U52-+PBbN_Os1_aw$FjbNO@MSTbb+F$graVh%vk1>d5^$$9 zTMtX%tWHb9K>q*cr^IA>xPDawCe6+TwPICOrM9Qr_d}j`!A=)iMMvI<9mF`}Ki9tp zDa6B|MjW73_-0!Pq)my)gqQw!jhusXYf%@-#YE!L%jKdDpG`LHItN==?Fte302oH$ Uo(`Ayg#Z8m07*qoM6N<$g1|VRod5s; literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/interface/front.png b/src/main/resources/assets/mekeng/textures/parts/interface/front.png new file mode 100644 index 0000000000000000000000000000000000000000..58a1ad5d2afaedbc51759dea29603077dad433a8 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`J)SO(Ar`&K5(Ni1iVvKbY5c$b z-=CR@r>E&w++%GWP+QAT=AzZ?! v(JIJ2;p(AS2CsPwMBH+MO;T-|blao=_5k#701m)CXj1W!wqhci^<;n~ zmPX_8-*0RK?o^hg9EJgG+lH#D=$@vDl*}hdLN~s%EF)jR0hVQ9mJD7#ZkqZAmy=DL zVHk9n1ZGBOUASgeu{9Zhl#=eEC?xV?97iSWx(qR3M=p697*gk@RE z2%Wi-zVE>_O%Sdu9Y($4$AtbX%w(V9LI764B&cxq|BU+d)6y) zT^F9-zKFk^9>8R9^fK8Id34sn+XE~1LJq(;fA1a-CKG%4Er>oB_+~tGLf=0@hhL#C! z^h!jUrqm$r1RJlwuCuX~{rt|q+lb)c_U+f1+fX|Ar`$?CmrNsP~dQm{qlc* z4vXv}vy(3_^*xf4&ic=t&0PL{g=oXs;+!<=uWUxEH+1MMo0ZQn)k$@6Lyv|-sE6td q-it3)5<=d&xwKdQ_K-hyM#^QClJu(!<|}~~FnGH9xvX!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a5n0T@z_$#98Qbry z%m)femAFQf1m~xflqVLYG6W=M=9TFAxrQi|8S9zq85+8Ob}|R5lJ#_P46*P}mPmVG qFW@lKg3&O`?H~ucvXP=8(5BP^{*MZe ztrI5-I`fwvtk*d3%*bh-@(b3SBA+Q%@~atdA&e!mZ;aCIVXPK_vCEtg(&Z1GZZ=P zj#TlAACb^(aqwFC=&&4++sWbKaC8pCZOiaCQi&5K3I)2a+unS5 Y>oc@aJ^y2(DbUFbp00i_>zopr06t@0RsaA1 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/mekeng/textures/parts/p2p/gases_p2p.png b/src/main/resources/assets/mekeng/textures/parts/p2p/gases_p2p.png new file mode 100644 index 0000000000000000000000000000000000000000..bc633c993634b4003842f3c1af3300dc2861918f GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ik>cxAr`&KH3s)z*fX;>bPAp2 mS