From db2ad29b52d667eb463ef43c3399b045bd42d6f0 Mon Sep 17 00:00:00 2001 From: GlodBlock <1356392126@qq.com> Date: Tue, 10 May 2022 17:57:38 +0800 Subject: [PATCH] begin work --- .editorconfig | 19 + .gitignore | 8 + CODEOWNERS | 3 + build.gradle | 738 +++++++++++++++++ dependencies.gradle | 14 + gradle.properties | 71 ++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 55616 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 164 ++++ repositories.gradle | 31 + .../java/com/glodblock/github/FluidCraft.java | 62 ++ .../glodblock/github/client/ClientHelper.java | 29 + .../gui/GuiBaseFluidPatternTerminal.java | 163 ++++ .../github/client/gui/GuiFCBaseMonitor.java | 750 ++++++++++++++++++ .../client/gui/GuiFluidCraftAmount.java | 257 ++++++ .../client/gui/GuiFluidCraftConfirm.java | 531 +++++++++++++ .../client/gui/GuiFluidPacketDecoder.java | 33 + .../client/gui/GuiFluidPatternEncoder.java | 136 ++++ .../client/gui/GuiFluidPatternTerminal.java | 73 ++ ...GuiFluidPatternTerminalCraftingStatus.java | 38 + .../client/gui/IFluidSyncContainer.java | 9 + .../container/ContainerFluidConfigurable.java | 382 +++++++++ .../container/ContainerFluidCraftConfirm.java | 436 ++++++++++ .../ContainerFluidPacketDecoder.java | 16 + .../ContainerFluidPatternEncoder.java | 226 ++++++ .../ContainerFluidPatternTerminal.java | 185 +++++ .../gui/container/FCBaseMonitorContain.java | 408 ++++++++++ .../gui/container/FCBasePartContainer.java | 625 +++++++++++++++ .../github/client/render/ItemDropRender.java | 109 +++ .../client/render/ItemPacketRender.java | 75 ++ .../render/RenderBlockFluidInterface.java | 41 + .../client/textures/FCPartsTexture.java | 50 ++ .../common/block/BlockFluidDiscretizer.java | 24 + .../common/block/BlockFluidInterface.java | 81 ++ .../common/block/BlockFluidPacketDecoder.java | 45 ++ .../block/BlockFluidPatternEncoder.java | 46 ++ .../github/common/block/FCBaseBlock.java | 37 + .../github/common/item/ItemFluidDrop.java | 142 ++++ .../common/item/ItemFluidEncodedPattern.java | 31 + .../github/common/item/ItemFluidPacket.java | 110 +++ .../item/ItemPartFluidPatternTerminal.java | 53 ++ .../github/common/parts/FCBasePart.java | 507 ++++++++++++ .../parts/PartFluidPatternTerminal.java | 288 +++++++ .../common/tile/TileFluidDiscretizer.java | 295 +++++++ .../common/tile/TileFluidInterface.java | 81 ++ .../common/tile/TileFluidPacketDecoder.java | 102 +++ .../common/tile/TileFluidPatternEncoder.java | 77 ++ .../github/coremod/FCClassTransformer.java | 59 ++ .../github/coremod/FluidCraftCore.java | 40 + .../github/coremod/hooker/CoreModHooks.java | 87 ++ ...ContainerInterfaceTerminalTransformer.java | 58 ++ .../transform/CraftingCpuTransformer.java | 86 ++ .../CraftingTreeNodeTransformer.java | 77 ++ .../DualityInterfaceTransformer.java | 62 ++ .../github/inventory/AEFluidInventory.java | 411 ++++++++++ .../github/inventory/AEFluidTank.java | 90 +++ .../github/inventory/AeItemStackHandler.java | 84 ++ .../github/inventory/AeStackInventory.java | 19 + .../inventory/AeStackInventoryImpl.java | 102 +++ .../FluidConvertingInventoryAdaptor.java | 161 ++++ .../FluidConvertingInventoryCrafting.java | 32 + .../github/inventory/IAEFluidInventory.java | 7 + .../github/inventory/IAEFluidTank.java | 13 + .../github/inventory/InventoryHandler.java | 51 ++ .../github/inventory/PatternConsumer.java | 9 + .../github/inventory/gui/GuiFactory.java | 20 + .../github/inventory/gui/GuiType.java | 107 +++ .../inventory/gui/MouseRegionManager.java | 84 ++ .../github/inventory/gui/PartGuiFactory.java | 28 + .../github/inventory/gui/TileGuiFactory.java | 66 ++ .../github/inventory/slot/SlotFluid.java | 15 + .../github/inventory/slot/SlotSingleItem.java | 102 +++ .../github/loader/ChannelLoader.java | 22 + .../github/loader/ItemAndBlockHolder.java | 24 + .../github/loader/ListenerLoader.java | 12 + .../glodblock/github/loader/RenderLoader.java | 14 + ...dPatternTerminalRecipeTransferHandler.java | 50 ++ .../glodblock/github/nei/NEI_FC_Config.java | 27 + .../github/nei/object/IFluidDummy.java | 9 + .../github/nei/object/IRecipeExtractor.java | 13 + .../nei/object/IllegalOrderStackID.java | 9 + .../github/nei/object/OrderStack.java | 106 +++ .../nei/recipes/DefaultExtractorLoader.java | 24 + .../github/nei/recipes/FluidRecipe.java | 42 + .../extractor/GregTech5RecipeExtractor.java | 94 +++ .../extractor/VanillaRecipeExtractor.java | 46 ++ .../github/network/CPacketCraftRequest.java | 118 +++ .../github/network/CPacketEncodePattern.java | 38 + .../network/CPacketFluidPatternTermBtns.java | 95 +++ .../network/CPacketInventoryAction.java | 124 +++ .../github/network/CPacketSetTargetItem.java | 56 ++ .../github/network/CPacketSwitchGuis.java | 68 ++ .../github/network/CPacketTransferRecipe.java | 128 +++ .../github/network/SPacketFluidSlot.java | 68 ++ .../network/SPacketMEInventoryUpdate.java | 84 ++ .../glodblock/github/proxy/ClientProxy.java | 23 + .../glodblock/github/proxy/CommonProxy.java | 20 + .../com/glodblock/github/util/Ae2Reflect.java | 237 ++++++ .../com/glodblock/github/util/BlockPos.java | 46 ++ .../github/util/FluidPatternDetails.java | 199 +++++ .../github/util/FluidSyncHelper.java | 94 +++ .../github/util/ModAndClassUtil.java | 30 + .../com/glodblock/github/util/NameConst.java | 29 + .../github/util/ObjectArrayIterator.java | 163 ++++ .../com/glodblock/github/util/RenderUtil.java | 32 + .../github/util/SetBackedMachineSet.java | 60 ++ .../java/com/glodblock/github/util/Util.java | 102 +++ .../resources/assets/ae2fc/lang/en_US.lang | 17 + .../textures/blocks/fluid_discretizer.png | Bin 0 -> 2004 bytes .../ae2fc/textures/blocks/fluid_interface.png | Bin 0 -> 1910 bytes .../textures/blocks/fluid_interface_a.png | Bin 0 -> 1910 bytes .../textures/blocks/fluid_interface_arrow.png | Bin 0 -> 194 bytes .../textures/blocks/fluid_packet_decoder.png | Bin 0 -> 2450 bytes .../textures/blocks/fluid_pattern_encoder.png | Bin 0 -> 1919 bytes .../blocks/fluid_pattern_encoderBottom.png | Bin 0 -> 2028 bytes .../blocks/fluid_pattern_encoderSide.png | Bin 0 -> 2159 bytes .../blocks/pattern_terminal_bright.png | Bin 0 -> 1488 bytes .../textures/blocks/pattern_terminal_dark.png | Bin 0 -> 1427 bytes .../blocks/pattern_terminal_medium.png | Bin 0 -> 1506 bytes .../ae2fc/textures/blocks/terminal_broad.png | Bin 0 -> 259 bytes .../textures/gui/fluid_packet_decoder.png | Bin 0 -> 5240 bytes .../textures/gui/fluid_pattern_encoder.png | Bin 0 -> 7072 bytes .../ae2fc/textures/items/fluid_drop.png | Bin 0 -> 1482 bytes .../textures/items/fluid_encoded_pattern.png | Bin 0 -> 801 bytes 124 files changed, 11799 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CODEOWNERS create mode 100644 build.gradle create mode 100644 dependencies.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 repositories.gradle create mode 100644 src/main/java/com/glodblock/github/FluidCraft.java create mode 100644 src/main/java/com/glodblock/github/client/ClientHelper.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiBaseFluidPatternTerminal.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFCBaseMonitor.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidCraftAmount.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidCraftConfirm.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidPacketDecoder.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidPatternEncoder.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminal.java create mode 100644 src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminalCraftingStatus.java create mode 100644 src/main/java/com/glodblock/github/client/gui/IFluidSyncContainer.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/ContainerFluidConfigurable.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/ContainerFluidCraftConfirm.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPacketDecoder.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternEncoder.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternTerminal.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/FCBaseMonitorContain.java create mode 100644 src/main/java/com/glodblock/github/client/gui/container/FCBasePartContainer.java create mode 100644 src/main/java/com/glodblock/github/client/render/ItemDropRender.java create mode 100644 src/main/java/com/glodblock/github/client/render/ItemPacketRender.java create mode 100644 src/main/java/com/glodblock/github/client/render/RenderBlockFluidInterface.java create mode 100644 src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java create mode 100644 src/main/java/com/glodblock/github/common/block/BlockFluidDiscretizer.java create mode 100644 src/main/java/com/glodblock/github/common/block/BlockFluidInterface.java create mode 100644 src/main/java/com/glodblock/github/common/block/BlockFluidPacketDecoder.java create mode 100644 src/main/java/com/glodblock/github/common/block/BlockFluidPatternEncoder.java create mode 100644 src/main/java/com/glodblock/github/common/block/FCBaseBlock.java create mode 100644 src/main/java/com/glodblock/github/common/item/ItemFluidDrop.java create mode 100644 src/main/java/com/glodblock/github/common/item/ItemFluidEncodedPattern.java create mode 100644 src/main/java/com/glodblock/github/common/item/ItemFluidPacket.java create mode 100644 src/main/java/com/glodblock/github/common/item/ItemPartFluidPatternTerminal.java create mode 100644 src/main/java/com/glodblock/github/common/parts/FCBasePart.java create mode 100644 src/main/java/com/glodblock/github/common/parts/PartFluidPatternTerminal.java create mode 100644 src/main/java/com/glodblock/github/common/tile/TileFluidDiscretizer.java create mode 100644 src/main/java/com/glodblock/github/common/tile/TileFluidInterface.java create mode 100644 src/main/java/com/glodblock/github/common/tile/TileFluidPacketDecoder.java create mode 100644 src/main/java/com/glodblock/github/common/tile/TileFluidPatternEncoder.java create mode 100644 src/main/java/com/glodblock/github/coremod/FCClassTransformer.java create mode 100644 src/main/java/com/glodblock/github/coremod/FluidCraftCore.java create mode 100644 src/main/java/com/glodblock/github/coremod/hooker/CoreModHooks.java create mode 100644 src/main/java/com/glodblock/github/coremod/transform/ContainerInterfaceTerminalTransformer.java create mode 100644 src/main/java/com/glodblock/github/coremod/transform/CraftingCpuTransformer.java create mode 100644 src/main/java/com/glodblock/github/coremod/transform/CraftingTreeNodeTransformer.java create mode 100644 src/main/java/com/glodblock/github/coremod/transform/DualityInterfaceTransformer.java create mode 100644 src/main/java/com/glodblock/github/inventory/AEFluidInventory.java create mode 100644 src/main/java/com/glodblock/github/inventory/AEFluidTank.java create mode 100644 src/main/java/com/glodblock/github/inventory/AeItemStackHandler.java create mode 100644 src/main/java/com/glodblock/github/inventory/AeStackInventory.java create mode 100644 src/main/java/com/glodblock/github/inventory/AeStackInventoryImpl.java create mode 100644 src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryAdaptor.java create mode 100644 src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryCrafting.java create mode 100644 src/main/java/com/glodblock/github/inventory/IAEFluidInventory.java create mode 100644 src/main/java/com/glodblock/github/inventory/IAEFluidTank.java create mode 100644 src/main/java/com/glodblock/github/inventory/InventoryHandler.java create mode 100644 src/main/java/com/glodblock/github/inventory/PatternConsumer.java create mode 100644 src/main/java/com/glodblock/github/inventory/gui/GuiFactory.java create mode 100644 src/main/java/com/glodblock/github/inventory/gui/GuiType.java create mode 100644 src/main/java/com/glodblock/github/inventory/gui/MouseRegionManager.java create mode 100644 src/main/java/com/glodblock/github/inventory/gui/PartGuiFactory.java create mode 100644 src/main/java/com/glodblock/github/inventory/gui/TileGuiFactory.java create mode 100644 src/main/java/com/glodblock/github/inventory/slot/SlotFluid.java create mode 100644 src/main/java/com/glodblock/github/inventory/slot/SlotSingleItem.java create mode 100644 src/main/java/com/glodblock/github/loader/ChannelLoader.java create mode 100644 src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java create mode 100644 src/main/java/com/glodblock/github/loader/ListenerLoader.java create mode 100644 src/main/java/com/glodblock/github/loader/RenderLoader.java create mode 100644 src/main/java/com/glodblock/github/nei/FluidPatternTerminalRecipeTransferHandler.java create mode 100644 src/main/java/com/glodblock/github/nei/NEI_FC_Config.java create mode 100644 src/main/java/com/glodblock/github/nei/object/IFluidDummy.java create mode 100644 src/main/java/com/glodblock/github/nei/object/IRecipeExtractor.java create mode 100644 src/main/java/com/glodblock/github/nei/object/IllegalOrderStackID.java create mode 100644 src/main/java/com/glodblock/github/nei/object/OrderStack.java create mode 100644 src/main/java/com/glodblock/github/nei/recipes/DefaultExtractorLoader.java create mode 100644 src/main/java/com/glodblock/github/nei/recipes/FluidRecipe.java create mode 100644 src/main/java/com/glodblock/github/nei/recipes/extractor/GregTech5RecipeExtractor.java create mode 100644 src/main/java/com/glodblock/github/nei/recipes/extractor/VanillaRecipeExtractor.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketCraftRequest.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketEncodePattern.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketFluidPatternTermBtns.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketInventoryAction.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketSetTargetItem.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketSwitchGuis.java create mode 100644 src/main/java/com/glodblock/github/network/CPacketTransferRecipe.java create mode 100644 src/main/java/com/glodblock/github/network/SPacketFluidSlot.java create mode 100644 src/main/java/com/glodblock/github/network/SPacketMEInventoryUpdate.java create mode 100644 src/main/java/com/glodblock/github/proxy/ClientProxy.java create mode 100644 src/main/java/com/glodblock/github/proxy/CommonProxy.java create mode 100644 src/main/java/com/glodblock/github/util/Ae2Reflect.java create mode 100644 src/main/java/com/glodblock/github/util/BlockPos.java create mode 100644 src/main/java/com/glodblock/github/util/FluidPatternDetails.java create mode 100644 src/main/java/com/glodblock/github/util/FluidSyncHelper.java create mode 100644 src/main/java/com/glodblock/github/util/ModAndClassUtil.java create mode 100644 src/main/java/com/glodblock/github/util/NameConst.java create mode 100644 src/main/java/com/glodblock/github/util/ObjectArrayIterator.java create mode 100644 src/main/java/com/glodblock/github/util/RenderUtil.java create mode 100644 src/main/java/com/glodblock/github/util/SetBackedMachineSet.java create mode 100644 src/main/java/com/glodblock/github/util/Util.java create mode 100644 src/main/resources/assets/ae2fc/lang/en_US.lang create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_discretizer.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_interface.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_interface_a.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_interface_arrow.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_packet_decoder.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoder.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderBottom.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderSide.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/pattern_terminal_bright.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/pattern_terminal_dark.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/pattern_terminal_medium.png create mode 100644 src/main/resources/assets/ae2fc/textures/blocks/terminal_broad.png create mode 100644 src/main/resources/assets/ae2fc/textures/gui/fluid_packet_decoder.png create mode 100644 src/main/resources/assets/ae2fc/textures/gui/fluid_pattern_encoder.png create mode 100644 src/main/resources/assets/ae2fc/textures/items/fluid_drop.png create mode 100644 src/main/resources/assets/ae2fc/textures/items/fluid_encoded_pattern.png diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..6effbc91 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# This is the universal Text Editor Configuration +# for all GTNewHorizons projects +# See: https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.{bat,ini}] +end_of_line = crlf + +[*.{dtd,json,info,mcmeta,md,sh,svg,xml,xsd,xsl,yaml,yml}] +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7e4570ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.bat +.gradle/ +.idea/ +out/ +*.iml +*.ipr +*.iws +run/ diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..a6b5f68c --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,3 @@ +# Any Github changes require admin approval +/.github/** @GTNewHorizons/admin + diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..c8cf37cd --- /dev/null +++ b/build.gradle @@ -0,0 +1,738 @@ +//version: 1644894948 +/* +DO NOT CHANGE THIS FILE! + +Also, you may replace this file at any time if there is an update available. +Please check https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/build.gradle for updates. +*/ + +import org.gradle.internal.logging.text.StyledTextOutput +import org.gradle.internal.logging.text.StyledTextOutputFactory +import org.gradle.internal.logging.text.StyledTextOutput.Style + +import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +import java.util.concurrent.TimeUnit + +buildscript { + repositories { + maven { + name 'forge' + url 'https://maven.minecraftforge.net' + } + maven { + name 'sonatype' + url 'https://oss.sonatype.org/content/repositories/snapshots/' + } + maven { + name 'Scala CI dependencies' + url 'https://repo1.maven.org/maven2/' + } + maven { + name 'jitpack' + url 'https://jitpack.io' + } + } + dependencies { + classpath 'com.github.GTNewHorizons:ForgeGradle:1.2.7' + } +} + +plugins { + id 'java-library' + id 'idea' + id 'eclipse' + id 'scala' + id 'maven-publish' + id 'org.jetbrains.kotlin.jvm' version '1.5.30' apply false + id 'org.jetbrains.kotlin.kapt' version '1.5.30' apply false + id 'org.ajoberstar.grgit' version '4.1.1' + id 'com.github.johnrengelman.shadow' version '4.0.4' + id 'com.palantir.git-version' version '0.13.0' apply false + id 'de.undercouch.download' version '5.0.1' + id 'com.github.gmazzo.buildconfig' version '3.0.3' apply false +} + +if (project.file('.git/HEAD').isFile()) { + apply plugin: 'com.palantir.git-version' +} + +def out = services.get(StyledTextOutputFactory).create('an-output') + +apply plugin: 'forge' + +def projectJavaVersion = JavaLanguageVersion.of(8) + +java { + toolchain { + languageVersion.set(projectJavaVersion) + } +} + +idea { + module { + inheritOutputDirs = true + downloadJavadoc = true + downloadSources = true + } +} + +if(JavaVersion.current() != JavaVersion.VERSION_1_8) { + throw new GradleException("This project requires Java 8, but it's running on " + JavaVersion.current()) +} + +checkPropertyExists("modName") +checkPropertyExists("modId") +checkPropertyExists("modGroup") +checkPropertyExists("autoUpdateBuildScript") +checkPropertyExists("minecraftVersion") +checkPropertyExists("forgeVersion") +checkPropertyExists("replaceGradleTokenInFile") +checkPropertyExists("gradleTokenModId") +checkPropertyExists("gradleTokenModName") +checkPropertyExists("gradleTokenVersion") +checkPropertyExists("gradleTokenGroupName") +checkPropertyExists("apiPackage") +checkPropertyExists("accessTransformersFile") +checkPropertyExists("usesMixins") +checkPropertyExists("mixinPlugin") +checkPropertyExists("mixinsPackage") +checkPropertyExists("coreModClass") +checkPropertyExists("containsMixinsAndOrCoreModOnly") +checkPropertyExists("usesShadowedDependencies") +checkPropertyExists("developmentEnvironmentUserName") + +boolean noPublishedSources = project.findProperty("noPublishedSources") ? project.noPublishedSources.toBoolean() : false + +String javaSourceDir = "src/main/java/" +String scalaSourceDir = "src/main/scala/" +String kotlinSourceDir = "src/main/kotlin/" + +String targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") +String targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") +String targetPackageKotlin = kotlinSourceDir + modGroup.toString().replaceAll("\\.", "/") +if(!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"modGroup\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) +} + +if(apiPackage) { + targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + targetPackageKotlin = kotlinSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + if(!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"apiPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) + } +} + +if(accessTransformersFile) { + String targetFile = "src/main/resources/META-INF/" + accessTransformersFile + if(!getFile(targetFile).exists()) { + throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile) + } +} + +if(usesMixins.toBoolean()) { + if(mixinsPackage.isEmpty() || mixinPlugin.isEmpty()) { + throw new GradleException("\"mixinPlugin\" requires \"mixinsPackage\" and \"mixinPlugin\" to be set!") + } + + targetPackageJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/") + targetPackageScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/") + targetPackageKotlin = kotlinSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinsPackage.toString().replaceAll("\\.", "/") + if(!(getFile(targetPackageJava).exists() || getFile(targetPackageScala).exists() || getFile(targetPackageKotlin).exists())) { + throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find " + targetPackageJava + " or " + targetPackageScala + " or " + targetPackageKotlin) + } + + String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java" + String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".scala" + String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".java" + String targetFileKotlin = kotlinSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + mixinPlugin.toString().replaceAll("\\.", "/") + ".kt" + if(!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) { + throw new GradleException("Could not resolve \"mixinPlugin\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin) + } +} + +if(coreModClass) { + String targetFileJava = javaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java" + String targetFileScala = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".scala" + String targetFileScalaJava = scalaSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".java" + String targetFileKotlin = kotlinSourceDir + modGroup.toString().replaceAll("\\.", "/") + "/" + coreModClass.toString().replaceAll("\\.", "/") + ".kt" + if(!(getFile(targetFileJava).exists() || getFile(targetFileScala).exists() || getFile(targetFileScalaJava).exists() || getFile(targetFileKotlin).exists())) { + throw new GradleException("Could not resolve \"coreModClass\"! Could not find " + targetFileJava + " or " + targetFileScala + " or " + targetFileScalaJava + " or " + targetFileKotlin) + } +} + +configurations.all { + resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS) + + // Make sure GregTech build won't time out + System.setProperty("org.gradle.internal.http.connectionTimeout", 120000 as String) + System.setProperty("org.gradle.internal.http.socketTimeout", 120000 as String) +} + +// Fix Jenkins' Git: chmod a file should not be detected as a change and append a '.dirty' to the version +try { + 'git config core.fileMode false'.execute() +} +catch (Exception ignored) { + out.style(Style.Failure).println("git isn't installed at all") +} + +// Pulls version first from the VERSION env and then git tag +String identifiedVersion +String versionOverride = System.getenv("VERSION") ?: null +try { + identifiedVersion = versionOverride == null ? gitVersion() : versionOverride +} +catch (Exception ignored) { + out.style(Style.Failure).text( + 'This mod must be version controlled by Git AND the repository must provide at least one tag,\n' + + 'or the VERSION override must be set! ').style(Style.SuccessHeader).text('(Do NOT download from GitHub using the ZIP option, instead\n' + + 'clone the repository, see ').style(Style.Info).text('https://gtnh.miraheze.org/wiki/Development').style(Style.SuccessHeader).println(' for details.)' + ) + versionOverride = 'NO-GIT-TAG-SET' + identifiedVersion = versionOverride +} +version = minecraftVersion + '-' + identifiedVersion +ext { + modVersion = identifiedVersion +} + +if(identifiedVersion == versionOverride) { + out.style(Style.Failure).text('Override version to ').style(Style.Identifier).text(modVersion).style(Style.Failure).println('!\7') +} + +group = modGroup +if(project.hasProperty("customArchiveBaseName") && customArchiveBaseName) { + archivesBaseName = customArchiveBaseName +} +else { + archivesBaseName = modId +} + +def arguments = [] +def jvmArguments = [] + +if(usesMixins.toBoolean()) { + arguments += [ + "--tweakClass org.spongepowered.asm.launch.MixinTweaker" + ] + jvmArguments += [ + "-Dmixin.debug.countInjections=true", "-Dmixin.debug.verbose=true", "-Dmixin.debug.export=true" + ] +} + +minecraft { + version = minecraftVersion + '-' + forgeVersion + '-' + minecraftVersion + runDir = 'run' + + if (replaceGradleTokenInFile) { + replaceIn replaceGradleTokenInFile + if(gradleTokenModId) { + replace gradleTokenModId, modId + } + if(gradleTokenModName) { + replace gradleTokenModName, modName + } + if(gradleTokenVersion) { + replace gradleTokenVersion, modVersion + } + if(gradleTokenGroupName) { + replace gradleTokenGroupName, modGroup + } + } + + clientIntellijRun { + args(arguments) + jvmArgs(jvmArguments) + + if(developmentEnvironmentUserName) { + args("--username", developmentEnvironmentUserName) + } + } + + serverIntellijRun { + args(arguments) + jvmArgs(jvmArguments) + } +} + +if(file('addon.gradle').exists()) { + apply from: 'addon.gradle' +} + +apply from: 'repositories.gradle' + +configurations { + implementation.extendsFrom(shadowImplementation) // TODO: remove after all uses are refactored + implementation.extendsFrom(shadowCompile) + implementation.extendsFrom(shadeCompile) +} + +repositories { + maven { + name 'Overmind forge repo mirror' + url 'https://gregtech.overminddl1.com/' + } + if(usesMixins.toBoolean()) { + maven { + name 'sponge' + url 'https://repo.spongepowered.org/repository/maven-public' + } + maven { + url 'https://jitpack.io' + } + } +} + +dependencies { + if(usesMixins.toBoolean()) { + annotationProcessor('org.ow2.asm:asm-debug-all:5.0.3') + annotationProcessor('com.google.guava:guava:24.1.1-jre') + annotationProcessor('com.google.code.gson:gson:2.8.6') + annotationProcessor('org.spongepowered:mixin:0.8-SNAPSHOT') + // using 0.8 to workaround a issue in 0.7 which fails mixin application + compile('com.github.GTNewHorizons:SpongePoweredMixin:0.7.12-GTNH') { + // Mixin includes a lot of dependencies that are too up-to-date + exclude module: 'launchwrapper' + exclude module: 'guava' + exclude module: 'gson' + exclude module: 'commons-io' + exclude module: 'log4j-core' + } + compile('com.github.GTNewHorizons:SpongeMixins:1.5.0') + } +} + +apply from: 'dependencies.gradle' + +def mixingConfigRefMap = 'mixins.' + modId + '.refmap.json' +def refMap = "${tasks.compileJava.temporaryDir}" + File.separator + mixingConfigRefMap +def mixinSrg = "${tasks.reobf.temporaryDir}" + File.separator + "mixins.srg" + +task generateAssets { + if(usesMixins.toBoolean()) { + getFile("/src/main/resources/mixins." + modId + ".json").text = """{ + "required": true, + "minVersion": "0.7.11", + "package": "${modGroup}.${mixinsPackage}", + "plugin": "${modGroup}.${mixinPlugin}", + "refmap": "${mixingConfigRefMap}", + "target": "@env(DEFAULT)", + "compatibilityLevel": "JAVA_8" +} + +""" + } +} + +task relocateShadowJar(type: ConfigureShadowRelocation) { + target = tasks.shadowJar + prefix = modGroup + ".shadow" +} + +shadowJar { + project.configurations.shadeCompile.each { dep -> + from(project.zipTree(dep)) { + exclude 'META-INF', 'META-INF/**' + } + } + + manifest { + attributes(getManifestAttributes()) + } + + minimize() // This will only allow shading for actually used classes + configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] + dependsOn(relocateShadowJar) +} + +jar { + project.configurations.shadeCompile.each { dep -> + from(project.zipTree(dep)) { + exclude 'META-INF', 'META-INF/**' + } + } + + manifest { + attributes(getManifestAttributes()) + } + + if(usesShadowedDependencies.toBoolean()) { + dependsOn(shadowJar) + enabled = false + } +} + +reobf { + if(usesMixins.toBoolean()) { + addExtraSrgFile mixinSrg + } +} + +afterEvaluate { + if(usesMixins.toBoolean()) { + tasks.compileJava { + options.compilerArgs += [ + "-AreobfSrgFile=${tasks.reobf.srg}", + "-AoutSrgFile=${mixinSrg}", + "-AoutRefMapFile=${refMap}", + // Elan: from what I understand they are just some linter configs so you get some warning on how to properly code + "-XDenableSunApiLintControl", + "-XDignore.symbol.file" + ] + } + } +} + +runClient { + if(developmentEnvironmentUserName) { + arguments += [ + "--username", + developmentEnvironmentUserName + ] + } + + args(arguments) + jvmArgs(jvmArguments) +} + +runServer { + args(arguments) + jvmArgs(jvmArguments) +} + +tasks.withType(JavaExec).configureEach { + javaLauncher.set( + javaToolchains.launcherFor { + languageVersion = projectJavaVersion + } + ) +} + +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 modVersion and minecraftVersion + expand "minecraftVersion": project.minecraft.version, + "modVersion": modVersion, + "modId": modId, + "modName": modName + } + + if(usesMixins.toBoolean()) { + from refMap + } + + // copy everything else that's not the mcmod.info + from(sourceSets.main.resources.srcDirs) { + exclude 'mcmod.info' + } +} + +def getManifestAttributes() { + def manifestAttributes = [:] + if(!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) { + manifestAttributes += ["FMLCorePluginContainsFMLMod": true] + } + + if(accessTransformersFile) { + manifestAttributes += ["FMLAT" : accessTransformersFile.toString()] + } + + if(coreModClass) { + manifestAttributes += ["FMLCorePlugin": modGroup + "." + coreModClass] + } + + if(usesMixins.toBoolean()) { + manifestAttributes += [ + "TweakClass" : "org.spongepowered.asm.launch.MixinTweaker", + "MixinConfigs" : "mixins." + modId + ".json", + "ForceLoadAsMod" : !containsMixinsAndOrCoreModOnly.toBoolean() + ] + } + return manifestAttributes +} + +task sourcesJar(type: Jar) { + from (sourceSets.main.allJava) + from (file("$projectDir/LICENSE")) + getArchiveClassifier().set('sources') +} + +task shadowDevJar(type: ShadowJar) { + project.configurations.shadeCompile.each { dep -> + from(project.zipTree(dep)) { + exclude 'META-INF', 'META-INF/**' + } + } + + from sourceSets.main.output + getArchiveClassifier().set("dev") + + manifest { + attributes(getManifestAttributes()) + } + + minimize() // This will only allow shading for actually used classes + configurations = [project.configurations.shadowImplementation, project.configurations.shadowCompile] +} + +task relocateShadowDevJar(type: ConfigureShadowRelocation) { + target = tasks.shadowDevJar + prefix = modGroup + ".shadow" +} + +task circularResolverJar(type: Jar) { + dependsOn(relocateShadowDevJar) + dependsOn(shadowDevJar) + enabled = false +} + +task devJar(type: Jar) { + project.configurations.shadeCompile.each { dep -> + from(project.zipTree(dep)) { + exclude 'META-INF', 'META-INF/**' + } + } + + from sourceSets.main.output + getArchiveClassifier().set("dev") + + manifest { + attributes(getManifestAttributes()) + } + + if(usesShadowedDependencies.toBoolean()) { + dependsOn(circularResolverJar) + enabled = false + } +} + +task apiJar(type: Jar) { + from (sourceSets.main.allJava) { + include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + '/**' + } + + from (sourceSets.main.output) { + include modGroup.toString().replaceAll("\\.", "/") + "/" + apiPackage.toString().replaceAll("\\.", "/") + '/**' + } + + from (sourceSets.main.resources.srcDirs) { + include("LICENSE") + } + + getArchiveClassifier().set('api') +} + +artifacts { + if(!noPublishedSources) { + archives sourcesJar + } + archives devJar + if(apiPackage) { + archives apiJar + } +} + +// The gradle metadata includes all of the additional deps that we disabled from POM generation (including forgeBin with no groupID), +// and isn't strictly needed with the POM so just disable it. +tasks.withType(GenerateModuleMetadata) { + enabled = false +} + +// workaround variable hiding in pom processing +def projectConfigs = project.configurations + +publishing { + publications { + maven(MavenPublication) { + from components.java + if(usesShadowedDependencies.toBoolean()) { + artifact source: shadowJar, classifier: "" + } + if(!noPublishedSources) { + artifact source: sourcesJar, classifier: "src" + } + artifact source: usesShadowedDependencies.toBoolean() ? shadowDevJar : devJar, classifier: "dev" + if (apiPackage) { + artifact source: apiJar, classifier: "api" + } + + groupId = System.getenv("ARTIFACT_GROUP_ID") ?: "com.github.GTNewHorizons" + artifactId = System.getenv("ARTIFACT_ID") ?: project.name + // Using the identified version, not project.version as it has the prepended 1.7.10 + version = System.getenv("RELEASE_VERSION") ?: identifiedVersion + + // remove extra garbage from minecraft and minecraftDeps configuration + pom.withXml { + def badArtifacts = [:].withDefault {[] as Set} + for (configuration in [projectConfigs.minecraft, projectConfigs.minecraftDeps]) { + for (dependency in configuration.allDependencies) { + badArtifacts[dependency.group == null ? "" : dependency.group] += dependency.name + } + } + // example for specifying extra stuff to ignore + // badArtifacts["org.example.group"] += "artifactName" + + Node pomNode = asNode() + pomNode.dependencies.'*'.findAll() { + badArtifacts[it.groupId.text()].contains(it.artifactId.text()) + }.each() { + it.parent().remove(it) + } + } + } + } + + repositories { + maven { + url = "http://jenkins.usrv.eu:8081/nexus/content/repositories/releases" + credentials { + username = System.getenv("MAVEN_USER") ?: "NONE" + password = System.getenv("MAVEN_PASSWORD") ?: "NONE" + } + } + } +} + +// Updating +task updateBuildScript { + doLast { + if (performBuildScriptUpdate(projectDir.toString())) return + + print("Build script already up-to-date!") + } +} + +if (isNewBuildScriptVersionAvailable(projectDir.toString())) { + if (autoUpdateBuildScript.toBoolean()) { + performBuildScriptUpdate(projectDir.toString()) + } else { + out.style(Style.SuccessHeader).println("Build script update available! Run 'gradle updateBuildScript'") + } +} + +static URL availableBuildScriptUrl() { + new URL("https://raw.githubusercontent.com/GTNewHorizons/ExampleMod1.7.10/main/build.gradle") +} + +boolean performBuildScriptUpdate(String projectDir) { + if (isNewBuildScriptVersionAvailable(projectDir)) { + def buildscriptFile = getFile("build.gradle") + availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } } + out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!") + return true + } + return false +} + +boolean isNewBuildScriptVersionAvailable(String projectDir) { + Map parameters = ["connectTimeout": 2000, "readTimeout": 2000] + + String currentBuildScript = getFile("build.gradle").getText() + String currentBuildScriptHash = getVersionHash(currentBuildScript) + String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText() + String availableBuildScriptHash = getVersionHash(availableBuildScript) + + boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash + return !isUpToDate +} + +static String getVersionHash(String buildScriptContent) { + String versionLine = buildScriptContent.find("^//version: [a-z0-9]*") + if(versionLine != null) { + return versionLine.split(": ").last() + } + return "" +} + +configure(updateBuildScript) { + group = 'forgegradle' + description = 'Updates the build script to the latest version' +} + +// Deobfuscation + +def deobf(String sourceURL) { + try { + URL url = new URL(sourceURL) + String fileName = url.getFile() + + //get rid of directories: + int lastSlash = fileName.lastIndexOf("/") + if(lastSlash > 0) { + fileName = fileName.substring(lastSlash + 1) + } + //get rid of extension: + if(fileName.endsWith(".jar")) { + fileName = fileName.substring(0, fileName.lastIndexOf(".")) + } + + String hostName = url.getHost() + if(hostName.startsWith("www.")) { + hostName = hostName.substring(4) + } + List parts = Arrays.asList(hostName.split("\\.")) + Collections.reverse(parts) + hostName = String.join(".", parts) + + return deobf(sourceURL, hostName + "/" + fileName) + } catch(Exception e) { + return deobf(sourceURL, "deobf/" + String.valueOf(sourceURL.hashCode())) + } +} + +// The method above is to be preferred. Use this method if the filename is not at the end of the URL. +def deobf(String sourceURL, String fileName) { + String cacheDir = System.getProperty("user.home") + "/.gradle/caches/" + String bon2Dir = cacheDir + "forge_gradle/deobf" + String bon2File = bon2Dir + "/BON2-2.5.0.jar" + String obfFile = cacheDir + "modules-2/files-2.1/" + fileName + ".jar" + String deobfFile = cacheDir + "modules-2/files-2.1/" + fileName + "-deobf.jar" + + if(file(deobfFile).exists()) { + return files(deobfFile) + } + + download.run { + src 'https://github.com/GTNewHorizons/BON2/releases/download/2.5.0/BON2-2.5.0.CUSTOM-all.jar' + dest bon2File + quiet true + overwrite false + } + + download.run { + src sourceURL + dest obfFile + quiet true + overwrite false + } + + exec { + commandLine 'java', '-jar', bon2File, '--inputJar', obfFile, '--outputJar', deobfFile, '--mcVer', '1.7.10', '--mappingsVer', 'stable_12', '--notch' + workingDir bon2Dir + standardOutput = new ByteArrayOutputStream() + } + + return files(deobfFile) +} + +// Helper methods + +def checkPropertyExists(String propertyName) { + if (!project.hasProperty(propertyName)) { + throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GTNewHorizons/ExampleMod1.7.10/blob/main/gradle.properties") + } +} + +def getFile(String relativePath) { + return new File(projectDir, relativePath) +} diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 00000000..ecfd8cbc --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,14 @@ +// Add your dependencies here + +dependencies { + compile('com.github.GTNewHorizons:GT5-Unofficial:5.09.40.41:dev') + compile('com.github.GTNewHorizons:StructureLib:1.0.15:dev') + compile('com.github.GTNewHorizons:NotEnoughItems:2.2.12-GTNH:dev') + compile('net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev') + compile('com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-75-GTNH:dev') + compile('curse.maven:cofh-core-69162:2388751') + compile('com.github.GTNewHorizons:ExtraCells2:2.5.9:dev') {transitive = false} + + compileOnly('com.github.GTNewHorizons:AppleCore:3.1.9:dev') {transitive = false} + compileOnly('com.github.GTNewHorizons:BuildCraft:7.1.27:dev') {transitive = false} +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..e3f5c6c9 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,71 @@ +modName = FluidCraftForAE2 + +# This is a case-sensitive string to identify your mod. Convention is to use lower case. +modId = ae2fc + +modGroup = com.glodblock.github + +# WHY is there no version field? +# The build script relies on git to provide a version via tags. It is super easy and will enable you to always know the +# code base or your binary. Check out this tutorial: https://blog.mattclemente.com/2017/10/13/versioning-with-git-tags/ + +# Will update your build.gradle automatically whenever an update is available +autoUpdateBuildScript = false + +minecraftVersion = 1.7.10 +forgeVersion = 10.13.4.1614 + +# Select a username for testing your mod with breakpoints. You may leave this empty for a random user name each time you +# restart Minecraft in development. Choose this dependent on your mod: +# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name +# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty +developmentEnvironmentUserName = Developer + +# Define a source file of your project with: +# public static final String VERSION = "GRADLETOKEN_VERSION"; +# The string's content will be replaced with your mods version when compiled. You should use this to specify your mod's +# version in @Mod([...], version = VERSION, [...]) +# Leave these properties empty to skip individual token replacements +replaceGradleTokenInFile = FluidCraft.java +gradleTokenModId = GRADLETOKEN_MODID +gradleTokenModName = GRADLETOKEN_MODNAME +gradleTokenVersion = GRADLETOKEN_VERSION +gradleTokenGroupName = GRADLETOKEN_GROUPNAME + +# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can +# leave this property empty. +# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api +apiPackage = + +# Specify the configuration file for Forge's access transformers here. I must be placed into /src/main/resources/META-INF/ +# Example value: mymodid_at.cfg +accessTransformersFile = + +# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! +usesMixins = false +# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise. +mixinPlugin = +# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! +mixinsPackage = +# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! +# This parameter is for legacy compatibility only +# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin +coreModClass = coremod.FluidCraftCore +# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod ( = some class +# that is annotated with @Mod) you want this to be true. When in doubt: leave it on false! +containsMixinsAndOrCoreModOnly = false + +# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated in your jar. It is your +# responsibility check the licence and request permission for distribution, if required. +usesShadowedDependencies = false + +# Optional parameter to customize the produced artifacts. Use this to preserver artifact naming when migrating older +# projects. New projects should not use this parameter. +#customArchiveBaseName = + +# Optional parameter to prevent the source code from being published +# noPublishedSources = +systemProp.http.proxyHost=127.0.0.1 +systemProp.http.proxyPort=7890 +systemProp.https.proxyHost=127.0.0.1 +systemProp.https.proxyPort=7890 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..5c2d1cf016b3885f6930543d57b744ea8c220a1a GIT binary patch literal 55616 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 \(.*\)$'` + 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/repositories.gradle b/repositories.gradle new file mode 100644 index 00000000..a4ff7a25 --- /dev/null +++ b/repositories.gradle @@ -0,0 +1,31 @@ +// Add any additional repositories for your dependencies here + +repositories { + maven { + name 'GTNH Maven' + url 'http://jenkins.usrv.eu:8081/nexus/content/groups/public/' + allowInsecureProtocol + } + maven { + name 'ic2' + url 'https://maven.ic2.player.to/' + metadataSources { + mavenPom() + artifact() + } + + } + maven { + name 'gt' + url 'https://gregtech.overminddl1.com/' + } + maven { + url 'https://cursemaven.com' + content { + includeGroup 'curse.maven' + } + } + maven { + url 'https://jitpack.io' + } +} diff --git a/src/main/java/com/glodblock/github/FluidCraft.java b/src/main/java/com/glodblock/github/FluidCraft.java new file mode 100644 index 00000000..589a7a2a --- /dev/null +++ b/src/main/java/com/glodblock/github/FluidCraft.java @@ -0,0 +1,62 @@ +package com.glodblock.github; + +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.loader.ChannelLoader; +import com.glodblock.github.loader.ListenerLoader; +import com.glodblock.github.loader.RenderLoader; +import com.glodblock.github.nei.recipes.DefaultExtractorLoader; +import com.glodblock.github.proxy.CommonProxy; +import com.glodblock.github.util.ModAndClassUtil; +import cpw.mods.fml.common.Mod; +import cpw.mods.fml.common.SidedProxy; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLLoadCompleteEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.network.NetworkRegistry; +import net.minecraft.util.ResourceLocation; + +@Mod(modid = FluidCraft.MODID, version = FluidCraft.VERSION) +public class FluidCraft { + + public static final String MODID = "GRADLETOKEN_MODID"; + public static final String VERSION = "GRADLETOKEN_VERSION"; + public static final String MODNAME = "GRADLETOKEN_MODNAME"; + + @Mod.Instance(MODID) + public static FluidCraft INSTANCE; + + @SidedProxy(clientSide = "com.glodblock.github.proxy.ClientProxy", serverSide = "com.glodblock.github.proxy.CommonProxy") + public static CommonProxy proxy; + + @Mod.EventHandler + public static void preInit(FMLPreInitializationEvent event) { + (new ChannelLoader()).run(); + proxy.preInit(event); + } + + @Mod.EventHandler + public static void init(FMLInitializationEvent event) { + (new RenderLoader()).run(); + (new ListenerLoader()).run(); + ModAndClassUtil.init(); + proxy.init(event); + } + + @Mod.EventHandler + public static void postInit(FMLPostInitializationEvent event) { + NetworkRegistry.INSTANCE.registerGuiHandler(FluidCraft.INSTANCE, new InventoryHandler()); + (new DefaultExtractorLoader()).run(); + proxy.postInit(event); + } + + @Mod.EventHandler + public void onLoadComplete(FMLLoadCompleteEvent event) { + + } + + public static ResourceLocation resource(String path) { + return new ResourceLocation(MODID, path); + } + +} diff --git a/src/main/java/com/glodblock/github/client/ClientHelper.java b/src/main/java/com/glodblock/github/client/ClientHelper.java new file mode 100644 index 00000000..41d19560 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/ClientHelper.java @@ -0,0 +1,29 @@ +package com.glodblock.github.client; + +import com.glodblock.github.client.textures.FCPartsTexture; +import cpw.mods.fml.common.FMLCommonHandler; +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.common.MinecraftForge; + +public class ClientHelper { + + @SubscribeEvent + public void updateTextureSheet( final TextureStitchEvent.Pre ev ) + { + if( ev.map.getTextureType() == 0 ) + { + for( final FCPartsTexture cb : FCPartsTexture.values() ) + { + cb.registerIcon( ev.map ); + } + } + } + + public static void register() { + ClientHelper handler = new ClientHelper(); + MinecraftForge.EVENT_BUS.register(handler); + FMLCommonHandler.instance().bus().register(handler); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiBaseFluidPatternTerminal.java b/src/main/java/com/glodblock/github/client/gui/GuiBaseFluidPatternTerminal.java new file mode 100644 index 00000000..bae893e3 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiBaseFluidPatternTerminal.java @@ -0,0 +1,163 @@ +package com.glodblock.github.client.gui; + +import appeng.api.config.ActionItems; +import appeng.api.config.ItemSubstitution; +import appeng.api.config.Settings; +import appeng.api.storage.ITerminalHost; +import appeng.client.gui.widgets.GuiImgButton; +import appeng.client.gui.widgets.GuiTabButton; +import appeng.container.slot.AppEngSlot; +import appeng.core.localization.GuiText; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.container.FCBasePartContainer; +import com.glodblock.github.network.CPacketFluidPatternTermBtns; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.ModAndClassUtil; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import org.lwjgl.input.Keyboard; + +public class GuiBaseFluidPatternTerminal extends GuiFCBaseMonitor { + + private static final String SUBSITUTION_DISABLE = "0"; + private static final String SUBSITUTION_ENABLE = "1"; + + private static final String CRAFTMODE_CRFTING = "1"; + private static final String CRAFTMODE_PROCESSING = "0"; + + public final FCBasePartContainer container; + + private GuiTabButton tabCraftButton; + private GuiTabButton tabProcessButton; + private GuiImgButton substitutionsEnabledBtn; + private GuiImgButton substitutionsDisabledBtn; + private GuiImgButton encodeBtn; + private GuiImgButton clearBtn; + private GuiImgButton doubleBtn; + + public GuiBaseFluidPatternTerminal(final InventoryPlayer inventoryPlayer, final ITerminalHost te ) + { + super( inventoryPlayer, te, new FCBasePartContainer( inventoryPlayer, te ) ); + this.container = (FCBasePartContainer) this.inventorySlots; + Ae2Reflect.setReservedSpace(this, 81); + } + + @Override + protected void actionPerformed( final GuiButton btn ) + { + super.actionPerformed( btn ); + + if( this.tabCraftButton == btn || this.tabProcessButton == btn ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "PatternTerminal.CraftMode", this.tabProcessButton == btn ? CRAFTMODE_CRFTING : CRAFTMODE_PROCESSING ) ); + } + else if( this.encodeBtn == btn ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "PatternTerminal.Encode", isShiftKeyDown() ? "2" : "1" ) ); + } + else if( this.clearBtn == btn ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "PatternTerminal.Clear", "1" ) ); + } + else if( this.substitutionsEnabledBtn == btn || this.substitutionsDisabledBtn == btn ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "PatternTerminal.Substitute", this.substitutionsEnabledBtn == btn ? SUBSITUTION_DISABLE : SUBSITUTION_ENABLE ) ); + } + else if (ModAndClassUtil.isDoubleButton && doubleBtn == btn) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "PatternTerminal.Double", Keyboard.isKeyDown( Keyboard.KEY_LSHIFT ) ? "1": "0") ); + } + } + + @Override + @SuppressWarnings("unchecked") + public void initGui() + { + super.initGui(); + this.tabCraftButton = new GuiTabButton( this.guiLeft + 173, this.guiTop + this.ySize - 177, new ItemStack( Blocks.crafting_table ), GuiText.CraftingPattern.getLocal(), itemRender ); + this.buttonList.add( this.tabCraftButton ); + + this.tabProcessButton = new GuiTabButton( this.guiLeft + 173, this.guiTop + this.ySize - 177, new ItemStack( Blocks.furnace ), GuiText.ProcessingPattern.getLocal(), itemRender ); + this.buttonList.add( this.tabProcessButton ); + + this.substitutionsEnabledBtn = new GuiImgButton( this.guiLeft + 84, this.guiTop + this.ySize - 163, Settings.ACTIONS, ItemSubstitution.ENABLED ); + this.substitutionsEnabledBtn.setHalfSize( true ); + this.buttonList.add( this.substitutionsEnabledBtn ); + + this.substitutionsDisabledBtn = new GuiImgButton( this.guiLeft + 84, this.guiTop + this.ySize - 163, Settings.ACTIONS, ItemSubstitution.DISABLED ); + this.substitutionsDisabledBtn.setHalfSize( true ); + this.buttonList.add( this.substitutionsDisabledBtn ); + + this.clearBtn = new GuiImgButton( this.guiLeft + 74, this.guiTop + this.ySize - 163, Settings.ACTIONS, ActionItems.CLOSE ); + this.clearBtn.setHalfSize( true ); + this.buttonList.add( this.clearBtn ); + + this.encodeBtn = new GuiImgButton( this.guiLeft + 147, this.guiTop + this.ySize - 142, Settings.ACTIONS, ActionItems.ENCODE ); + this.buttonList.add( this.encodeBtn ); + + if (ModAndClassUtil.isDoubleButton) { + this.doubleBtn = new GuiImgButton( this.guiLeft + 74, this.guiTop + this.ySize - 153, Settings.ACTIONS, ActionItems.DOUBLE ); + this.doubleBtn.setHalfSize( true ); + this.buttonList.add( this.doubleBtn ); + } + } + + @Override + public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + if( !this.container.isCraftingMode() ) + { + this.tabCraftButton.visible = false; + this.tabProcessButton.visible = true; + if (ModAndClassUtil.isDoubleButton) + this.doubleBtn.visible = true; + } + else + { + this.tabCraftButton.visible = true; + this.tabProcessButton.visible = false; + if (ModAndClassUtil.isDoubleButton) + this.doubleBtn.visible = false; + } + + if( this.container.substitute ) + { + this.substitutionsEnabledBtn.visible = true; + this.substitutionsDisabledBtn.visible = false; + } + else + { + this.substitutionsEnabledBtn.visible = false; + this.substitutionsDisabledBtn.visible = true; + } + + super.drawFG( offsetX, offsetY, mouseX, mouseY ); + this.fontRendererObj.drawString( GuiText.PatternTerminal.getLocal(), 8, this.ySize - 96 + 2 - Ae2Reflect.getReservedSpace(this), 4210752 ); + } + + @Override + protected String getBackground() + { + if( this.container.isCraftingMode() ) + { + return "guis/pattern.png"; + } + return "guis/pattern2.png"; + } + + @Override + protected void repositionSlot( final AppEngSlot s ) + { + if( s.isPlayerSide() ) + { + s.yDisplayPosition = s.getY() + this.ySize - 78 - 5; + } + else + { + s.yDisplayPosition = s.getY() + this.ySize - 78 - 3; + } + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFCBaseMonitor.java b/src/main/java/com/glodblock/github/client/gui/GuiFCBaseMonitor.java new file mode 100644 index 00000000..38dd7564 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFCBaseMonitor.java @@ -0,0 +1,750 @@ +package com.glodblock.github.client.gui; + +import appeng.api.config.*; +import appeng.api.implementations.tiles.IViewCellStorage; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.util.IConfigManager; +import appeng.api.util.IConfigurableObject; +import appeng.client.gui.AEBaseMEGui; +import appeng.client.gui.widgets.*; +import appeng.client.me.InternalSlotME; +import appeng.client.me.ItemRepo; +import appeng.client.me.SlotDisconnected; +import appeng.client.me.SlotME; +import appeng.container.AEBaseContainer; +import appeng.container.slot.*; +import appeng.core.AEConfig; +import appeng.core.AELog; +import appeng.core.localization.ButtonToolTips; +import appeng.core.localization.GuiText; +import appeng.core.sync.GuiBridge; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketInventoryAction; +import appeng.core.sync.packets.PacketSwitchGuis; +import appeng.core.sync.packets.PacketValueConfig; +import appeng.helpers.InventoryAction; +import appeng.integration.IntegrationRegistry; +import appeng.integration.IntegrationType; +import appeng.util.IConfigManagerHost; +import appeng.util.Platform; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.container.FCBaseMonitorContain; +import com.glodblock.github.client.gui.container.FCBasePartContainer; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.network.CPacketInventoryAction; +import com.glodblock.github.network.CPacketSetTargetItem; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.NameConst; +import com.google.common.base.Stopwatch; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class GuiFCBaseMonitor extends AEBaseMEGui implements ISortSource, IConfigManagerHost +{ + + public static int craftingGridOffsetX; + public static int craftingGridOffsetY; + + private static String memoryText = ""; + private final ItemRepo repo; + private final int offsetX = 9; + private final int lowerTextureOffset = 0; + private final IConfigManager configSrc; + private final boolean viewCell; + private final ItemStack[] myCurrentViewCells = new ItemStack[5]; + private final FCBaseMonitorContain monitorableContainer; + private GuiTabButton craftingStatusBtn; + private GuiImgButton craftingStatusImgBtn; + private MEGuiTextField searchField; + private String myName; + private int perRow = 9; + private int reservedSpace = 0; + private boolean customSortOrder = true; + private int rows = 0; + private int maxRows = Integer.MAX_VALUE; + private int standardSize; + private GuiImgButton ViewBox; + private GuiImgButton SortByBox; + private GuiImgButton SortDirBox; + private GuiImgButton searchBoxSettings; + private GuiImgButton terminalStyleBox; + private GuiImgButton searchStringSave; + + public GuiFCBaseMonitor(final InventoryPlayer inventoryPlayer, final ITerminalHost te ) + { + this( inventoryPlayer, te, new FCBasePartContainer( inventoryPlayer, te ) ); + } + + public GuiFCBaseMonitor( final InventoryPlayer inventoryPlayer, final ITerminalHost te, final FCBasePartContainer c ) + { + + super( c ); + + final GuiScrollbar scrollbar = new GuiScrollbar(); + this.setScrollBar( scrollbar ); + this.repo = new ItemRepo( scrollbar, this ); + + this.xSize = 185; + this.ySize = 204; + + if( te instanceof IViewCellStorage) + { + this.xSize += 33; + } + + this.standardSize = this.xSize; + + this.configSrc = ( (IConfigurableObject) this.inventorySlots ).getConfigManager(); + ( this.monitorableContainer = (FCBaseMonitorContain) this.inventorySlots ).setGui( this ); + + this.viewCell = te instanceof IViewCellStorage; + + if( te instanceof PartFluidPatternTerminal) + { + this.myName = NameConst.GUI_FLUID_PATTERN_TERMINAL; + } + } + + public void postUpdate( final List list ) + { + for( final IAEItemStack 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 + protected void actionPerformed( final GuiButton btn ) + { + + if( btn == this.craftingStatusBtn || btn == this.craftingStatusImgBtn ) + { + NetworkHandler.instance.sendToServer( new PacketSwitchGuis( GuiBridge.GUI_CRAFTING_STATUS ) ); + } + + 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() ); + + if( btn == this.terminalStyleBox ) + { + AEConfig.instance.settings.putSetting( iBtn.getSetting(), next ); + } + else if( btn == this.searchBoxSettings ) + { + AEConfig.instance.settings.putSetting( iBtn.getSetting(), next ); + } + else if( btn == this.searchStringSave ) + { + AEConfig.instance.preserveSearchBar = next == YesNo.YES; + } + else + { + try + { + NetworkHandler.instance.sendToServer( new PacketValueConfig( iBtn.getSetting().name(), next.name() ) ); + } + catch( final IOException e ) + { + AELog.debug( e ); + } + } + + iBtn.set( next ); + + if( next.getClass() == SearchBoxMode.class || next.getClass() == TerminalStyle.class ) + { + this.reinitalize(); + } + } + } + } + + private void reinitalize() + { + this.buttonList.clear(); + this.initGui(); + } + + @Override + public void initGui() + { + Keyboard.enableRepeatEvents( true ); + + this.maxRows = this.getMaxRows(); + this.perRow = AEConfig.instance.getConfigManager().getSetting( Settings.TERMINAL_STYLE ) != TerminalStyle.FULL ? 9 : 9 + ( ( this.width - this.standardSize ) / 18 ); + + final boolean hasNEI = IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.NEI ); + + final int NEI = hasNEI ? 0 : 0; + int top = hasNEI ? 22 : 0; + + final int magicNumber = 114 + 1; + final int extraSpace = this.height - magicNumber - NEI - top - this.reservedSpace; + + this.rows = (int) Math.floor( extraSpace / 18 ); + if( this.rows > this.maxRows ) + { + top += ( this.rows - this.maxRows ) * 18 / 2; + this.rows = this.maxRows; + } + + if( hasNEI ) + { + this.rows--; + } + + if( this.rows < 3 ) + { + this.rows = 3; + } + + this.getMeSlots().clear(); + for( int y = 0; y < this.rows; y++ ) + { + for( int x = 0; x < this.perRow; x++ ) + { + this.getMeSlots().add( new InternalSlotME( this.repo, x + y * this.perRow, this.offsetX + x * 18, 18 + y * 18 ) ); + } + } + + if( AEConfig.instance.getConfigManager().getSetting( Settings.TERMINAL_STYLE ) != TerminalStyle.FULL ) + { + this.xSize = this.standardSize + ( ( this.perRow - 9 ) * 18 ); + } + else + { + this.xSize = this.standardSize; + } + + super.initGui(); + // full size : 204 + // extra slots : 72 + // slot 18 + + this.ySize = magicNumber + this.rows * 18 + this.reservedSpace; + // this.guiTop = top; + final int unusedSpace = this.height - this.ySize; + this.guiTop = (int) Math.floor( unusedSpace / ( unusedSpace < 0 ? 3.8f : 2.0f ) ); + + int offset = this.guiTop + 8; + + if( this.customSortOrder ) + { + 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.ViewBox = new GuiImgButton( this.guiLeft - 18, offset, Settings.VIEW_MODE, this.configSrc.getSetting( Settings.VIEW_MODE ) ) ); + offset += 20; + + this.buttonList.add( this.SortDirBox = new GuiImgButton( this.guiLeft - 18, offset, Settings.SORT_DIRECTION, this.configSrc.getSetting( Settings.SORT_DIRECTION ) ) ); + offset += 20; + + this.buttonList.add( this.searchBoxSettings = new GuiImgButton( this.guiLeft - 18, offset, Settings.SEARCH_MODE, AEConfig.instance.settings.getSetting( Settings.SEARCH_MODE ) ) ); + offset += 20; + + this.buttonList.add( this.searchStringSave = new GuiImgButton( this.guiLeft - 18, offset, Settings.SAVE_SEARCH, AEConfig.instance.preserveSearchBar ? YesNo.YES : YesNo.NO ) ); + offset += 20; + + this.buttonList.add( this.terminalStyleBox = new GuiImgButton( this.guiLeft - 18, offset, Settings.TERMINAL_STYLE, AEConfig.instance.settings.getSetting( Settings.TERMINAL_STYLE ) ) ); + offset += 20; + + this.searchField = new MEGuiTextField( this.fontRendererObj, this.guiLeft + Math.max( 80, this.offsetX ), this.guiTop + 4, 90, 12 ); + this.searchField.setEnableBackgroundDrawing( false ); + this.searchField.setMaxStringLength( 25 ); + this.searchField.setTextColor( 0xFFFFFF ); + this.searchField.setVisible( true ); + searchField.setMessage(ButtonToolTips.SearchStringTooltip.getLocal()); + + if( this.viewCell) + { + if (AEConfig.instance.getConfigManager().getSetting(Settings.CRAFTING_STATUS).equals(CraftingStatus.BUTTON)) { + this.buttonList.add(this.craftingStatusImgBtn = new GuiImgButton(this.guiLeft - 18, offset, Settings.CRAFTING_STATUS, AEConfig.instance.settings.getSetting(Settings.CRAFTING_STATUS))); + } else { + this.buttonList.add(this.craftingStatusBtn = new GuiTabButton(this.guiLeft + 170, this.guiTop - 4, 2 + 11 * 16, GuiText.CraftingStatus.getLocal(), itemRender)); + this.craftingStatusBtn.setHideEdge( 13 ); // GuiTabButton implementation // + } + } + + // Enum setting = AEConfig.INSTANCE.getSetting( "Terminal", SearchBoxMode.class, SearchBoxMode.AUTOSEARCH ); + final Enum setting = AEConfig.instance.settings.getSetting( Settings.SEARCH_MODE ); + this.searchField.setFocused( SearchBoxMode.AUTOSEARCH == setting || SearchBoxMode.NEI_AUTOSEARCH == setting ); + + if (AEConfig.instance.preserveSearchBar || this.isSubGui()) + { + setSearchString(memoryText, false); + } + if( this.isSubGui() ) + { + this.repo.updateView(); + this.setScrollBar(); + } + + craftingGridOffsetX = Integer.MAX_VALUE; + craftingGridOffsetY = Integer.MAX_VALUE; + + for( final Object s : this.inventorySlots.inventorySlots ) + { + if( s instanceof AppEngSlot) + { + if( ( (Slot) s ).xDisplayPosition < 197 ) + { + this.repositionSlot( (AppEngSlot) s ); + } + } + + if( s instanceof SlotCraftingMatrix || s instanceof SlotFakeCraftingMatrix) + { + final Slot g = (Slot) s; + if( g.xDisplayPosition > 0 && g.yDisplayPosition > 0 ) + { + craftingGridOffsetX = Math.min( craftingGridOffsetX, g.xDisplayPosition ); + craftingGridOffsetY = Math.min( craftingGridOffsetY, g.yDisplayPosition ); + } + } + } + + craftingGridOffsetX -= 25; + craftingGridOffsetY -= 6; + } + + public void setSearchString(String memoryText, boolean updateView) + { + this.searchField.setText(memoryText); + this.repo.setSearchString(memoryText); + if (updateView) + { + this.repo.updateView(); + this.setScrollBar(); + } + } + + @Override + public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + this.fontRendererObj.drawString( this.getGuiDisplayName( GuiText.Terminal.getLocal() ), 8, 6, 4210752 ); + this.fontRendererObj.drawString( GuiText.inventory.getLocal(), 8, this.ySize - 96 + 3, 4210752 ); + } + + @Override + protected void mouseClicked( final int xCoord, final int yCoord, final int btn ) + { + final Enum searchMode = AEConfig.instance.settings.getSetting( Settings.SEARCH_MODE ); + + if( searchMode != SearchBoxMode.AUTOSEARCH && searchMode != SearchBoxMode.NEI_AUTOSEARCH ) + { + this.searchField.mouseClicked( xCoord, yCoord, btn ); + } + + if( btn == 1 && this.searchField.isMouseIn( xCoord, yCoord ) ) + { + setSearchString("", true); + } + + super.mouseClicked( xCoord, yCoord, btn ); + } + + @Override + protected void handleMouseClick( final Slot slot, final int slotIdx, final int ctrlDown, final int mouseButton ) + { + final EntityPlayer player = Minecraft.getMinecraft().thePlayer; + + if( slot instanceof SlotFake) + { + final InventoryAction action = ctrlDown == 1 ? InventoryAction.SPLIT_OR_PLACE_SINGLE : InventoryAction.PICKUP_OR_SET_DOWN; + + if( Ae2Reflect.getDragClick(this).size() > 1 ) + { + return; + } + + final PacketInventoryAction p = new PacketInventoryAction( action, slotIdx, 0 ); + NetworkHandler.instance.sendToServer( p ); + + return; + } + + if( slot instanceof SlotPatternTerm) + { + if( mouseButton == 6 ) + { + return; // prevent weird double clicks.. + } + + try + { + NetworkHandler.instance.sendToServer( ( (SlotPatternTerm) slot ).getRequest( isShiftKeyDown() ) ); + } + catch( final IOException e ) + { + AELog.debug( e ); + } + } + else if( slot instanceof SlotCraftingTerm ) + { + if( mouseButton == 6 ) + { + return; // prevent weird double clicks.. + } + + InventoryAction action = null; + if( isShiftKeyDown() ) + { + action = InventoryAction.CRAFT_SHIFT; + } + else + { + // Craft stack on right-click, craft single on left-click + action = ( mouseButton == 1 ) ? InventoryAction.CRAFT_STACK : InventoryAction.CRAFT_ITEM; + } + + final PacketInventoryAction p = new PacketInventoryAction( action, slotIdx, 0 ); + NetworkHandler.instance.sendToServer( p ); + + return; + } + + if( Keyboard.isKeyDown( Keyboard.KEY_SPACE ) ) + { + if( this.enableSpaceClicking() && !( slot instanceof SlotPatternTerm ) ) + { + IAEItemStack stack = null; + if( slot instanceof SlotME ) + { + stack = ( (SlotME) slot ).getAEStack(); + } + + int slotNum = Ae2Reflect.getInventorySlots(this).size(); + + if( !( slot instanceof SlotME ) && slot != null ) + { + slotNum = slot.slotNumber; + } + + ( (AEBaseContainer) this.inventorySlots ).setTargetStack( stack ); + final PacketInventoryAction p = new PacketInventoryAction( InventoryAction.MOVE_REGION, slotNum, 0 ); + NetworkHandler.instance.sendToServer( p ); + return; + } + } + + if( slot instanceof SlotDisconnected) + { + if( Ae2Reflect.getDragClick(this).size() > 1 ) + { + return; + } + + InventoryAction action = null; + + switch( mouseButton ) + { + case 0: // pickup / set-down. + { + ItemStack heldStack = player.inventory.getItemStack(); + if (slot.getStack() == null && heldStack != null) + action = InventoryAction.SPLIT_OR_PLACE_SINGLE; + else if (slot.getStack() != null && (heldStack == null || heldStack.stackSize <= 1)) + action = InventoryAction.PICKUP_OR_SET_DOWN; + } + break; + case 1: + action = ctrlDown == 1 ? InventoryAction.PICKUP_SINGLE : InventoryAction.SHIFT_CLICK; + break; + + case 3: // creative dupe: + + if( player.capabilities.isCreativeMode ) + { + action = InventoryAction.CREATIVE_DUPLICATE; + } + + break; + + default: + case 4: // drop item: + case 6: + } + + if( action != null ) + { + final PacketInventoryAction p = new PacketInventoryAction( action, slot.getSlotIndex(), ( (SlotDisconnected) slot ).getSlot().getId() ); + NetworkHandler.instance.sendToServer( p ); + } + + return; + } + + if( slot instanceof SlotME ) + { + InventoryAction action = null; + IAEItemStack stack = null; + + switch( mouseButton ) + { + case 0: // pickup / set-down. + action = ctrlDown == 1 ? InventoryAction.SPLIT_OR_PLACE_SINGLE : InventoryAction.PICKUP_OR_SET_DOWN; + stack = ( (SlotME) slot ).getAEStack(); + + if( stack != null && action == InventoryAction.PICKUP_OR_SET_DOWN && stack.getStackSize() == 0 && player.inventory.getItemStack() == null ) + { + action = InventoryAction.AUTO_CRAFT; + } + + break; + case 1: + action = ctrlDown == 1 ? InventoryAction.PICKUP_SINGLE : InventoryAction.SHIFT_CLICK; + stack = ( (SlotME) slot ).getAEStack(); + break; + + case 3: // creative dupe: + + stack = ( (SlotME) slot ).getAEStack(); + if( stack != null && stack.isCraftable() ) + { + action = InventoryAction.AUTO_CRAFT; + } + else if( player.capabilities.isCreativeMode ) + { + final IAEItemStack slotItem = ( (SlotME) slot ).getAEStack(); + if( slotItem != null ) + { + action = InventoryAction.CREATIVE_DUPLICATE; + } + } + break; + + default: + case 4: // drop item: + case 6: + } + + if (action == InventoryAction.AUTO_CRAFT) { + ((AEBaseContainer) this.inventorySlots).setTargetStack( stack ); + FluidCraft.proxy.netHandler.sendToServer(new CPacketInventoryAction(action, Ae2Reflect.getInventorySlots(this).size(), 0, stack)); + } + else if( action != null ) + { + ( (AEBaseContainer) this.inventorySlots ).setTargetStack( stack ); + final PacketInventoryAction p = new PacketInventoryAction( action, Ae2Reflect.getInventorySlots(this).size(), 0 ); + NetworkHandler.instance.sendToServer( p ); + } + + return; + } + + super.handleMouseClick( slot, slotIdx, ctrlDown, mouseButton ); + } + + @Override + public void onGuiClosed() + { + super.onGuiClosed(); + Keyboard.enableRepeatEvents( false ); + memoryText = this.searchField.getText(); + } + + @Override + public void drawBG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + + this.bindTexture( this.getBackground() ); + final int x_width = 197; + this.drawTexturedModalRect( offsetX, offsetY, 0, 0, x_width, 18 ); + + if( this.viewCell ) + { + this.drawTexturedModalRect( offsetX + x_width, offsetY, x_width, 0, 46, 128 ); + } + + for( int x = 0; x < this.rows; x++ ) + { + this.drawTexturedModalRect( offsetX, offsetY + 18 + x * 18, 0, 18, x_width, 18 ); + } + + this.drawTexturedModalRect( offsetX, offsetY + 16 + this.rows * 18 + this.lowerTextureOffset, 0, 106 - 18 - 18, x_width, 99 + this.reservedSpace - this.lowerTextureOffset ); + + if( this.viewCell ) + { + boolean update = false; + + for( int i = 0; i < 5; i++ ) + { + if( this.myCurrentViewCells[i] != this.monitorableContainer.getCellViewSlot( i ).getStack() ) + { + update = true; + this.myCurrentViewCells[i] = this.monitorableContainer.getCellViewSlot( i ).getStack(); + } + } + + if( update ) + { + this.repo.setViewCell( this.myCurrentViewCells ); + } + } + + if( this.searchField != null ) + { + this.searchField.drawTextBox(); + } + } + + protected String getBackground() + { + return "guis/terminal.png"; + } + + @Override + protected boolean isPowered() + { + return this.repo.hasPower(); + } + + int getMaxRows() + { + return AEConfig.instance.getConfigManager().getSetting( Settings.TERMINAL_STYLE ) == TerminalStyle.SMALL ? 6 : Integer.MAX_VALUE; + } + + protected void repositionSlot( final AppEngSlot s ) + { + s.yDisplayPosition = s.getY() + this.ySize - 78 - 5; + } + + @Override + protected void keyTyped( final char character, final int key ) + { + 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 + public void updateScreen() + { + this.repo.setPower( this.monitorableContainer.isPowered() ); + super.updateScreen(); + } + + @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( final IConfigManager manager, final Enum settingName, final 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 ) ); + } + + if( this.ViewBox != null ) + { + this.ViewBox.set( this.configSrc.getSetting( Settings.VIEW_MODE ) ); + } + + this.repo.updateView(); + } + + int getReservedSpace() + { + return this.reservedSpace; + } + + void setReservedSpace( final int reservedSpace ) + { + this.reservedSpace = reservedSpace; + } + + public boolean isCustomSortOrder() + { + return this.customSortOrder; + } + + void setCustomSortOrder( final boolean customSortOrder ) + { + this.customSortOrder = customSortOrder; + } + + public int getStandardSize() + { + return this.standardSize; + } + + void setStandardSize( final int standardSize ) + { + this.standardSize = standardSize; + } + + @Override + public void drawScreen( final int mouseX, final int mouseY, final float btn ) { + super.drawScreen(mouseX, mouseY, btn); + if (AEConfig.instance.preserveSearchBar && searchField != null) + handleTooltip(mouseX, mouseY, searchField.new TooltipProvider()); + } + public boolean isOverSearchField(int x, int y) + { + return searchField.isMouseIn(x,y); + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftAmount.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftAmount.java new file mode 100644 index 00000000..f854df83 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftAmount.java @@ -0,0 +1,257 @@ +package com.glodblock.github.client.gui; + +import appeng.api.AEApi; +import appeng.api.definitions.IDefinitions; +import appeng.api.definitions.IParts; +import appeng.api.storage.ITerminalHost; +import appeng.client.gui.AEBaseGui; +import appeng.client.gui.widgets.GuiNumberBox; +import appeng.client.gui.widgets.GuiTabButton; +import appeng.container.AEBaseContainer; +import appeng.container.implementations.ContainerCraftAmount; +import appeng.core.AEConfig; +import appeng.core.localization.GuiText; +import appeng.helpers.Reflected; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.network.CPacketCraftRequest; +import com.glodblock.github.network.CPacketSwitchGuis; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public class GuiFluidCraftAmount extends AEBaseGui +{ + private GuiNumberBox amountToCraft; + private GuiTabButton originalGuiBtn; + + private GuiButton next; + + private GuiButton plus1; + private GuiButton plus10; + private GuiButton plus100; + private GuiButton plus1000; + private GuiButton minus1; + private GuiButton minus10; + private GuiButton minus100; + private GuiButton minus1000; + + private GuiType originalGui; + + @Reflected + public GuiFluidCraftAmount(final InventoryPlayer inventoryPlayer, final ITerminalHost te ) + { + super( new ContainerCraftAmount( inventoryPlayer, te ) ); + } + + @Override + public void initGui() + { + super.initGui(); + + final int a = AEConfig.instance.craftItemsByStackAmounts( 0 ); + final int b = AEConfig.instance.craftItemsByStackAmounts( 1 ); + final int c = AEConfig.instance.craftItemsByStackAmounts( 2 ); + final int d = AEConfig.instance.craftItemsByStackAmounts( 3 ); + + this.buttonList.add( this.plus1 = new GuiButton( 0, this.guiLeft + 20, this.guiTop + 26, 22, 20, "+" + a ) ); + this.buttonList.add( this.plus10 = new GuiButton( 0, this.guiLeft + 48, this.guiTop + 26, 28, 20, "+" + b ) ); + this.buttonList.add( this.plus100 = new GuiButton( 0, this.guiLeft + 82, this.guiTop + 26, 32, 20, "+" + c ) ); + this.buttonList.add( this.plus1000 = new GuiButton( 0, this.guiLeft + 120, this.guiTop + 26, 38, 20, "+" + d ) ); + + this.buttonList.add( this.minus1 = new GuiButton( 0, this.guiLeft + 20, this.guiTop + 75, 22, 20, "-" + a ) ); + this.buttonList.add( this.minus10 = new GuiButton( 0, this.guiLeft + 48, this.guiTop + 75, 28, 20, "-" + b ) ); + this.buttonList.add( this.minus100 = new GuiButton( 0, this.guiLeft + 82, this.guiTop + 75, 32, 20, "-" + c ) ); + this.buttonList.add( this.minus1000 = new GuiButton( 0, this.guiLeft + 120, this.guiTop + 75, 38, 20, "-" + d ) ); + + this.buttonList.add( this.next = new GuiButton( 0, this.guiLeft + 128, this.guiTop + 51, 38, 20, GuiText.Next.getLocal() ) ); + + ItemStack myIcon = null; + final Object target = ( (AEBaseContainer) this.inventorySlots ).getTarget(); + final IDefinitions definitions = AEApi.instance().definitions(); + final IParts parts = definitions.parts(); + + if( target instanceof PartFluidPatternTerminal) + { + myIcon = new ItemStack(ItemAndBlockHolder.FLUID_TERMINAL, 1); + this.originalGui = GuiType.FLUID_PATTERN_TERMINAL; + } + + if( this.originalGui != null && myIcon != null ) + { + this.buttonList.add( this.originalGuiBtn = new GuiTabButton( this.guiLeft + 154, this.guiTop, myIcon, myIcon.getDisplayName(), itemRender ) ); + } + + this.amountToCraft = new GuiNumberBox( this.fontRendererObj, this.guiLeft + 62, this.guiTop + 57, 59, this.fontRendererObj.FONT_HEIGHT, Integer.class ); + this.amountToCraft.setEnableBackgroundDrawing( false ); + this.amountToCraft.setMaxStringLength( 16 ); + this.amountToCraft.setTextColor( 0xFFFFFF ); + this.amountToCraft.setVisible( true ); + this.amountToCraft.setFocused( true ); + this.amountToCraft.setText( "1" ); + this.amountToCraft.setSelectionPos(0); + } + + @Override + public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + this.fontRendererObj.drawString( GuiText.SelectAmount.getLocal(), 8, 6, 4210752 ); + } + + @Override + public void drawBG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + this.next.displayString = isShiftKeyDown() ? GuiText.Start.getLocal() : GuiText.Next.getLocal(); + + this.bindTexture( "guis/craftAmt.png" ); + this.drawTexturedModalRect( offsetX, offsetY, 0, 0, this.xSize, this.ySize ); + + try + { + Long.parseLong( this.amountToCraft.getText() ); + this.next.enabled = this.amountToCraft.getText().length() > 0; + } + catch( final NumberFormatException e ) + { + this.next.enabled = false; + } + + this.amountToCraft.drawTextBox(); + } + + @Override + protected void keyTyped( final char character, final int key ) + { + if( !this.checkHotbarKeys( key ) ) + { + if( key == 28 ) + { + this.actionPerformed( this.next ); + } + if( ( key == 211 || key == 205 || key == 203 || key == 14 || character == '-' || Character.isDigit( character ) ) && this.amountToCraft.textboxKeyTyped( character, key ) ) + { + try + { + String out = this.amountToCraft.getText(); + + boolean fixed = false; + while( out.startsWith( "0" ) && out.length() > 1 ) + { + out = out.substring( 1 ); + fixed = true; + } + + if( fixed ) + { + this.amountToCraft.setText( out ); + } + + if( out.isEmpty() ) + { + out = "0"; + } + + final long result = Long.parseLong( out ); + if( result < 0 ) + { + this.amountToCraft.setText( "1" ); + } + } + catch( final NumberFormatException e ) + { + // :P + } + } + else + { + super.keyTyped( character, key ); + } + } + } + + @Override + protected void actionPerformed( final GuiButton btn ) + { + super.actionPerformed( btn ); + + try + { + + if( btn == this.originalGuiBtn ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketSwitchGuis( this.originalGui ) ); + } + + if( btn == this.next ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketCraftRequest( Integer.parseInt( this.amountToCraft.getText() ), isShiftKeyDown() ) ); + } + } + catch( final NumberFormatException e ) + { + // nope.. + this.amountToCraft.setText( "1" ); + } + + 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 int i ) + { + try + { + String out = this.amountToCraft.getText(); + + boolean fixed = false; + while( out.startsWith( "0" ) && out.length() > 1 ) + { + out = out.substring( 1 ); + fixed = true; + } + + if( fixed ) + { + this.amountToCraft.setText( out ); + } + + if( out.isEmpty() ) + { + out = "0"; + } + + long result = Integer.parseInt( out ); + + if( result == 1 && i > 1 ) + { + result = 0; + } + + result += i; + if( result < 1 ) + { + result = 1; + } + + out = Long.toString( result ); + Integer.parseInt( out ); + this.amountToCraft.setText( out ); + } + catch( final NumberFormatException e ) + { + // :P + } + } + + protected String getBackground() + { + return "guis/craftAmt.png"; + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftConfirm.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftConfirm.java new file mode 100644 index 00000000..f650d5d1 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidCraftConfirm.java @@ -0,0 +1,531 @@ +package com.glodblock.github.client.gui; + +import appeng.api.AEApi; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.client.gui.AEBaseGui; +import appeng.client.gui.widgets.GuiScrollbar; +import appeng.core.AELog; +import appeng.core.localization.GuiText; +import appeng.util.Platform; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.container.ContainerFluidCraftConfirm; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.network.CPacketFluidPatternTermBtns; +import com.glodblock.github.network.CPacketSwitchGuis; +import com.google.common.base.Joiner; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +public class GuiFluidCraftConfirm extends AEBaseGui { + + private final ContainerFluidCraftConfirm ccc; + + private final int rows = 5; + + private final IItemList storage = AEApi.instance().storage().createItemList(); + private final IItemList pending = AEApi.instance().storage().createItemList(); + private final IItemList missing = AEApi.instance().storage().createItemList(); + + private final List visual = new ArrayList(); + + private GuiType OriginalGui; + private GuiButton cancel; + private GuiButton start; + private GuiButton selectCPU; + private int tooltip = -1; + + public GuiFluidCraftConfirm(final InventoryPlayer inventoryPlayer, final ITerminalHost te ) + { + super( new ContainerFluidCraftConfirm( inventoryPlayer, te ) ); + this.xSize = 238; + this.ySize = 206; + + final GuiScrollbar scrollbar = new GuiScrollbar(); + this.setScrollBar( scrollbar ); + + this.ccc = (ContainerFluidCraftConfirm) this.inventorySlots; + + if( te instanceof PartFluidPatternTerminal) + { + this.OriginalGui = GuiType.FLUID_PATTERN_TERMINAL; + } + } + + boolean isAutoStart() + { + return ( (ContainerFluidCraftConfirm) this.inventorySlots ).isAutoStart(); + } + + @Override + public void initGui() + { + super.initGui(); + + this.start = new GuiButton( 0, this.guiLeft + 162, this.guiTop + this.ySize - 25, 50, 20, GuiText.Start.getLocal() ); + this.start.enabled = false; + this.buttonList.add( this.start ); + + this.selectCPU = new GuiButton( 0, this.guiLeft + ( 219 - 180 ) / 2, this.guiTop + this.ySize - 68, 180, 20, GuiText.CraftingCPU.getLocal() + ": " + GuiText.Automatic ); + this.selectCPU.enabled = false; + this.buttonList.add( this.selectCPU ); + + if( this.OriginalGui != null ) + { + this.cancel = new GuiButton( 0, this.guiLeft + 6, this.guiTop + this.ySize - 25, 50, 20, GuiText.Cancel.getLocal() ); + } + + this.buttonList.add( this.cancel ); + } + + @Override + public void drawScreen( final int mouseX, final int mouseY, final float btn ) + { + this.updateCPUButtonText(); + + this.start.enabled = !( this.ccc.hasNoCPU() || this.isSimulation() ); + this.selectCPU.enabled = !this.isSimulation(); + + final int gx = ( this.width - this.xSize ) / 2; + final int gy = ( this.height - this.ySize ) / 2; + + this.tooltip = -1; + + final int offY = 23; + int y = 0; + int x = 0; + for( int z = 0; z <= 4 * 5; z++ ) + { + final int minX = gx + 9 + x * 67; + final int minY = gy + 22 + y * offY; + + if( minX < mouseX && minX + 67 > mouseX ) + { + if( minY < mouseY && minY + offY - 2 > mouseY ) + { + this.tooltip = z; + break; + } + } + + x++; + + if( x > 2 ) + { + y++; + x = 0; + } + } + + super.drawScreen( mouseX, mouseY, btn ); + } + + private void updateCPUButtonText() + { + String btnTextText = GuiText.CraftingCPU.getLocal() + ": " + GuiText.Automatic.getLocal(); + if( this.ccc.getSelectedCpu() >= 0 )// && status.selectedCpu < status.cpus.size() ) + { + if( this.ccc.getName().length() > 0 ) + { + final String name = this.ccc.getName().substring( 0, Math.min( 20, this.ccc.getName().length() ) ); + btnTextText = GuiText.CraftingCPU.getLocal() + ": " + name; + } + else + { + btnTextText = GuiText.CraftingCPU.getLocal() + ": #" + this.ccc.getSelectedCpu(); + } + } + + if( this.ccc.hasNoCPU() ) + { + btnTextText = GuiText.NoCraftingCPUs.getLocal(); + } + + this.selectCPU.displayString = btnTextText; + } + + private boolean isSimulation() + { + return ( (ContainerFluidCraftConfirm) this.inventorySlots ).isSimulation(); + } + + @Override + public void drawFG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + final long BytesUsed = this.ccc.getUsedBytes(); + final String byteUsed = NumberFormat.getInstance().format( BytesUsed ); + final String Add = BytesUsed > 0 ? ( byteUsed + ' ' + GuiText.BytesUsed.getLocal() ) : GuiText.CalculatingWait.getLocal(); + this.fontRendererObj.drawString( GuiText.CraftingPlan.getLocal() + " - " + Add, 8, 7, 4210752 ); + + String dsp = null; + + if( this.isSimulation() ) + { + dsp = GuiText.Simulation.getLocal(); + } + else + { + dsp = this.ccc.getCpuAvailableBytes() > 0 ? ( GuiText.Bytes.getLocal() + ": " + this.ccc.getCpuAvailableBytes() + " : " + GuiText.CoProcessors.getLocal() + ": " + this.ccc.getCpuCoProcessors() ) : GuiText.Bytes.getLocal() + ": N/A : " + GuiText.CoProcessors.getLocal() + ": N/A"; + } + + final int offset = ( 219 - this.fontRendererObj.getStringWidth( dsp ) ) / 2; + this.fontRendererObj.drawString( dsp, offset, 165, 4210752 ); + + final int sectionLength = 67; + + int x = 0; + int y = 0; + final int xo = 9; + final int yo = 22; + final int viewStart = this.getScrollBar().getCurrentScroll() * 3; + final int viewEnd = viewStart + 3 * this.rows; + + String dspToolTip = ""; + final List lineList = new LinkedList(); + int toolPosX = 0; + int toolPosY = 0; + + final int offY = 23; + + for( int z = viewStart; z < Math.min( viewEnd, this.visual.size() ); z++ ) + { + final IAEItemStack refStack = this.visual.get( z );// repo.getReferenceItem( z ); + if( refStack != null ) + { + GL11.glPushMatrix(); + GL11.glScaled( 0.5, 0.5, 0.5 ); + + final IAEItemStack stored = this.storage.findPrecise( refStack ); + final IAEItemStack pendingStack = this.pending.findPrecise( refStack ); + final IAEItemStack missingStack = this.missing.findPrecise( refStack ); + + int lines = 0; + + if( stored != null && stored.getStackSize() > 0 ) + { + lines++; + } + if( pendingStack != null && pendingStack.getStackSize() > 0 ) + { + lines++; + } + if( pendingStack != null && pendingStack.getStackSize() > 0 ) + { + lines++; + } + + final int negY = ( ( lines - 1 ) * 5 ) / 2; + int downY = 0; + + if( stored != null && stored.getStackSize() > 0 ) + { + String str = Long.toString( stored.getStackSize() ); + if( stored.getStackSize() >= 10000 ) + { + str = Long.toString( stored.getStackSize() / 1000 ) + 'k'; + } + if( stored.getStackSize() >= 10000000 ) + { + str = Long.toString( stored.getStackSize() / 1000000 ) + 'm'; + } + + str = GuiText.FromStorage.getLocal() + ": " + str; + final int w = 4 + this.fontRendererObj.getStringWidth( str ); + this.fontRendererObj.drawString( str, (int) ( ( x * ( 1 + sectionLength ) + xo + sectionLength - 19 - ( w * 0.5 ) ) * 2 ), ( y * offY + yo + 6 - negY + downY ) * 2, 4210752 ); + + if( this.tooltip == z - viewStart ) + { + lineList.add( GuiText.FromStorage.getLocal() + ": " + Long.toString( stored.getStackSize() ) ); + } + + downY += 5; + } + + boolean red = false; + if( missingStack != null && missingStack.getStackSize() > 0 ) + { + String str = Long.toString( missingStack.getStackSize() ); + if( missingStack.getStackSize() >= 10000 ) + { + str = Long.toString( missingStack.getStackSize() / 1000 ) + 'k'; + } + if( missingStack.getStackSize() >= 10000000 ) + { + str = Long.toString( missingStack.getStackSize() / 1000000 ) + 'm'; + } + + str = GuiText.Missing.getLocal() + ": " + str; + final int w = 4 + this.fontRendererObj.getStringWidth( str ); + this.fontRendererObj.drawString( str, (int) ( ( x * ( 1 + sectionLength ) + xo + sectionLength - 19 - ( w * 0.5 ) ) * 2 ), ( y * offY + yo + 6 - negY + downY ) * 2, 4210752 ); + + if( this.tooltip == z - viewStart ) + { + lineList.add( GuiText.Missing.getLocal() + ": " + Long.toString( missingStack.getStackSize() ) ); + } + + red = true; + downY += 5; + } + + if( pendingStack != null && pendingStack.getStackSize() > 0 ) + { + String str = Long.toString( pendingStack.getStackSize() ); + if( pendingStack.getStackSize() >= 10000 ) + { + str = Long.toString( pendingStack.getStackSize() / 1000 ) + 'k'; + } + if( pendingStack.getStackSize() >= 10000000 ) + { + str = Long.toString( pendingStack.getStackSize() / 1000000 ) + 'm'; + } + + str = GuiText.ToCraft.getLocal() + ": " + str; + final int w = 4 + this.fontRendererObj.getStringWidth( str ); + this.fontRendererObj.drawString( str, (int) ( ( x * ( 1 + sectionLength ) + xo + sectionLength - 19 - ( w * 0.5 ) ) * 2 ), ( y * offY + yo + 6 - negY + downY ) * 2, 4210752 ); + + if( this.tooltip == z - viewStart ) + { + lineList.add( GuiText.ToCraft.getLocal() + ": " + Long.toString( pendingStack.getStackSize() ) ); + } + } + + GL11.glPopMatrix(); + final int posX = x * ( 1 + sectionLength ) + xo + sectionLength - 19; + final int posY = y * offY + yo; + + final ItemStack is = refStack.copy().getItemStack(); + + if( this.tooltip == z - viewStart ) + { + dspToolTip = Platform.getItemDisplayName( is ); + if( lineList.size() > 0 ) + { + addItemTooltip(is, lineList); + dspToolTip = dspToolTip + '\n' + Joiner.on( "\n" ).join( lineList ); + } + + toolPosX = x * ( 1 + sectionLength ) + xo + sectionLength - 8; + toolPosY = y * offY + yo; + } + + this.drawItem( posX, posY, is ); + + if( red ) + { + final int startX = x * ( 1 + sectionLength ) + xo; + final int startY = posY - 4; + drawRect( startX, startY, startX + sectionLength, startY + offY, 0x1AFF0000 ); + } + + x++; + + if( x > 2 ) + { + y++; + x = 0; + } + } + } + + if( this.tooltip >= 0 && dspToolTip.length() > 0 ) + { + this.drawTooltip( toolPosX, toolPosY + 10, 0, dspToolTip ); + } + } + + @Override + public void drawBG( final int offsetX, final int offsetY, final int mouseX, final int mouseY ) + { + this.setScrollBar(); + this.bindTexture( "guis/craftingreport.png" ); + this.drawTexturedModalRect( offsetX, offsetY, 0, 0, this.xSize, this.ySize ); + } + + private void setScrollBar() + { + final int size = this.visual.size(); + + this.getScrollBar().setTop( 19 ).setLeft( 218 ).setHeight( 114 ); + this.getScrollBar().setRange( 0, ( size + 2 ) / 3 - this.rows, 1 ); + } + + public void postUpdate( final List list, final byte ref ) + { + switch( ref ) + { + case 0: + for( final IAEItemStack l : list ) + { + this.handleInput( this.storage, l ); + } + break; + + case 1: + for( final IAEItemStack l : list ) + { + this.handleInput( this.pending, l ); + } + break; + + case 2: + for( final IAEItemStack l : list ) + { + this.handleInput( this.missing, l ); + } + break; + } + + for( final IAEItemStack l : list ) + { + final long amt = this.getTotal( l ); + + if( amt <= 0 ) + { + this.deleteVisualStack( l ); + } + else + { + final IAEItemStack is = this.findVisualStack( l ); + is.setStackSize( amt ); + } + } + + this.setScrollBar(); + } + + private void handleInput( final IItemList s, final IAEItemStack l ) + { + IAEItemStack a = s.findPrecise( l ); + + if( l.getStackSize() <= 0 ) + { + if( a != null ) + { + a.reset(); + } + } + else + { + if( a == null ) + { + s.add( l.copy() ); + a = s.findPrecise( l ); + } + + if( a != null ) + { + a.setStackSize( l.getStackSize() ); + } + } + } + + private long getTotal( final IAEItemStack is ) + { + final IAEItemStack a = this.storage.findPrecise( is ); + final IAEItemStack c = this.pending.findPrecise( is ); + final IAEItemStack m = this.missing.findPrecise( is ); + + long total = 0; + + if( a != null ) + { + total += a.getStackSize(); + } + + if( c != null ) + { + total += c.getStackSize(); + } + + if( m != null ) + { + total += m.getStackSize(); + } + + return total; + } + + private void deleteVisualStack( final IAEItemStack l ) + { + final Iterator i = this.visual.iterator(); + while( i.hasNext() ) + { + final IAEItemStack o = i.next(); + if( o.equals( l ) ) + { + i.remove(); + return; + } + } + } + + private IAEItemStack findVisualStack( final IAEItemStack l ) + { + for( final IAEItemStack o : this.visual ) + { + if( o.equals( l ) ) + { + return o; + } + } + + final IAEItemStack stack = l.copy(); + this.visual.add( stack ); + return stack; + } + + @Override + protected void keyTyped( final char character, final int key ) + { + if( !this.checkHotbarKeys( key ) ) + { + if( key == 28 ) + { + this.actionPerformed( this.start ); + } + super.keyTyped( character, key ); + } + } + + @Override + protected void actionPerformed( final GuiButton btn ) + { + super.actionPerformed( btn ); + + final boolean backwards = Mouse.isButtonDown( 1 ); + + if( btn == this.selectCPU ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "Terminal.Cpu", backwards ? "Prev" : "Next" ) ); + } + + if( btn == this.cancel ) + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketSwitchGuis( this.OriginalGui ) ); + } + + if( btn == this.start ) + { + try + { + FluidCraft.proxy.netHandler.sendToServer( new CPacketFluidPatternTermBtns( "Terminal.Start", "Start" ) ); + } + catch( final Throwable e ) + { + AELog.debug( e ); + } + } + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidPacketDecoder.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidPacketDecoder.java new file mode 100644 index 00000000..989a439d --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidPacketDecoder.java @@ -0,0 +1,33 @@ +package com.glodblock.github.client.gui; + +import appeng.client.gui.AEBaseGui; +import appeng.core.localization.GuiText; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.container.ContainerFluidPacketDecoder; +import com.glodblock.github.common.tile.TileFluidPacketDecoder; +import com.glodblock.github.util.NameConst; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.util.ResourceLocation; + +public class GuiFluidPacketDecoder extends AEBaseGui { + + private static final ResourceLocation TEX_BG = FluidCraft.resource("textures/gui/fluid_packet_decoder.png"); + + public GuiFluidPacketDecoder(InventoryPlayer ipl, TileFluidPacketDecoder tile) { + super(new ContainerFluidPacketDecoder(ipl, tile)); + } + + @Override + public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) { + mc.getTextureManager().bindTexture(TEX_BG); + drawTexturedModalRect(offsetX, offsetY, 0, 0, 176, ySize); + } + + @Override + public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) { + fontRendererObj.drawString(getGuiDisplayName(I18n.format(NameConst.GUI_FLUID_PACKET_DECODER)), 8, 6, 0x404040); + fontRendererObj.drawString(GuiText.inventory.getLocal(), 8, ySize - 94, 0x404040); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternEncoder.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternEncoder.java new file mode 100644 index 00000000..45f71284 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternEncoder.java @@ -0,0 +1,136 @@ +package com.glodblock.github.client.gui; + +import appeng.api.storage.data.IAEItemStack; +import appeng.client.gui.AEBaseGui; +import appeng.client.render.AppEngRenderItem; +import appeng.core.localization.GuiText; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.container.ContainerFluidPatternEncoder; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.tile.TileFluidPatternEncoder; +import com.glodblock.github.inventory.gui.MouseRegionManager; +import com.glodblock.github.inventory.slot.SlotFluid; +import com.glodblock.github.inventory.slot.SlotSingleItem; +import com.glodblock.github.network.CPacketEncodePattern; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.NameConst; +import com.glodblock.github.util.Util; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fluids.FluidStack; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class GuiFluidPatternEncoder extends AEBaseGui { + + private static final ResourceLocation TEX_BG = FluidCraft.resource("textures/gui/fluid_pattern_encoder.png"); + + private final ContainerFluidPatternEncoder cont; + private final MouseRegionManager mouseRegions = new MouseRegionManager(this, fontRendererObj); + private final AppEngRenderItem stackSizeRenderer = Ae2Reflect.getStackSizeRenderer(this); + + public GuiFluidPatternEncoder(InventoryPlayer ipl, TileFluidPatternEncoder tile) { + super(new ContainerFluidPatternEncoder(ipl, tile)); + this.cont = (ContainerFluidPatternEncoder)inventorySlots; + mouseRegions.addRegion(141, 38, 10, 10, new MouseRegionManager.Handler() { + @Override + public List getTooltip() { + return Collections.singletonList(I18n.format(NameConst.TT_ENCODE_PATTERN)); + } + + @Override + public boolean onClick(int button) { + if (button == 0) { + if (cont.canEncodePattern()) { + FluidCraft.proxy.netHandler.sendToServer(new CPacketEncodePattern()); + } + return true; + } + return false; + } + }); + } + + @Override + protected void mouseClicked(int xCoord, int yCoord, int btn) { + if (mouseRegions.onClick(xCoord, yCoord, btn)) { + super.mouseClicked(xCoord, yCoord, btn); + } + } + + @Override + public void drawBG(int offsetX, int offsetY, int mouseX, int mouseY) { + mc.getTextureManager().bindTexture(TEX_BG); + drawTexturedModalRect(offsetX, offsetY, 0, 0, 176, ySize); + } + + @Override + public void drawFG(int offsetX, int offsetY, int mouseX, int mouseY) { + fontRendererObj.drawString(getGuiDisplayName(I18n.format(NameConst.GUI_FLUID_PATTERN_ENCODER)), 8, 6, 0x404040); + fontRendererObj.drawString(GuiText.inventory.getLocal(), 8, ySize - 94, 0x404040); + mouseRegions.render(mouseX, mouseY); + } + + @Override + public void func_146977_a( final Slot s ) + { + this.drawSlot0( s ); + } + + public void drawSlot0(Slot slot) { + if (slot instanceof SlotFluid) { + IAEItemStack stack = ((SlotFluid)slot).getAeStack(); + super.func_146977_a(new SlotSingleItem(slot)); + if (stack == null) return; + IAEItemStack fake = stack.copy(); + if (fake.getItemStack().getItem() instanceof ItemFluidPacket) { + if (ItemFluidPacket.getFluidStack(stack) != null && ItemFluidPacket.getFluidStack(stack).amount > 0) + fake.setStackSize(ItemFluidPacket.getFluidStack(stack).amount); + } + stackSizeRenderer.setAeStack(fake); + stackSizeRenderer.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), stack.getItemStack(), slot.xDisplayPosition, slot.yDisplayPosition); + } else { + super.func_146977_a(slot); + } + } + + @Override + @SuppressWarnings({"unchecked", "rawtypes"}) + protected void renderToolTip(ItemStack stack, int x, int y) { + List list; + if (stack.getItem() instanceof ItemFluidPacket) { + FluidStack fluid = ItemFluidPacket.getFluidStack(stack); + if (fluid != null) { + list = Arrays.asList(fluid.getLocalizedName(), String.format(EnumChatFormatting.GRAY + "%,d mB", fluid.amount)); + } + else { + list = stack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + } + } + else { + list = stack.getTooltip(this.mc.thePlayer, this.mc.gameSettings.advancedItemTooltips); + } + + for (int k = 0; k < list.size(); ++k) + { + if (k == 0) + { + list.set(k, stack.getRarity().rarityColor + (String)list.get(k)); + } + else + { + list.set(k, EnumChatFormatting.GRAY + (String)list.get(k)); + } + } + FontRenderer font = stack.getItem().getFontRenderer(stack); + drawHoveringText(list, x, y, (font == null ? fontRendererObj : font)); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminal.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminal.java new file mode 100644 index 00000000..7a372bb4 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminal.java @@ -0,0 +1,73 @@ +package com.glodblock.github.client.gui; + +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.client.gui.widgets.GuiTabButton; +import appeng.client.render.AppEngRenderItem; +import appeng.container.slot.SlotFake; +import appeng.util.item.AEItemStack; +import com.glodblock.github.client.gui.container.ContainerFluidPatternTerminal; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.inventory.slot.SlotSingleItem; +import com.glodblock.github.util.Ae2Reflect; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; + +public class GuiFluidPatternTerminal extends GuiBaseFluidPatternTerminal { + + private GuiTabButton craftingStatusBtn; + private final AppEngRenderItem stackSizeRenderer = Ae2Reflect.getStackSizeRenderer(this); + + public GuiFluidPatternTerminal(InventoryPlayer inventoryPlayer, ITerminalHost te) { + super(inventoryPlayer, te); + ContainerFluidPatternTerminal container = new ContainerFluidPatternTerminal(inventoryPlayer, te); + container.setGui(this); + this.inventorySlots = container; + Ae2Reflect.setGuiContainer(this, container); + } + + @Override + public void initGui() { + super.initGui(); + craftingStatusBtn = Ae2Reflect.getCraftingStatusButton(this); + } + + @Override + public void func_146977_a( final Slot s ) + { + if (drawSlot0(s)) + super.func_146977_a( s ); + } + + public boolean drawSlot0(Slot slot) { + if (slot instanceof SlotFake && !this.container.isCraftingMode()) { + AEItemStack stack = AEItemStack.create(slot.getStack()); + super.func_146977_a(new SlotSingleItem(slot)); + if (stack == null) return true; + IAEItemStack fake = stack.copy(); + if (fake.getItemStack().getItem() instanceof ItemFluidPacket) { + if (ItemFluidPacket.getFluidStack(stack) != null && ItemFluidPacket.getFluidStack(stack).amount > 0) + fake.setStackSize(ItemFluidPacket.getFluidStack(stack).amount); + } + else + return true; + stackSizeRenderer.setAeStack(fake); + stackSizeRenderer.renderItemOverlayIntoGUI(fontRendererObj, mc.getTextureManager(), stack.getItemStack(), slot.xDisplayPosition, slot.yDisplayPosition); + return false; + } + return true; + } + + @Override + protected void actionPerformed(final GuiButton btn) { + if (btn == craftingStatusBtn) { + InventoryHandler.switchGui(GuiType.FLUID_PAT_TERM_CRAFTING_STATUS); + } else { + super.actionPerformed(btn); + } + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminalCraftingStatus.java b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminalCraftingStatus.java new file mode 100644 index 00000000..3e6e44fb --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/GuiFluidPatternTerminalCraftingStatus.java @@ -0,0 +1,38 @@ +package com.glodblock.github.client.gui; + +import appeng.api.storage.ITerminalHost; +import appeng.client.gui.implementations.GuiCraftingStatus; +import appeng.client.gui.widgets.GuiTabButton; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.Ae2Reflect; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.item.ItemStack; + +public class GuiFluidPatternTerminalCraftingStatus extends GuiCraftingStatus { + + private GuiTabButton originalGuiBtn; + + public GuiFluidPatternTerminalCraftingStatus(InventoryPlayer inventoryPlayer, ITerminalHost te) { + super(inventoryPlayer, te); + } + + @Override + public void initGui() { + Ae2Reflect.rewriteIcon(this, new ItemStack(ItemAndBlockHolder.FLUID_TERMINAL, 1)); + super.initGui(); + originalGuiBtn = Ae2Reflect.getOriginalGuiButton(this); + } + + @Override + protected void actionPerformed(final GuiButton btn) { + if (btn == originalGuiBtn) { + InventoryHandler.switchGui(GuiType.FLUID_PATTERN_TERMINAL); + } else { + super.actionPerformed(btn); + } + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/IFluidSyncContainer.java b/src/main/java/com/glodblock/github/client/gui/IFluidSyncContainer.java new file mode 100644 index 00000000..469d66b7 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/IFluidSyncContainer.java @@ -0,0 +1,9 @@ +package com.glodblock.github.client.gui; + +import appeng.api.storage.data.IAEFluidStack; + +import java.util.Map; + +public interface IFluidSyncContainer { + void receiveFluidSlots( final Map fluids ); +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidConfigurable.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidConfigurable.java new file mode 100644 index 00000000..209572fa --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidConfigurable.java @@ -0,0 +1,382 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.config.Upgrades; +import appeng.api.implementations.IUpgradeableHost; +import appeng.api.storage.data.IAEFluidStack; +import appeng.client.me.InternalSlotME; +import appeng.client.me.SlotME; +import appeng.container.implementations.ContainerUpgradeable; +import appeng.container.slot.*; +import appeng.util.Platform; +import appeng.util.item.AEFluidStack; +import com.glodblock.github.client.gui.IFluidSyncContainer; +import com.glodblock.github.inventory.IAEFluidTank; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.FluidSyncHelper; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public abstract class ContainerFluidConfigurable extends ContainerUpgradeable implements IFluidSyncContainer +{ + private FluidSyncHelper sync = null; + + public ContainerFluidConfigurable(InventoryPlayer ip, IUpgradeableHost te ) + { + super( ip, te ); + } + + public abstract IAEFluidTank getFluidConfigInventory(); + + private FluidSyncHelper getSynchHelper() + { + if( this.sync == null ) + { + this.sync = new FluidSyncHelper( this.getFluidConfigInventory(), 0 ); + } + return this.sync; + } + + @Override + public ItemStack transferStackInSlot(final EntityPlayer p, final int idx ) + { + if( Platform.isClient() ) + { + return null; + } + + boolean hasMETiles = false; + for( final Object is : this.inventorySlots ) + { + if( is instanceof InternalSlotME) + { + hasMETiles = true; + break; + } + } + + if( hasMETiles && Platform.isClient() ) + { + return null; + } + + final AppEngSlot clickSlot = (AppEngSlot) this.inventorySlots.get( idx ); // require AE SLots! + + if( clickSlot instanceof SlotDisabled || clickSlot instanceof SlotInaccessible) + { + return null; + } + if( clickSlot != null && clickSlot.getHasStack() ) + { + ItemStack tis = clickSlot.getStack(); + + if( tis == null ) + { + return null; + } + + final List selectedSlots = new ArrayList(); + + /** + * Gather a list of valid destinations. + */ + if( clickSlot.isPlayerSide() ) + { + tis = this.transferStackToContainer( tis ); + + // target slots in the container... + for( final Object inventorySlot : this.inventorySlots ) + { + final AppEngSlot cs = (AppEngSlot) inventorySlot; + + if( !( cs.isPlayerSide() ) && !( cs instanceof SlotFake) && !( cs instanceof SlotCraftingMatrix) ) + { + if( cs.isItemValid( tis ) ) + { + selectedSlots.add( cs ); + } + } + } + } + else + { + // target slots in the container... + for( final Object inventorySlot : this.inventorySlots ) + { + final AppEngSlot cs = (AppEngSlot) inventorySlot; + + if( ( cs.isPlayerSide() ) && !( cs instanceof SlotFake ) && !( cs instanceof SlotCraftingMatrix ) ) + { + if( cs.isItemValid( tis ) ) + { + selectedSlots.add( cs ); + } + } + } + } + + /** + * Handle Fake Slot Shift clicking. + */ + if( selectedSlots.isEmpty() && clickSlot.isPlayerSide() ) + { + if( tis != null ) + { + // target slots in the container... + for( final Object inventorySlot : this.inventorySlots ) + { + final AppEngSlot cs = (AppEngSlot) inventorySlot; + final ItemStack destination = cs.getStack(); + + if( !( cs.isPlayerSide() ) && cs instanceof SlotFake ) + { + if( Platform.isSameItemPrecise( destination, tis ) ) + { + break; + } + else if( destination == null ) + { + cs.putStack( tis.copy() ); + cs.onSlotChanged(); + this.detectAndSendChanges(); + break; + } + } + } + } + } + + if( tis != null ) + { + // find partials.. + for( final Slot d : selectedSlots ) + { + if( d instanceof SlotDisabled || d instanceof SlotME) + { + continue; + } + + if( d.isItemValid( tis ) ) + { + if( d.getHasStack() ) + { + final ItemStack t = d.getStack(); + + if( Platform.isSameItemPrecise( tis, t ) ) // t.isItemEqual(tis)) + { + int maxSize = t.getMaxStackSize(); + if( maxSize > d.getSlotStackLimit() ) + { + maxSize = d.getSlotStackLimit(); + } + + int placeAble = maxSize - t.stackSize; + + if( tis.stackSize < placeAble ) + { + placeAble = tis.stackSize; + } + + t.stackSize += placeAble; + tis.stackSize -= placeAble; + + if( tis.stackSize <= 0 ) + { + clickSlot.putStack( null ); + d.onSlotChanged(); + + // if ( hasMETiles ) updateClient(); + + this.detectAndSendChanges(); + this.detectAndSendChanges(); + return null; + } + else + { + this.detectAndSendChanges(); + } + } + } + } + } + + // any match.. + for( final Slot d : selectedSlots ) + { + if( d instanceof SlotDisabled || d instanceof SlotME ) + { + continue; + } + + if( d.isItemValid( tis ) ) + { + if( d.getHasStack() ) + { + final ItemStack t = d.getStack(); + + if( Platform.isSameItemPrecise( t, tis ) ) + { + int maxSize = t.getMaxStackSize(); + if( d.getSlotStackLimit() < maxSize ) + { + maxSize = d.getSlotStackLimit(); + } + + int placeAble = maxSize - t.stackSize; + + if( tis.stackSize < placeAble ) + { + placeAble = tis.stackSize; + } + + t.stackSize += placeAble; + tis.stackSize -= placeAble; + + if( tis.stackSize <= 0 ) + { + clickSlot.putStack( null ); + d.onSlotChanged(); + + // if ( worldEntity != null ) + // worldEntity.markDirty(); + // if ( hasMETiles ) updateClient(); + + this.detectAndSendChanges(); + this.detectAndSendChanges(); + return null; + } + else + { + this.detectAndSendChanges(); + } + } + } + else + { + int maxSize = tis.getMaxStackSize(); + if( maxSize > d.getSlotStackLimit() ) + { + maxSize = d.getSlotStackLimit(); + } + + final ItemStack tmp = tis.copy(); + if( tmp.stackSize > maxSize ) + { + tmp.stackSize = maxSize; + } + + tis.stackSize -= tmp.stackSize; + d.putStack( tmp ); + + if( tis.stackSize <= 0 ) + { + clickSlot.putStack( null ); + d.onSlotChanged(); + + // if ( worldEntity != null ) + // worldEntity.markDirty(); + // if ( hasMETiles ) updateClient(); + + this.detectAndSendChanges(); + this.detectAndSendChanges(); + return null; + } + else + { + this.detectAndSendChanges(); + } + } + } + } + } + + clickSlot.putStack( tis != null ? tis.copy() : null ); + } + + this.detectAndSendChanges(); + return null; + } + + protected ItemStack transferStackToContainer(ItemStack input ) + { + FluidStack fs = Util.getFluidFromItem( input ); + if( fs != null ) + { + final IAEFluidTank t = this.getFluidConfigInventory(); + final IAEFluidStack stack = AEFluidStack.create( fs ); + for( int i = 0; i < t.getSlots(); ++i ) + { + if( t.getFluidInSlot( i ) == null && this.isValidForConfig( i, stack ) ) + { + t.setFluidInSlot( i, stack ); + break; + } + } + } + return input; + } + + protected boolean isValidForConfig( int slot, IAEFluidStack fs ) + { + if( this.supportCapacity() ) + { + // assumes 4 slots per upgrade + final int upgrades = Ae2Reflect.getUpgrade(this).getInstalledUpgrades( Upgrades.CAPACITY ); + + if( slot > 0 && upgrades < 1 ) + { + return false; + } + if( slot > 4 && upgrades < 2 ) + { + return false; + } + } + + return true; + } + + @Override + @SuppressWarnings("unchecked") + protected void standardDetectAndSendChanges() + { + if( Platform.isServer() ) + { + this.getSynchHelper().sendDiff( this.crafters ); + + // clear out config items that are no longer valid (eg capacity upgrade removed) + final IAEFluidTank t = this.getFluidConfigInventory(); + for( int i = 0; i < t.getSlots(); ++i ) + { + if( t.getFluidInSlot( i ) != null && !this.isValidForConfig( i, t.getFluidInSlot( i ) ) ) + { + t.setFluidInSlot( i, null ); + } + } + } + super.standardDetectAndSendChanges(); + } + + @Override + public void addCraftingToCrafters( ICrafting listener ) + { + super.addCraftingToCrafters( listener ); + this.getSynchHelper().sendFull( Collections.singleton( listener ) ); + } + + @Override + public void receiveFluidSlots( Map fluids ) + { + this.getSynchHelper().readPacket( fluids ); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidCraftConfirm.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidCraftConfirm.java new file mode 100644 index 00000000..3f26ac62 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidCraftConfirm.java @@ -0,0 +1,436 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.AEApi; +import appeng.api.config.Actionable; +import appeng.api.config.SecurityPermissions; +import appeng.api.networking.IGrid; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.api.networking.crafting.ICraftingGrid; +import appeng.api.networking.crafting.ICraftingJob; +import appeng.api.networking.crafting.ICraftingLink; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.IActionHost; +import appeng.api.networking.security.PlayerSource; +import appeng.api.networking.storage.IStorageGrid; +import appeng.api.storage.IMEInventory; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.container.AEBaseContainer; +import appeng.container.guisync.GuiSync; +import appeng.container.implementations.CraftingCPURecord; +import appeng.core.AELog; +import appeng.util.Platform; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.network.SPacketMEInventoryUpdate; +import com.glodblock.github.util.Ae2Reflect; +import com.google.common.collect.ImmutableSet; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.util.ChatComponentText; +import net.minecraft.world.World; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.Collections; +import java.util.concurrent.Future; + +public class ContainerFluidCraftConfirm extends AEBaseContainer { + + private final ArrayList cpus = new ArrayList(); + private Future job; + private ICraftingJob result; + @GuiSync( 0 ) + public long bytesUsed; + @GuiSync( 1 ) + public long cpuBytesAvail; + @GuiSync( 2 ) + public int cpuCoProcessors; + @GuiSync( 3 ) + public boolean autoStart = false; + @GuiSync( 4 ) + public boolean simulation = true; + @GuiSync( 5 ) + public int selectedCpu = -1; + @GuiSync( 6 ) + public boolean noCPU = true; + @GuiSync( 7 ) + public String myName = ""; + + public ContainerFluidCraftConfirm(final InventoryPlayer ip, final ITerminalHost te ) + { + super( ip, te ); + } + + public void cycleCpu( final boolean next ) + { + if( next ) + { + this.setSelectedCpu( this.getSelectedCpu() + 1 ); + } + else + { + this.setSelectedCpu( this.getSelectedCpu() - 1 ); + } + + if( this.getSelectedCpu() < -1 ) + { + this.setSelectedCpu( this.cpus.size() - 1 ); + } + else if( this.getSelectedCpu() >= this.cpus.size() ) + { + this.setSelectedCpu( -1 ); + } + + if( this.getSelectedCpu() == -1 ) + { + this.setCpuAvailableBytes( 0 ); + this.setCpuCoProcessors( 0 ); + this.setName( "" ); + } + else + { + this.setName(Ae2Reflect.getName(this.cpus.get(this.getSelectedCpu()))); + this.setCpuAvailableBytes(Ae2Reflect.getSize(this.cpus.get(this.getSelectedCpu()))); + this.setCpuCoProcessors(Ae2Reflect.getProcessors(this.cpus.get(this.getSelectedCpu()))); + } + } + + @Override + public void detectAndSendChanges() + { + if( Platform.isClient() ) + { + return; + } + if (getGrid() == null) + return; + + final ICraftingGrid cc = this.getGrid().getCache( ICraftingGrid.class ); + final ImmutableSet cpuSet = cc.getCpus(); + + int matches = 0; + boolean changed = false; + for( final ICraftingCPU c : cpuSet ) + { + boolean found = false; + for( final CraftingCPURecord ccr : this.cpus ) + { + if( Ae2Reflect.getCPU(ccr) == c ) + { + found = true; + } + } + + final boolean matched = this.cpuMatches( c ); + + if( matched ) + { + matches++; + } + + if( found == !matched ) + { + changed = true; + } + } + + if( changed || this.cpus.size() != matches ) + { + this.cpus.clear(); + for( final ICraftingCPU c : cpuSet ) + { + if( this.cpuMatches( c ) ) + { + this.cpus.add( new CraftingCPURecord( c.getAvailableStorage(), c.getCoProcessors(), c ) ); + } + } + + this.sendCPUs(); + } + + this.setNoCPU( this.cpus.isEmpty() ); + + super.detectAndSendChanges(); + + if( this.getJob() != null && this.getJob().isDone() ) + { + try + { + this.result = this.getJob().get(); + + if( !this.result.isSimulation() ) + { + this.setSimulation( false ); + if( this.isAutoStart() ) + { + this.startJob(); + return; + } + } + else + { + this.setSimulation( true ); + } + + final SPacketMEInventoryUpdate a = new SPacketMEInventoryUpdate( (byte) 0 ); + final SPacketMEInventoryUpdate b = new SPacketMEInventoryUpdate( (byte) 1 ); + final SPacketMEInventoryUpdate c = this.result.isSimulation() ? new SPacketMEInventoryUpdate( (byte) 2 ) : null; + + final IItemList plan = AEApi.instance().storage().createItemList(); + this.result.populatePlan( plan ); + + this.setUsedBytes( this.result.getByteTotal() ); + + for( final IAEItemStack out : plan ) + { + + IAEItemStack o = out.copy(); + o.reset(); + o.setStackSize( out.getStackSize() ); + + final IAEItemStack p = out.copy(); + p.reset(); + p.setStackSize( out.getCountRequestable() ); + + final IStorageGrid sg = this.getGrid().getCache( IStorageGrid.class ); + final IMEInventory items = sg.getItemInventory(); + + IAEItemStack m = null; + if( c != null && this.result.isSimulation() ) + { + m = o.copy(); + o = items.extractItems( o, Actionable.SIMULATE, this.getActionSource() ); + + if( o == null ) + { + o = m.copy(); + o.setStackSize( 0 ); + } + + m.setStackSize( m.getStackSize() - o.getStackSize() ); + } + + if( o.getStackSize() > 0 ) + { + a.appendItem( o ); + } + + if( p.getStackSize() > 0 ) + { + b.appendItem( p ); + } + + if( c != null && m != null && m.getStackSize() > 0 ) + { + c.appendItem( m ); + } + } + + for( final Object g : this.crafters ) + { + if( g instanceof EntityPlayer) + { + FluidCraft.proxy.netHandler.sendTo( a, (EntityPlayerMP) g ); + FluidCraft.proxy.netHandler.sendTo( b, (EntityPlayerMP) g ); + if( c != null ) + { + FluidCraft.proxy.netHandler.sendTo( c, (EntityPlayerMP) g ); + } + } + } + } + catch( final Throwable e ) + { + this.getPlayerInv().player.addChatMessage( new ChatComponentText( "Error: " + e.toString() ) ); + AELog.debug( e ); + this.setValidContainer( false ); + this.result = null; + } + + this.setJob( null ); + } + this.verifyPermissions( SecurityPermissions.CRAFT, false ); + } + + private IGrid getGrid() + { + final IActionHost h = ( (IActionHost) this.getTarget() ); + if (h == null || h.getActionableNode() == null) + return null; + return h.getActionableNode().getGrid(); + } + + private boolean cpuMatches( final ICraftingCPU c ) + { + return c.getAvailableStorage() >= this.getUsedBytes() && !c.isBusy(); + } + + private void sendCPUs() + { + Collections.sort( this.cpus ); + + if( this.getSelectedCpu() >= this.cpus.size() ) + { + this.setSelectedCpu( -1 ); + this.setCpuAvailableBytes( 0 ); + this.setCpuCoProcessors( 0 ); + this.setName( "" ); + } + else if( this.getSelectedCpu() != -1 ) + { + this.setName(Ae2Reflect.getName(this.cpus.get(this.getSelectedCpu()))); + this.setCpuAvailableBytes(Ae2Reflect.getSize(this.cpus.get(this.getSelectedCpu()))); + this.setCpuCoProcessors(Ae2Reflect.getProcessors(this.cpus.get(this.getSelectedCpu()))); + } + } + + public void startJob() + { + GuiType originalGui = null; + + final IActionHost ah = this.getActionHost(); + if( ah instanceof PartFluidPatternTerminal) + { + originalGui = GuiType.FLUID_PATTERN_TERMINAL; + } + + if( this.result != null && !this.isSimulation() && getGrid() != null) + { + final ICraftingGrid cc = this.getGrid().getCache( ICraftingGrid.class ); + final ICraftingLink g = cc.submitJob( this.result, null, this.getSelectedCpu() == -1 ? null : Ae2Reflect.getCPU(this.cpus.get(this.getSelectedCpu())), true, this.getActionSrc() ); + this.setAutoStart( false ); + if( g != null && originalGui != null && this.getOpenContext() != null ) + { + InventoryHandler.switchGui(originalGui); + } + } + } + + private BaseActionSource getActionSrc() + { + return new PlayerSource( this.getPlayerInv().player, (IActionHost) this.getTarget() ); + } + + @Override + public void removeCraftingFromCrafters( final ICrafting c ) + { + super.removeCraftingFromCrafters( c ); + if( this.getJob() != null ) + { + this.getJob().cancel( true ); + this.setJob( null ); + } + } + + @Override + public void onContainerClosed( final EntityPlayer par1EntityPlayer ) + { + super.onContainerClosed( par1EntityPlayer ); + if( this.getJob() != null ) + { + this.getJob().cancel( true ); + this.setJob( null ); + } + } + + public World getWorld() + { + return this.getPlayerInv().player.worldObj; + } + + public boolean isAutoStart() + { + return this.autoStart; + } + + public void setAutoStart( final boolean autoStart ) + { + this.autoStart = autoStart; + } + + public long getUsedBytes() + { + return this.bytesUsed; + } + + private void setUsedBytes( final long bytesUsed ) + { + this.bytesUsed = bytesUsed; + } + + public long getCpuAvailableBytes() + { + return this.cpuBytesAvail; + } + + private void setCpuAvailableBytes( final long cpuBytesAvail ) + { + this.cpuBytesAvail = cpuBytesAvail; + } + + public int getCpuCoProcessors() + { + return this.cpuCoProcessors; + } + + private void setCpuCoProcessors( final int cpuCoProcessors ) + { + this.cpuCoProcessors = cpuCoProcessors; + } + + public int getSelectedCpu() + { + return this.selectedCpu; + } + + private void setSelectedCpu( final int selectedCpu ) + { + this.selectedCpu = selectedCpu; + } + + public String getName() + { + return this.myName; + } + + private void setName( @Nonnull final String myName ) + { + this.myName = myName; + } + + public boolean hasNoCPU() + { + return this.noCPU; + } + + private void setNoCPU( final boolean noCPU ) + { + this.noCPU = noCPU; + } + + public boolean isSimulation() + { + return this.simulation; + } + + private void setSimulation( final boolean simulation ) + { + this.simulation = simulation; + } + + private Future getJob() + { + return this.job; + } + + public void setJob( final Future job ) + { + this.job = job; + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPacketDecoder.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPacketDecoder.java new file mode 100644 index 00000000..94adedee --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPacketDecoder.java @@ -0,0 +1,16 @@ +package com.glodblock.github.client.gui.container; + +import appeng.container.AEBaseContainer; +import appeng.container.slot.SlotNormal; +import com.glodblock.github.common.tile.TileFluidPacketDecoder; +import net.minecraft.entity.player.InventoryPlayer; + +public class ContainerFluidPacketDecoder extends AEBaseContainer { + + public ContainerFluidPacketDecoder(InventoryPlayer ipl, TileFluidPacketDecoder tile) { + super(ipl, tile); + addSlotToContainer(new SlotNormal(tile.getInventory(), 0, 80, 35)); + bindPlayerInventory(ipl, 0, 84); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternEncoder.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternEncoder.java new file mode 100644 index 00000000..fe2d6dcf --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternEncoder.java @@ -0,0 +1,226 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.AEApi; +import appeng.api.storage.data.IAEItemStack; +import appeng.container.AEBaseContainer; +import appeng.container.slot.SlotFake; +import appeng.container.slot.SlotRestrictedInput; +import appeng.helpers.InventoryAction; +import appeng.util.item.AEItemStack; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidEncodedPattern; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.tile.TileFluidPatternEncoder; +import com.glodblock.github.inventory.AeItemStackHandler; +import com.glodblock.github.inventory.AeStackInventory; +import com.glodblock.github.inventory.PatternConsumer; +import com.glodblock.github.inventory.slot.SlotFluid; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.FluidPatternDetails; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class ContainerFluidPatternEncoder extends AEBaseContainer implements PatternConsumer { + + private final TileFluidPatternEncoder tile; + + public ContainerFluidPatternEncoder(InventoryPlayer ipl, TileFluidPatternEncoder tile) { + super(ipl, tile); + this.tile = tile; + AeItemStackHandler crafting = new AeItemStackHandler(tile.getCraftingSlots()); + AeItemStackHandler output = new AeItemStackHandler(tile.getOutputSlots()); + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + addSlotToContainer(new SlotFluidConvertingFake(crafting, y * 3 + x, 23 + x * 18, 17 + y * 18)); + } + addSlotToContainer(new SlotFluidConvertingFake(output, y, 113, 17 + y * 18)); + } + addSlotToContainer(new SlotRestrictedInput( + SlotRestrictedInput.PlacableItemType.BLANK_PATTERN, tile.getInventory(), 0, 138, 20, ipl)); + addSlotToContainer(new SlotRestrictedInput( + SlotRestrictedInput.PlacableItemType.ENCODED_PATTERN, tile.getInventory(), 1, 138, 50, ipl)); + bindPlayerInventory(ipl, 0, 84); + } + + public TileFluidPatternEncoder getTile() { + return tile; + } + + public boolean canEncodePattern() { + if (isNotPattern(tile.getInventory().getStackInSlot(0)) && isNotPattern(tile.getInventory().getStackInSlot(1))) { + return false; + } + find_input: + { + for (IAEItemStack stack : tile.getCraftingSlots()) { + if (stack != null && stack.getStackSize() > 0) { + break find_input; + } + } + return false; + } + for (IAEItemStack stack : tile.getOutputSlots()) { + if (stack != null && stack.getStackSize() > 0) { + return true; + } + } + return false; + } + + private static boolean isNotPattern(ItemStack stack) { + return stack == null || !(AEApi.instance().definitions().materials().blankPattern().isSameAs(stack) + || (stack.getItem() instanceof ItemFluidEncodedPattern)); + } + + public void encodePattern() { + if (canEncodePattern()) { + // if there is an encoded pattern, overwrite it; otherwise, consume a blank + if (tile.getInventory().getStackInSlot(1) == null) { + tile.getInventory().decrStackSize(0, 1); // this better work + } + ItemStack patternStack = new ItemStack(ItemAndBlockHolder.PATTERN); + FluidPatternDetails pattern = new FluidPatternDetails(patternStack); + pattern.setInputs(collectAeInventory(tile.getCraftingSlots())); + pattern.setOutputs(collectAeInventory(tile.getOutputSlots())); + tile.getInventory().setInventorySlotContents(1, pattern.writeToStack()); + } + } + + private static IAEItemStack[] collectAeInventory(AeStackInventory inv) { + // see note at top of DensePatternDetails + List acc = new ArrayList<>(); + for (IAEItemStack stack : inv) { + if (stack != null) { + if (stack.getItem() instanceof ItemFluidPacket) { + IAEItemStack dropStack = ItemFluidDrop.newAeStack(ItemFluidPacket.getFluidStack(stack)); + if (dropStack != null) { + acc.add(dropStack); + continue; + } + } + acc.add(stack); + } + } + return acc.toArray(new IAEItemStack[0]); + } + + // adapted from ae2's AEBaseContainer#doAction + @Override + public void doAction(EntityPlayerMP player, InventoryAction action, int slotId, long id) { + Slot slot = getSlot(slotId); + if (slot instanceof SlotFluidConvertingFake) { + final ItemStack stack = player.inventory.getItemStack(); + switch (action) { + case PICKUP_OR_SET_DOWN: + if (stack == null) { + slot.putStack(null); + } else { + ((SlotFluidConvertingFake)slot).putConvertedStack(stack.copy()); + } + break; + case PLACE_SINGLE: + if (stack != null) { + ((SlotFluidConvertingFake)slot).putConvertedStack(Objects.requireNonNull(Util.copyStackWithSize(stack, 1))); + } + break; + case SPLIT_OR_PLACE_SINGLE: + ItemStack inSlot = slot.getStack(); + if (inSlot != null) { + if (stack == null) { + slot.putStack(Util.copyStackWithSize(inSlot, Math.max(1, inSlot.stackSize - 1))); + } else if (stack.isItemEqual(inSlot)) { + slot.putStack(Util.copyStackWithSize(inSlot, + Math.min(inSlot.getMaxStackSize(), inSlot.stackSize + 1))); + } else { + ((SlotFluidConvertingFake)slot).putConvertedStack(Objects.requireNonNull(Util.copyStackWithSize(stack, 1))); + } + } else if (stack != null) { + ((SlotFluidConvertingFake)slot).putConvertedStack(Objects.requireNonNull(Util.copyStackWithSize(stack, 1))); + } + break; + } + } else { + super.doAction(player, action, slotId, id); + } + } + + @Override + public void acceptPattern(IAEItemStack[] inputs, IAEItemStack[] outputs) { + copyStacks(inputs, tile.getCraftingSlots()); + copyStacks(outputs, tile.getOutputSlots()); + } + + private static void copyStacks(IAEItemStack[] src, AeStackInventory dest) { + int bound = Math.min(src.length, dest.getSlotCount()); + for (int i = 0; i < bound; i++) { + dest.setStack(i, src[i]); + } + } + + private static class SlotFluidConvertingFake extends SlotFake implements SlotFluid { + + private final AeStackInventory inv; + + public SlotFluidConvertingFake(AeItemStackHandler inv, int idx, int x, int y) { + super(inv, idx, x, y); + this.inv = inv.getAeInventory(); + } + + @Override + public void putStack(ItemStack stack) { + inv.setStack(getSlotIndex(), AEItemStack.create(stack)); + } + + @Override + public void setAeStack(@Nullable IAEItemStack stack, boolean sync) { + inv.setStack(getSlotIndex(), stack); + } + + public void putConvertedStack(ItemStack stack) { + if (stack == null) { + setAeStack(null, false); + return; + } else if (stack.getItem() instanceof IFluidContainerItem) { + FluidStack fluid = ((IFluidContainerItem) stack.getItem()).getFluid(stack); + if (fluid != null) { + fluid.amount *= stack.stackSize; + IAEItemStack aeStack = ItemFluidPacket.newAeStack(fluid); + if (aeStack != null) { + setAeStack(aeStack, false); + return; + } + } + } else if (FluidContainerRegistry.isContainer(stack)) { + FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(stack); + if (fluid != null) { + fluid.amount *= stack.stackSize; + IAEItemStack aeStack = ItemFluidPacket.newAeStack(fluid); + if (aeStack != null) { + setAeStack(aeStack, false); + return; + } + } + } + putStack(stack); + } + + @Nullable + @Override + public IAEItemStack getAeStack() { + return inv.getStack(getSlotIndex()); + } + + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternTerminal.java b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternTerminal.java new file mode 100644 index 00000000..936c8309 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/ContainerFluidPatternTerminal.java @@ -0,0 +1,185 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.AEApi; +import appeng.api.definitions.IDefinitions; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.container.slot.SlotFakeCraftingMatrix; +import appeng.container.slot.SlotPatternOutputs; +import appeng.helpers.InventoryAction; +import appeng.util.item.AEItemStack; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidEncodedPattern; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.inventory.PatternConsumer; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.FluidPatternDetails; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +import java.util.ArrayList; +import java.util.List; + +public class ContainerFluidPatternTerminal extends FCBasePartContainer implements PatternConsumer { + + public ContainerFluidPatternTerminal(InventoryPlayer ip, ITerminalHost monitorable) { + super(ip, monitorable); + } + + @Override + public void encode() { + if (!checkHasFluidPattern()) { + super.encode(); + return; + } + ItemStack stack = this.patternSlotOUT.getStack(); + if (stack == null) { + stack = this.patternSlotIN.getStack(); + if (!isPattern(stack)) { + return; + } + if (stack.stackSize == 1) { + this.patternSlotIN.putStack(null); + } else { + stack.stackSize --; + } + encodeFluidPattern(); + } else if (isPattern(stack)) { + encodeFluidPattern(); + } + } + + private static boolean isPattern(final ItemStack output) { + if (output == null) { + return false; + } + if (output.getItem() instanceof ItemFluidEncodedPattern) { + return true; + } + final IDefinitions defs = AEApi.instance().definitions(); + return defs.items().encodedPattern().isSameAs(output) || defs.materials().blankPattern().isSameAs(output); + } + + private boolean checkHasFluidPattern() { + if (this.craftingMode) { + return false; + } + boolean hasFluid = false, search = false; + for (Slot craftingSlot : this.craftingSlots) { + final ItemStack crafting = craftingSlot.getStack(); + if (crafting == null) { + continue; + } + search = true; + if (crafting.getItem() instanceof ItemFluidPacket) { + hasFluid = true; + break; + } + } + if (!search) { // search=false -> inputs were empty + return false; + } + // `search` should be true at this point + for (Slot outputSlot : this.outputSlots) { + final ItemStack out = outputSlot.getStack(); + if (out == null) { + continue; + } + search = false; + if (hasFluid) { + break; + } else if (out.getItem() instanceof ItemFluidPacket) { + hasFluid = true; + break; + } + } + return hasFluid && !search; // search=true -> outputs were empty + } + + private void encodeFluidPattern() { + ItemStack patternStack = new ItemStack(ItemAndBlockHolder.PATTERN); + FluidPatternDetails pattern = new FluidPatternDetails(patternStack); + pattern.setInputs(collectInventory(craftingSlots)); + pattern.setOutputs(collectInventory(outputSlots)); + patternSlotOUT.putStack(pattern.writeToStack()); + } + + private static IAEItemStack[] collectInventory(Slot[] slots) { + // see note at top of FluidPatternDetails + List acc = new ArrayList<>(); + for (Slot slot : slots) { + ItemStack stack = slot.getStack(); + if (stack == null) { + continue; + } + if (stack.getItem() instanceof ItemFluidPacket) { + IAEItemStack dropStack = ItemFluidDrop.newAeStack(ItemFluidPacket.getFluidStack(stack)); + if (dropStack != null) { + acc.add(dropStack); + continue; + } + } + IAEItemStack aeStack = AEItemStack.create(stack); + acc.add(aeStack); + } + return acc.toArray(new IAEItemStack[0]); + } + + @Override + public void acceptPattern(IAEItemStack[] inputs, IAEItemStack[] outputs) { + if (getPatternTerminal() != null) { + getPatternTerminal().onChangeCrafting(inputs, outputs); + } + } + + @Override + public void doAction(EntityPlayerMP player, InventoryAction action, int slotId, long id) { + if (this.isCraftingMode()) { + super.doAction(player, action, slotId, id); + return; + } + if (slotId < 0 || slotId >= this.inventorySlots.size()) { + super.doAction(player, action, slotId, id); + return; + } + Slot slot = getSlot(slotId); + ItemStack stack = player.inventory.getItemStack(); + if ((slot instanceof SlotFakeCraftingMatrix || slot instanceof SlotPatternOutputs) && stack != null + && (stack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(stack))) { + FluidStack fluid = null; + switch (action) { + case PICKUP_OR_SET_DOWN: + fluid = Util.getFluidFromItem(stack); + slot.putStack(ItemFluidPacket.newStack(fluid)); + break; + case SPLIT_OR_PLACE_SINGLE: + fluid = Util.getFluidFromItem(Util.copyStackWithSize(stack, 1)); + FluidStack origin = ItemFluidPacket.getFluidStack(slot.getStack()); + if (fluid != null && fluid.equals(origin)) { + fluid.amount += origin.amount; + if (fluid.amount <= 0) fluid = null; + } + slot.putStack(ItemFluidPacket.newStack(fluid)); + break; + } + if (fluid == null) { + super.doAction(player, action, slotId, id); + return; + } + return; + } + if (action == InventoryAction.SPLIT_OR_PLACE_SINGLE) { + if (stack == null && slot.getStack() != null && slot.getStack().getItem() instanceof ItemFluidPacket) { + return; + } + } + super.doAction(player, action, slotId, id); + } + +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/FCBaseMonitorContain.java b/src/main/java/com/glodblock/github/client/gui/container/FCBaseMonitorContain.java new file mode 100644 index 00000000..27212654 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/FCBaseMonitorContain.java @@ -0,0 +1,408 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.AEApi; +import appeng.api.config.*; +import appeng.api.implementations.guiobjects.IPortableCell; +import appeng.api.implementations.tiles.IMEChest; +import appeng.api.implementations.tiles.IViewCellStorage; +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.BaseActionSource; +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.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.api.util.IConfigManager; +import appeng.api.util.IConfigurableObject; +import appeng.container.AEBaseContainer; +import appeng.container.guisync.GuiSync; +import appeng.container.slot.SlotRestrictedInput; +import appeng.core.AELog; +import appeng.core.sync.network.NetworkHandler; +import appeng.core.sync.packets.PacketValueConfig; +import appeng.helpers.WirelessTerminalGuiObject; +import appeng.me.helpers.ChannelPowerSrc; +import appeng.util.ConfigManager; +import appeng.util.IConfigManagerHost; +import appeng.util.Platform; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.network.SPacketMEInventoryUpdate; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.ICrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.nio.BufferOverflowException; + +public class FCBaseMonitorContain extends AEBaseContainer implements IConfigManagerHost, IConfigurableObject, IMEMonitorHandlerReceiver +{ + + private final SlotRestrictedInput[] cellView = new SlotRestrictedInput[5]; + private final IMEMonitor monitor; + private final IItemList items = AEApi.instance().storage().createItemList(); + private final IConfigManager clientCM; + private final ITerminalHost host; + @GuiSync( 99 ) + public boolean canAccessViewCells = false; + @GuiSync( 98 ) + public boolean hasPower = false; + private IConfigManagerHost gui; + private IConfigManager serverCM; + private IGridNode networkNode; + + public FCBaseMonitorContain(final InventoryPlayer ip, final ITerminalHost monitorable ) + { + this( ip, monitorable, true ); + } + + protected FCBaseMonitorContain( final InventoryPlayer ip, final ITerminalHost monitorable, final boolean bindInventory ) + { + super( ip, monitorable instanceof TileEntity ? (TileEntity) monitorable : null, monitorable instanceof IPart ? (IPart) monitorable : null ); + + this.host = monitorable; + this.clientCM = new ConfigManager( this ); + + this.clientCM.registerSetting( Settings.SORT_BY, SortOrder.NAME ); + this.clientCM.registerSetting( Settings.VIEW_MODE, ViewItems.ALL ); + this.clientCM.registerSetting( Settings.SORT_DIRECTION, SortDir.ASCENDING ); + + if( Platform.isServer() ) + { + this.serverCM = monitorable.getConfigManager(); + + this.monitor = monitorable.getItemInventory(); + if( this.monitor != null ) + { + this.monitor.addListener( this, null ); + + this.setCellInventory( this.monitor ); + + if( monitorable instanceof IPortableCell) + { + this.setPowerSource( (IEnergySource) monitorable ); + } + else if( monitorable instanceof IMEChest) + { + this.setPowerSource( (IEnergySource) monitorable ); + } + else if( monitorable instanceof IGridHost) + { + final IGridNode node = ( (IGridHost) monitorable ).getGridNode( ForgeDirection.UNKNOWN ); + if( node != null ) + { + this.networkNode = node; + final IGrid g = node.getGrid(); + if( g != null ) + { + this.setPowerSource( new ChannelPowerSrc( this.networkNode, (IEnergySource) g.getCache( IEnergyGrid.class ) ) ); + } + } + } + } + else + { + this.setValidContainer( false ); + } + } + else + { + this.monitor = null; + } + + this.canAccessViewCells = false; + if( monitorable instanceof IViewCellStorage) + { + for( int y = 0; y < 5; y++ ) + { + this.cellView[y] = new SlotRestrictedInput( SlotRestrictedInput.PlacableItemType.VIEW_CELL, ( (IViewCellStorage) monitorable ).getViewCellStorage(), y, 206, y * 18 + 8, this.getInventoryPlayer() ); + this.cellView[y].setAllowEdit( this.canAccessViewCells ); + this.addSlotToContainer( this.cellView[y] ); + } + } + + if( bindInventory ) + { + this.bindPlayerInventory( ip, 0, 0 ); + } + } + + public IGridNode getNetworkNode() + { + return this.networkNode; + } + + @Override + public void detectAndSendChanges() + { + if( Platform.isServer() ) + { + if( this.monitor != this.host.getItemInventory() ) + { + 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 Object crafter : this.crafters ) + { + try + { + NetworkHandler.instance.sendTo( new PacketValueConfig( set.name(), sideLocal.name() ), (EntityPlayerMP) crafter ); + } + catch( final IOException e ) + { + AELog.debug( e ); + } + } + } + } + + if( !this.items.isEmpty() ) + { + final IItemList monitorCache = this.monitor.getStorageList(); + + final SPacketMEInventoryUpdate piu = new SPacketMEInventoryUpdate(); + + for( final IAEItemStack is : this.items ) + { + final IAEItemStack send = monitorCache.findPrecise( is ); + if( send == null ) + { + is.setStackSize( 0 ); + piu.appendItem( is ); + } + else + { + piu.appendItem( send ); + } + } + + if( !piu.isEmpty() ) + { + this.items.resetStatus(); + + for( final Object c : this.crafters ) + { + if( c instanceof EntityPlayer) + { + FluidCraft.proxy.netHandler.sendTo(piu, (EntityPlayerMP) c); + } + } + } + } + + this.updatePowerStatus(); + + final boolean oldAccessible = this.canAccessViewCells; + this.canAccessViewCells = + this.host instanceof WirelessTerminalGuiObject + || this.hasAccess( SecurityPermissions.BUILD, false ); + if( this.canAccessViewCells != oldAccessible ) + { + for( int y = 0; y < 5; y++ ) + { + if( this.cellView[y] != null ) + { + this.cellView[y].setAllowEdit( this.canAccessViewCells ); + } + } + } + + super.detectAndSendChanges(); + } + } + + 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 Throwable t ) + { + // :P + } + } + + @Override + public void onUpdate( final String field, final Object oldValue, final Object newValue ) + { + if( field.equals( "canAccessViewCells" ) ) + { + for( int y = 0; y < 5; y++ ) + { + if( this.cellView[y] != null ) + { + this.cellView[y].setAllowEdit( this.canAccessViewCells ); + } + } + } + + super.onUpdate( field, oldValue, newValue ); + } + + @Override + public void addCraftingToCrafters( final ICrafting c ) + { + super.addCraftingToCrafters( c ); + this.queueInventory( c ); + } + + private void queueInventory( final ICrafting c ) + { + if( Platform.isServer() && c instanceof EntityPlayer && this.monitor != null ) + { + SPacketMEInventoryUpdate piu = new SPacketMEInventoryUpdate(); + final IItemList monitorCache = this.monitor.getStorageList(); + + for( final IAEItemStack send : monitorCache ) + { + try + { + piu.appendItem( send ); + } + catch( final BufferOverflowException boe ) + { + FluidCraft.proxy.netHandler.sendTo( piu, (EntityPlayerMP) c ); + + piu = new SPacketMEInventoryUpdate(); + piu.appendItem( send ); + } + } + FluidCraft.proxy.netHandler.sendTo( piu, (EntityPlayerMP) c ); + } + } + + @Override + public void removeCraftingFromCrafters( final ICrafting c ) + { + super.removeCraftingFromCrafters( c ); + + if( this.crafters.isEmpty() && this.monitor != null ) + { + this.monitor.removeListener( this ); + } + } + + @Override + public void onContainerClosed( final EntityPlayer player ) + { + super.onContainerClosed( player ); + if( this.monitor != null ) + { + this.monitor.removeListener( this ); + } + } + + @Override + public boolean isValid( final Object verificationToken ) + { + return true; + } + + @Override + public void postChange(final IBaseMonitor monitor, final Iterable change, final BaseActionSource source ) + { + for( final IAEItemStack is : change ) + { + this.items.add( is ); + } + } + + @Override + public void onListUpdate() + { + for( final Object c : this.crafters ) + { + if( c instanceof ICrafting ) + { + final ICrafting cr = (ICrafting) c; + this.queueInventory( cr ); + } + } + } + + @Override + public void updateSetting( final IConfigManager manager, final Enum settingName, final Enum newValue ) + { + if( this.getGui() != null ) + { + this.getGui().updateSetting( manager, settingName, newValue ); + } + } + + @Override + public IConfigManager getConfigManager() + { + if( Platform.isServer() ) + { + return this.serverCM; + } + return this.clientCM; + } + + public ItemStack[] getViewCells() + { + final ItemStack[] list = new ItemStack[this.cellView.length]; + + for( int x = 0; x < this.cellView.length; x++ ) + { + list[x] = this.cellView[x].getStack(); + } + + return list; + } + + public SlotRestrictedInput getCellViewSlot( final int index ) + { + return this.cellView[index]; + } + + public boolean isPowered() + { + return this.hasPower; + } + + private void setPowered( final boolean isPowered ) + { + this.hasPower = isPowered; + } + + private IConfigManagerHost getGui() + { + return this.gui; + } + + public void setGui( @Nonnull final IConfigManagerHost gui ) + { + this.gui = gui; + } +} diff --git a/src/main/java/com/glodblock/github/client/gui/container/FCBasePartContainer.java b/src/main/java/com/glodblock/github/client/gui/container/FCBasePartContainer.java new file mode 100644 index 00000000..320088fd --- /dev/null +++ b/src/main/java/com/glodblock/github/client/gui/container/FCBasePartContainer.java @@ -0,0 +1,625 @@ +package com.glodblock.github.client.gui.container; + +import appeng.api.AEApi; +import appeng.api.config.Actionable; +import appeng.api.definitions.IDefinitions; +import appeng.api.networking.security.MachineSource; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IItemList; +import appeng.container.ContainerNull; +import appeng.container.guisync.GuiSync; +import appeng.container.slot.*; +import appeng.core.sync.packets.PacketPatternSlot; +import appeng.helpers.IContainerCraftingPacket; +import appeng.items.storage.ItemViewCell; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.inventory.IAEAppEngInventory; +import appeng.tile.inventory.InvOperation; +import appeng.util.InventoryAdaptor; +import appeng.util.Platform; +import appeng.util.inv.AdaptorPlayerHand; +import appeng.util.item.AEItemStack; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.inventory.*; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.CraftingManager; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.nbt.NBTBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +public class FCBasePartContainer extends FCBaseMonitorContain implements IAEAppEngInventory, IOptionalSlotHost, IContainerCraftingPacket { + + private final PartFluidPatternTerminal patternTerminal; + private final AppEngInternalInventory cOut = new AppEngInternalInventory( null, 1 ); + private final IInventory crafting; + protected final SlotFakeCraftingMatrix[] craftingSlots = new SlotFakeCraftingMatrix[9]; + protected final OptionalSlotFake[] outputSlots = new OptionalSlotFake[3]; + protected final SlotPatternTerm craftSlot; + protected final SlotRestrictedInput patternSlotIN; + protected final SlotRestrictedInput patternSlotOUT; + @GuiSync( 97 ) + public boolean craftingMode = true; + @GuiSync( 96 ) + public boolean substitute = false; + + public FCBasePartContainer(final InventoryPlayer ip, final ITerminalHost monitorable ) + { + super( ip, monitorable, false ); + this.patternTerminal = (PartFluidPatternTerminal) monitorable; + + final IInventory patternInv = this.getPatternTerminal().getInventoryByName( "pattern" ); + final IInventory output = this.getPatternTerminal().getInventoryByName( "output" ); + + this.crafting = this.getPatternTerminal().getInventoryByName( "crafting" ); + + for( int y = 0; y < 3; y++ ) + { + for( int x = 0; x < 3; x++ ) + { + this.addSlotToContainer( this.craftingSlots[x + y * 3] = new SlotFakeCraftingMatrix( this.crafting, x + y * 3, 18 + x * 18, -76 + y * 18 ) ); + } + } + + this.addSlotToContainer( this.craftSlot = new SlotPatternTerm( ip.player, this.getActionSource(), this.getPowerSource(), monitorable, this.crafting, patternInv, this.cOut, 110, -76 + 18, this, 2, this ) ); + this.craftSlot.setIIcon( -1 ); + + for( int y = 0; y < 3; y++ ) + { + this.addSlotToContainer( this.outputSlots[y] = new SlotPatternOutputs( output, this, y, 110, -76 + y * 18, 0, 0, 1 ) ); + this.outputSlots[y].setRenderDisabled( false ); + } + + this.addSlotToContainer( this.patternSlotIN = new SlotRestrictedInput( SlotRestrictedInput.PlacableItemType.BLANK_PATTERN, patternInv, 0, 147, -72 - 9, this.getInventoryPlayer() ) ); + this.addSlotToContainer( this.patternSlotOUT = new SlotRestrictedInput( SlotRestrictedInput.PlacableItemType.ENCODED_PATTERN, patternInv, 1, 147, -72 + 34, this.getInventoryPlayer() ) ); + + this.patternSlotOUT.setStackLimit( 1 ); + + this.bindPlayerInventory( ip, 0, 0 ); + this.updateOrderOfOutputSlots(); + } + + private void updateOrderOfOutputSlots() + { + if( !this.isCraftingMode() ) + { + this.craftSlot.xDisplayPosition = -9000; + + for( int y = 0; y < 3; y++ ) + { + this.outputSlots[y].xDisplayPosition = this.outputSlots[y].getX(); + } + } + else + { + this.craftSlot.xDisplayPosition = this.craftSlot.getX(); + + for( int y = 0; y < 3; y++ ) + { + this.outputSlots[y].xDisplayPosition = -9000; + } + } + } + + @Override + public void putStackInSlot( final int par1, final ItemStack par2ItemStack ) + { + super.putStackInSlot( par1, par2ItemStack ); + this.getAndUpdateOutput(); + } + + @Override + public void putStacksInSlots( final ItemStack[] par1ArrayOfItemStack ) + { + super.putStacksInSlots( par1ArrayOfItemStack ); + this.getAndUpdateOutput(); + } + + private ItemStack getAndUpdateOutput() + { + final InventoryCrafting ic = new InventoryCrafting( this, 3, 3 ); + + for( int x = 0; x < ic.getSizeInventory(); x++ ) + { + ic.setInventorySlotContents( x, this.crafting.getStackInSlot( x ) ); + } + + final ItemStack is = CraftingManager.getInstance().findMatchingRecipe( ic, this.getPlayerInv().player.worldObj ); + this.cOut.setInventorySlotContents( 0, is ); + return is; + } + + @Override + public void saveChanges() + { + + } + + @Override + public void onChangeInventory(final IInventory inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack ) + { + + } + + public void encodeAndMoveToInventory() + { + encode(); + ItemStack output = this.patternSlotOUT.getStack(); + if ( output != null ) + { + if (!getPlayerInv().addItemStackToInventory( output )){ + getPlayerInv().player.entityDropItem(output, 0); + } + this.patternSlotOUT.putStack( null ); + } + } + + public void encode() + { + ItemStack output = this.patternSlotOUT.getStack(); + + final ItemStack[] in = this.getInputs(); + final ItemStack[] out = this.getOutputs(); + + // if there is no input, this would be silly. + if( in == null || out == null ) + { + return; + } + + // first check the output slots, should either be null, or a pattern + if( output != null && !this.isPattern( output ) ) + { + return; + }// if nothing is there we should snag a new pattern. + else if( output == null ) + { + output = this.patternSlotIN.getStack(); + if (!this.isPattern( output )) + { + return; // no blanks. + } + + // remove one, and clear the input slot. + output.stackSize--; + if( output.stackSize == 0 ) + { + this.patternSlotIN.putStack( null ); + } + + // add a new encoded pattern. + for( final ItemStack encodedPatternStack : AEApi.instance().definitions().items().encodedPattern().maybeStack( 1 ).asSet() ) + { + output = encodedPatternStack; + this.patternSlotOUT.putStack( output ); + } + } + + // encode the slot. + final NBTTagCompound encodedValue = new NBTTagCompound(); + + final NBTTagList tagIn = new NBTTagList(); + final NBTTagList tagOut = new NBTTagList(); + + for( final ItemStack i : in ) + { + tagIn.appendTag( this.createItemTag( i ) ); + } + + for( final ItemStack i : out ) + { + tagOut.appendTag( this.createItemTag( i ) ); + } + + encodedValue.setTag( "in", tagIn ); + encodedValue.setTag( "out", tagOut ); + encodedValue.setBoolean( "crafting", this.isCraftingMode() ); + encodedValue.setBoolean( "substitute", this.isSubstitute() ); + + output.setTagCompound( encodedValue ); + } + + private ItemStack[] getInputs() + { + final ItemStack[] input = new ItemStack[9]; + boolean hasValue = false; + + for( int x = 0; x < this.craftingSlots.length; x++ ) + { + input[x] = this.craftingSlots[x].getStack(); + if( input[x] != null ) + { + hasValue = true; + } + } + + if( hasValue ) + { + return input; + } + + return null; + } + + private ItemStack[] getOutputs() + { + if( this.isCraftingMode() ) + { + final ItemStack out = this.getAndUpdateOutput(); + + if( out != null && out.stackSize > 0 ) + { + return new ItemStack[] { out }; + } + } + else + { + final List list = new ArrayList<>( 3 ); + boolean hasValue = false; + + for( final OptionalSlotFake outputSlot : this.outputSlots ) + { + final ItemStack out = outputSlot.getStack(); + + if( out != null && out.stackSize > 0 ) + { + list.add( out ); + hasValue = true; + } + } + + if( hasValue ) + { + return list.toArray( new ItemStack[list.size()] ); + } + } + + return null; + } + + private boolean isPattern( final ItemStack output ) + { + if( output == null ) + { + return false; + } + + final IDefinitions definitions = AEApi.instance().definitions(); + + boolean isPattern = definitions.items().encodedPattern().isSameAs( output ); + isPattern |= definitions.materials().blankPattern().isSameAs( output ); + + return isPattern; + } + + private NBTBase createItemTag(final ItemStack i ) + { + final NBTTagCompound c = new NBTTagCompound(); + + if( i != null ) + { + i.writeToNBT( c ); + } + + return c; + } + + @Override + public boolean isSlotEnabled( final int idx ) + { + if( idx == 1 ) + { + return Platform.isServer() ? !this.getPatternTerminal().isCraftingRecipe() : !this.isCraftingMode(); + } + else if( idx == 2 ) + { + return Platform.isServer() ? this.getPatternTerminal().isCraftingRecipe() : this.isCraftingMode(); + } + else + { + return false; + } + } + + public void craftOrGetItem( final PacketPatternSlot packetPatternSlot ) + { + if( packetPatternSlot.slotItem != null && this.getCellInventory() != null ) + { + final IAEItemStack out = packetPatternSlot.slotItem.copy(); + InventoryAdaptor inv = new AdaptorPlayerHand( this.getPlayerInv().player ); + final InventoryAdaptor playerInv = InventoryAdaptor.getAdaptor( this.getPlayerInv().player, ForgeDirection.UNKNOWN ); + + if( packetPatternSlot.shift ) + { + inv = playerInv; + } + + if( inv.simulateAdd( out.getItemStack() ) != null ) + { + return; + } + + final IAEItemStack extracted = Platform.poweredExtraction( this.getPowerSource(), this.getCellInventory(), out, this.getActionSource() ); + final EntityPlayer p = this.getPlayerInv().player; + + if( extracted != null ) + { + inv.addItems( extracted.getItemStack() ); + if( p instanceof EntityPlayerMP) + { + this.updateHeld( (EntityPlayerMP) p ); + } + this.detectAndSendChanges(); + return; + } + + final InventoryCrafting ic = new InventoryCrafting( new ContainerNull(), 3, 3 ); + final InventoryCrafting real = new InventoryCrafting( new ContainerNull(), 3, 3 ); + + for( int x = 0; x < 9; x++ ) + { + ic.setInventorySlotContents( x, packetPatternSlot.pattern[x] == null ? null : packetPatternSlot.pattern[x].getItemStack() ); + } + + final IRecipe r = Platform.findMatchingRecipe( ic, p.worldObj ); + + if( r == null ) + { + return; + } + + final IMEMonitor storage = this.getPatternTerminal().getItemInventory(); + final IItemList all = storage.getStorageList(); + + final ItemStack is = r.getCraftingResult( ic ); + + for( int x = 0; x < ic.getSizeInventory(); x++ ) + { + if( ic.getStackInSlot( x ) != null ) + { + final ItemStack pulled = Platform.extractItemsByRecipe( this.getPowerSource(), this.getActionSource(), storage, p.worldObj, r, is, ic, ic.getStackInSlot( x ), x, all, Actionable.MODULATE, ItemViewCell.createFilter( this.getViewCells() ) ); + real.setInventorySlotContents( x, pulled ); + } + } + + final IRecipe rr = Platform.findMatchingRecipe( real, p.worldObj ); + + if( rr == r && Platform.isSameItemPrecise( rr.getCraftingResult( real ), is ) ) + { + final SlotCrafting sc = new SlotCrafting( p, real, this.cOut, 0, 0, 0 ); + sc.onPickupFromSlot( p, is ); + + for( int x = 0; x < real.getSizeInventory(); x++ ) + { + final ItemStack failed = playerInv.addItems( real.getStackInSlot( x ) ); + + if( failed != null ) + { + p.dropPlayerItemWithRandomChoice( failed, false ); + } + } + + inv.addItems( is ); + if( p instanceof EntityPlayerMP ) + { + this.updateHeld( (EntityPlayerMP) p ); + } + this.detectAndSendChanges(); + } + else + { + for( int x = 0; x < real.getSizeInventory(); x++ ) + { + final ItemStack failed = real.getStackInSlot( x ); + if( failed != null ) + { + this.getCellInventory().injectItems( AEItemStack.create( failed ), Actionable.MODULATE, new MachineSource( this.getPatternTerminal() ) ); + } + } + } + } + } + + @Override + public void detectAndSendChanges() + { + super.detectAndSendChanges(); + if( Platform.isServer() ) + { + if( this.isCraftingMode() != this.getPatternTerminal().isCraftingRecipe() ) + { + this.setCraftingMode( this.getPatternTerminal().isCraftingRecipe() ); + this.updateOrderOfOutputSlots(); + } + + this.substitute = this.patternTerminal.isSubstitution(); + } + } + + @Override + public void onUpdate( final String field, final Object oldValue, final Object newValue ) + { + super.onUpdate( field, oldValue, newValue ); + + if( field.equals( "craftingMode" ) ) + { + this.getAndUpdateOutput(); + this.updateOrderOfOutputSlots(); + } + } + + @Override + public void onSlotChange( final Slot s ) + { + if( s == this.patternSlotOUT && Platform.isServer() ) + { + for( final Object crafter : this.crafters ) + { + final ICrafting icrafting = (ICrafting) crafter; + + for( final Object g : this.inventorySlots ) + { + if( g instanceof OptionalSlotFake || g instanceof SlotFakeCraftingMatrix ) + { + final Slot sri = (Slot) g; + icrafting.sendSlotContents( this, sri.slotNumber, sri.getStack() ); + } + } + ( (EntityPlayerMP) icrafting ).isChangingQuantityOnly = false; + } + this.detectAndSendChanges(); + } + } + + public void clear() + { + for( final Slot s : this.craftingSlots ) + { + s.putStack( null ); + } + + for( final Slot s : this.outputSlots ) + { + s.putStack( null ); + } + + this.detectAndSendChanges(); + this.getAndUpdateOutput(); + } + + @Override + public IInventory getInventoryByName( final String name ) + { + if( name.equals( "player" ) ) + { + return this.getInventoryPlayer(); + } + return this.getPatternTerminal().getInventoryByName( name ); + } + + @Override + public boolean useRealItems() + { + return false; + } + + public void toggleSubstitute() + { + this.substitute = !this.substitute; + + this.detectAndSendChanges(); + this.getAndUpdateOutput(); + } + + public boolean isCraftingMode() + { + return this.craftingMode; + } + + private void setCraftingMode( final boolean craftingMode ) + { + this.craftingMode = craftingMode; + } + + public PartFluidPatternTerminal getPatternTerminal() + { + return this.patternTerminal; + } + + private boolean isSubstitute() + { + return this.substitute; + } + + public void setSubstitute( final boolean substitute ) + { + this.substitute = substitute; + } + + static boolean canDoubleStacks(SlotFake[] slots) + { + List enabledSlots = Arrays.stream(slots).filter(SlotFake::isEnabled).collect(Collectors.toList()); + long emptyStots = enabledSlots.stream().filter(s -> s.getStack() == null).count(); + long fullSlots = enabledSlots.stream().filter(s-> s.getStack() != null && s.getStack().stackSize * 2 > 127).count(); + return fullSlots <= emptyStots; + } + + static void doubleStacksInternal(SlotFake[] slots) + { + List overFlowStacks = new ArrayList<>(); + List enabledSlots = Arrays.stream(slots).filter(SlotFake::isEnabled).collect(Collectors.toList()); + for (final Slot s : enabledSlots) + { + ItemStack st = s.getStack(); + if (st == null) + continue; + if (Util.isFluidPacket(st)) { + FluidStack fluidStack = ItemFluidPacket.getFluidStack(st); + if (fluidStack != null) { + fluidStack = ItemFluidPacket.getFluidStack(st).copy(); + if (fluidStack.amount < Integer.MAX_VALUE / 2) + fluidStack.amount *= 2; + } + s.putStack(ItemFluidPacket.newStack(fluidStack)); + } + else if (st.stackSize * 2 > 127) + { + overFlowStacks.add(st.copy()); + } + else + { + st.stackSize *= 2; + s.putStack(st); + } + } + Iterator ow = overFlowStacks.iterator(); + for (final Slot s : enabledSlots) { + if (!ow.hasNext()) + break; + if (s.getStack() != null) + continue; + s.putStack(ow.next()); + } + assert !ow.hasNext(); + } + + public void doubleStacks(boolean isShift) + { + if (!isCraftingMode() && canDoubleStacks(craftingSlots) && canDoubleStacks(outputSlots)) + { + doubleStacksInternal(this.craftingSlots); + doubleStacksInternal(this.outputSlots); + if (isShift && !containsFluid(outputSlots) && !containsFluid(craftingSlots)) + { + while (canDoubleStacks(craftingSlots) && canDoubleStacks(outputSlots)) + { + doubleStacksInternal(this.craftingSlots); + doubleStacksInternal(this.outputSlots); + } + } + this.detectAndSendChanges(); + } + } + + static boolean containsFluid(SlotFake[] slots) { + List enabledSlots = Arrays.stream(slots).filter(SlotFake::isEnabled).collect(Collectors.toList()); + long fluid = enabledSlots.stream().filter(s -> Util.isFluidPacket(s.getStack())).count(); + return fluid > 0; + } + +} diff --git a/src/main/java/com/glodblock/github/client/render/ItemDropRender.java b/src/main/java/com/glodblock/github/client/render/ItemDropRender.java new file mode 100644 index 00000000..fe4173f4 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/render/ItemDropRender.java @@ -0,0 +1,109 @@ +package com.glodblock.github.client.render; + +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.RenderUtil; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraftforge.client.IItemRenderer; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import org.lwjgl.opengl.GL11; + +import java.util.HashMap; +import java.util.Map; + +@SideOnly(Side.CLIENT) +public class ItemDropRender implements IItemRenderer { + + private final Map colourCache = new HashMap<>(); + + public ItemDropRender() { + MinecraftForgeClient.registerItemRenderer(ItemAndBlockHolder.DROP, this); + } + + @Override + public boolean handleRenderType(ItemStack item, ItemRenderType type) { + return type != ItemRenderType.FIRST_PERSON_MAP; + } + + @Override + public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { + return type == ItemRenderType.ENTITY; + } + + @Override + public void renderItem(ItemRenderType type, ItemStack item, Object... data) { + IIcon shape = ItemAndBlockHolder.DROP.shape; + GL11.glEnable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + if (type.equals(ItemRenderType.ENTITY)) { + GL11.glRotated(90.0D, 0.0D, 1.0D, 0.0D); + GL11.glTranslated(-0.5D, -0.6D, 0.0D); + } + + Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.locationItemsTexture); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + FluidStack fluid = ItemFluidDrop.getFluidStack(item); + if (fluid != null) { + int RGB = getColour(fluid); + GL11.glColor3f((RGB >> 16 & 0xFF) / 255.0F, (RGB >> 8 & 0xFF) / 255.0F, (RGB & 0xFF) / 255.0F); + } + + if (type.equals(ItemRenderType.INVENTORY)) { + RenderUtil.renderItemIcon(shape, 16.0D, 0.001D, 0.0F, 0.0F, -1.0F); + } else { + ItemRenderer.renderItemIn2D(Tessellator.instance, shape.getMaxU(), shape.getMinV(), shape.getMinU(), shape.getMaxV(), shape.getIconWidth(), shape.getIconHeight(), 0.0625F); + } + + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glDisable(GL11.GL_BLEND); + } + + public int getColour(FluidStack fluidStack) { + Fluid fluid = fluidStack.getFluid(); + int colour = fluid.getColor(fluidStack); + return colour != 0xFFFFFF ? colour : getColour(fluid); + } + + public int getColour(Fluid fluid) { + Integer cached = colourCache.get(fluid.getName()); + if (cached != null) { + return cached; + } + int colour = fluid.getColor(); + if (colour == 0xFFFFFF) { + TextureAtlasSprite sprite = Minecraft.getMinecraft().getTextureMapBlocks() + .getTextureExtry(fluid.getStillIcon().getIconName()); + if (sprite != null && sprite.getFrameCount() > 0) { + int[][] image = sprite.getFrameTextureData(0); + int r = 0, g = 0, b = 0, count = 0; + for (int[] row : image) { + for (int pixel : row) { + if (((pixel >> 24) & 0xFF) > 127) { // is alpha above 50%? + r += (pixel >> 16) & 0xFF; + g += (pixel >> 8) & 0xFF; + b += pixel & 0xFF; + ++count; + } + } + } + if (count > 0) { + // probably shouldn't need to mask each component by 0xFF + colour = ((r / count) << 16) | ((g / count) << 8) | (b / count); + } + } + } + colourCache.put(fluid.getName(), colour); + return colour; + } +} diff --git a/src/main/java/com/glodblock/github/client/render/ItemPacketRender.java b/src/main/java/com/glodblock/github/client/render/ItemPacketRender.java new file mode 100644 index 00000000..fbec4a86 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/render/ItemPacketRender.java @@ -0,0 +1,75 @@ +package com.glodblock.github.client.render; + +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.RenderUtil; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemRenderer; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraftforge.client.IItemRenderer; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import org.lwjgl.opengl.GL11; + +@SideOnly(Side.CLIENT) +public class ItemPacketRender implements IItemRenderer { + + public ItemPacketRender() { + MinecraftForgeClient.registerItemRenderer(ItemAndBlockHolder.PACKET, this); + } + + @Override + public boolean handleRenderType(ItemStack item, ItemRenderType type) { + return type == ItemRenderType.INVENTORY; + } + + @Override + public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) { + return false; + } + + @Override + public void renderItem(ItemRenderType type, ItemStack item, Object... data) { + if (item == null || item.getItem() == null || !(item.getItem() instanceof ItemFluidPacket)) + return; + + GL11.glEnable(GL11.GL_BLEND); + GL11.glEnable(GL11.GL_ALPHA_TEST); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + + FluidStack fluid = ItemFluidPacket.getFluidStack(item); + int RGB = 0xFFFFFF; + if (fluid != null) { + RGB = fluid.getFluid().getColor(fluid); + } + IIcon icon = fluid == null ? FluidRegistry.WATER.getStillIcon() : fluid.getFluid().getStillIcon(); + + if (RGB != 0xFFFFFF) { + GL11.glColor3f((RGB >> 16 & 0xFF) / 255.0F, (RGB >> 8 & 0xFF) / 255.0F, (RGB & 0xFF) / 255.0F); + } + + Tessellator tess = Tessellator.instance; + tess.startDrawingQuads(); + // draw a simple rectangle for the inventory icon + final float x_min = icon.getMinU(); + final float x_max = icon.getMaxU(); + final float y_min = icon.getMinV(); + final float y_max = icon.getMaxV(); + tess.addVertexWithUV( 0, 16, 0, x_min, y_max); + tess.addVertexWithUV(16, 16, 0, x_max, y_max); + tess.addVertexWithUV(16, 0, 0, x_max, y_min); + tess.addVertexWithUV( 0, 0, 0, x_min, y_min); + tess.draw(); + + GL11.glDisable(GL11.GL_BLEND); + GL11.glDisable(GL11.GL_ALPHA_TEST); + } + +} diff --git a/src/main/java/com/glodblock/github/client/render/RenderBlockFluidInterface.java b/src/main/java/com/glodblock/github/client/render/RenderBlockFluidInterface.java new file mode 100644 index 00000000..d99d1020 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/render/RenderBlockFluidInterface.java @@ -0,0 +1,41 @@ +package com.glodblock.github.client.render; + +import appeng.client.render.BaseBlockRender; +import appeng.client.render.BlockRenderInfo; +import appeng.client.texture.ExtraBlockTextures; +import appeng.tile.misc.TileInterface; +import com.glodblock.github.client.textures.FCPartsTexture; +import com.glodblock.github.common.block.BlockFluidInterface; +import com.glodblock.github.common.tile.TileFluidInterface; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.util.IIcon; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.common.util.ForgeDirection; + +public class RenderBlockFluidInterface extends BaseBlockRender +{ + + public RenderBlockFluidInterface() + { + super( false, 20 ); + } + + @Override + public boolean renderInWorld(final BlockFluidInterface block, final IBlockAccess world, final int x, final int y, final int z, final RenderBlocks renderer ) + { + final TileInterface ti = block.getTileEntity( world, x, y, z ); + final BlockRenderInfo info = block.getRendererInstance(); + + if( ti != null && ti.getForward() != ForgeDirection.UNKNOWN ) + { + final IIcon side = FCPartsTexture.BlockFluidInterfaceAlternate_Arrow.getIcon(); + info.setTemporaryRenderIcons( FCPartsTexture.BlockInterfaceAlternate.getIcon(), block.getIcon( 0, 0 ), side, side, side, side ); + } + + final boolean fz = super.renderInWorld( block, world, x, y, z, renderer ); + + info.setTemporaryRenderIcon( null ); + + return fz; + } +} diff --git a/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java b/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java new file mode 100644 index 00000000..b65c06d1 --- /dev/null +++ b/src/main/java/com/glodblock/github/client/textures/FCPartsTexture.java @@ -0,0 +1,50 @@ +package com.glodblock.github.client.textures; + +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.util.IIcon; +import net.minecraft.util.ResourceLocation; + +public enum FCPartsTexture { + + PartFluidPatternTerminal_Bright("pattern_terminal_bright"), PartFluidPatternTerminal_Dark("pattern_terminal_dark"), + PartFluidPatternTerminal_Colored("pattern_terminal_medium"), PartTerminalBroad("terminal_broad"), + BlockFluidInterfaceAlternate_Arrow("fluid_interface_arrow"), BlockInterfaceAlternate("fluid_interface_a"); + + private final String name; + public net.minecraft.util.IIcon IIcon; + + FCPartsTexture( final String name ) + { + this.name = name; + } + + public static ResourceLocation GuiTexture(final String string) + { + return null; + } + + @SideOnly( Side.CLIENT ) + public static IIcon getMissing() + { + return ( (TextureMap) Minecraft.getMinecraft().getTextureManager().getTexture( TextureMap.locationBlocksTexture ) ).getAtlasSprite( "missingno" ); + } + + public String getName() + { + return this.name; + } + + public IIcon getIcon() + { + return this.IIcon; + } + + public void registerIcon( final TextureMap map ) + { + this.IIcon = map.registerIcon( NameConst.RES_KEY + this.name ); + } +} diff --git a/src/main/java/com/glodblock/github/common/block/BlockFluidDiscretizer.java b/src/main/java/com/glodblock/github/common/block/BlockFluidDiscretizer.java new file mode 100644 index 00000000..d20deb7e --- /dev/null +++ b/src/main/java/com/glodblock/github/common/block/BlockFluidDiscretizer.java @@ -0,0 +1,24 @@ +package com.glodblock.github.common.block; + +import appeng.block.AEBaseItemBlock; +import com.glodblock.github.common.tile.TileFluidDiscretizer; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraft.block.material.Material; + +public class BlockFluidDiscretizer extends FCBaseBlock { + + public BlockFluidDiscretizer() { + super(Material.iron, NameConst.BLOCK_FLUID_DISCRETIZER); + setFullBlock(true); + setOpaque(true); + setTileEntity(TileFluidDiscretizer.class); + } + + public BlockFluidDiscretizer register() { + GameRegistry.registerBlock(this, AEBaseItemBlock.class, NameConst.BLOCK_FLUID_DISCRETIZER); + GameRegistry.registerTileEntity(TileFluidDiscretizer.class, NameConst.BLOCK_FLUID_DISCRETIZER); + return this; + } + +} diff --git a/src/main/java/com/glodblock/github/common/block/BlockFluidInterface.java b/src/main/java/com/glodblock/github/common/block/BlockFluidInterface.java new file mode 100644 index 00000000..e342b966 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/block/BlockFluidInterface.java @@ -0,0 +1,81 @@ +package com.glodblock.github.common.block; + +import appeng.api.util.IOrientable; +import appeng.block.AEBaseItemBlock; +import appeng.core.features.AEFeature; +import appeng.core.sync.GuiBridge; +import appeng.tile.misc.TileInterface; +import appeng.util.Platform; +import com.glodblock.github.client.render.RenderBlockFluidInterface; +import com.glodblock.github.common.tile.TileFluidInterface; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.block.material.Material; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import java.util.EnumSet; + +public class BlockFluidInterface extends FCBaseBlock { + + public BlockFluidInterface() { + super(Material.iron, NameConst.BLOCK_FLUID_INTERFACE); + setFullBlock(true); + setOpaque(true); + setTileEntity(TileFluidInterface.class); + setFeature( EnumSet.of( AEFeature.Core ) ); + } + + @Override + @SideOnly( Side.CLIENT ) + protected RenderBlockFluidInterface getRenderer() + { + return new RenderBlockFluidInterface(); + } + + @Override + public boolean onActivated(final World w, final int x, final int y, final int z, final EntityPlayer p, final int side, final float hitX, final float hitY, final float hitZ ) + { + if( p.isSneaking() ) + { + return false; + } + + final TileInterface tg = this.getTileEntity( w, x, y, z ); + if( tg != null ) + { + if( Platform.isServer() ) + { + Platform.openGUI( p, tg, ForgeDirection.getOrientation( side ), GuiBridge.GUI_INTERFACE ); + } + return true; + } + return false; + } + + @Override + protected boolean hasCustomRotation() + { + return true; + } + + @Override + protected void customRotateBlock(final IOrientable rotatable, final ForgeDirection axis ) + { + if( rotatable instanceof TileInterface ) + { + ( (TileInterface) rotatable ).setSide( axis ); + } + } + + public BlockFluidInterface register() { + GameRegistry.registerBlock(this, AEBaseItemBlock.class, NameConst.BLOCK_FLUID_INTERFACE); + GameRegistry.registerTileEntity(TileFluidInterface.class, NameConst.BLOCK_FLUID_INTERFACE); + return this; + } + + +} diff --git a/src/main/java/com/glodblock/github/common/block/BlockFluidPacketDecoder.java b/src/main/java/com/glodblock/github/common/block/BlockFluidPacketDecoder.java new file mode 100644 index 00000000..bea0ee3a --- /dev/null +++ b/src/main/java/com/glodblock/github/common/block/BlockFluidPacketDecoder.java @@ -0,0 +1,45 @@ +package com.glodblock.github.common.block; + +import appeng.block.AEBaseItemBlock; +import com.glodblock.github.common.tile.TileFluidPacketDecoder; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraft.block.material.Material; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +public class BlockFluidPacketDecoder extends FCBaseBlock { + + public BlockFluidPacketDecoder() { + super(Material.iron, NameConst.BLOCK_FLUID_PACKET_DECODER); + setFullBlock(true); + setOpaque(true); + setTileEntity(TileFluidPacketDecoder.class); + } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int facing, float hitX, float hitY, float hitZ) { + if (player.isSneaking()) { + return false; + } + TileFluidPacketDecoder tile = getTileEntity(world, x, y, z); + if (tile != null) { + if (!world.isRemote) { + InventoryHandler.openGui(player, world, new BlockPos(x, y, z), EnumFacing.getFront(facing), GuiType.FLUID_PACKET_DECODER); + } + return true; + } + return false; + } + + public BlockFluidPacketDecoder register() { + GameRegistry.registerBlock(this, AEBaseItemBlock.class, NameConst.BLOCK_FLUID_PACKET_DECODER); + GameRegistry.registerTileEntity(TileFluidPacketDecoder.class, NameConst.BLOCK_FLUID_PACKET_DECODER); + return this; + } + +} diff --git a/src/main/java/com/glodblock/github/common/block/BlockFluidPatternEncoder.java b/src/main/java/com/glodblock/github/common/block/BlockFluidPatternEncoder.java new file mode 100644 index 00000000..2790b504 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/block/BlockFluidPatternEncoder.java @@ -0,0 +1,46 @@ +package com.glodblock.github.common.block; + +import appeng.block.AEBaseItemBlock; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.tile.TileFluidPatternEncoder; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraft.block.material.Material; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +public class BlockFluidPatternEncoder extends FCBaseBlock { + + public BlockFluidPatternEncoder() { + super(Material.iron, NameConst.BLOCK_FLUID_PATTERN_ENCODER); + setFullBlock(true); + setOpaque(true); + setTileEntity(TileFluidPatternEncoder.class); + } + + @Override + public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int facing, float hitX, float hitY, float hitZ) { + if (player.isSneaking()) { + return false; + } + TileFluidPatternEncoder tile = getTileEntity(world, x, y, z); + if (tile != null) { + if (!world.isRemote) { + InventoryHandler.openGui(player, world, new BlockPos(x, y, z), EnumFacing.getFront(facing), GuiType.FLUID_PATTERN_ENCODER); + } + return true; + } + return false; + } + + public BlockFluidPatternEncoder register() { + GameRegistry.registerBlock(this, AEBaseItemBlock.class, NameConst.BLOCK_FLUID_PATTERN_ENCODER); + GameRegistry.registerTileEntity(TileFluidPatternEncoder.class, NameConst.BLOCK_FLUID_PATTERN_ENCODER); + return this; + } + +} diff --git a/src/main/java/com/glodblock/github/common/block/FCBaseBlock.java b/src/main/java/com/glodblock/github/common/block/FCBaseBlock.java new file mode 100644 index 00000000..01805e37 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/block/FCBaseBlock.java @@ -0,0 +1,37 @@ +package com.glodblock.github.common.block; + +import appeng.block.AEBaseTileBlock; +import appeng.core.features.AEFeature; +import com.glodblock.github.FluidCraft; +import net.minecraft.block.material.Material; +import net.minecraft.tileentity.TileEntity; + +import java.util.EnumSet; + +public class FCBaseBlock extends AEBaseTileBlock { + + public FCBaseBlock(Material mat, String name) { + super(mat); + this.setBlockName(name); + this.setBlockTextureName(FluidCraft.MODID + ":" + name); + } + + @Override + public void setTileEntity( final Class clazz ) { + super.setTileEntity(clazz); + } + + public void setOpaque(boolean opaque) { + this.isOpaque = opaque; + } + + public void setFullBlock(boolean full) { + this.isFullSize = full; + } + + @Override + public void setFeature( final EnumSet f ) { + super.setFeature(f); + } + +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemFluidDrop.java b/src/main/java/com/glodblock/github/common/item/ItemFluidDrop.java new file mode 100644 index 00000000..4661224f --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemFluidDrop.java @@ -0,0 +1,142 @@ +package com.glodblock.github.common.item; + +import appeng.api.storage.data.IAEFluidStack; +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEFluidStack; +import appeng.util.item.AEItemStack; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.client.resources.I18n; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IIcon; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; + +public class ItemFluidDrop extends Item { + + @SideOnly(Side.CLIENT) + public IIcon shape; + + @Override + @SuppressWarnings("unchecked") + @SideOnly(Side.CLIENT) + public void getSubItems(Item item, CreativeTabs tab, List list) { + if (CreativeTabs.tabMisc.equals(tab)) { + list.add(newStack(new FluidStack(FluidRegistry.WATER, 1))); + list.add(newStack(new FluidStack(FluidRegistry.LAVA, 1))); + } + } + + @Override + public String getItemStackDisplayName(ItemStack stack) { + FluidStack fluid = getFluidStack(stack); + return I18n.format("item.fluid_drop.name", fluid == null ? "???" : fluid.getLocalizedName()); + } + + @Override + @SuppressWarnings("unchecked") + @SideOnly(Side.CLIENT) + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean flag) { + FluidStack fluid = getFluidStack(stack); + if (fluid != null) { + tooltip.add(String.format(EnumChatFormatting.GRAY + "%s, 1 mB", fluid.getLocalizedName())); + } else { + tooltip.add(EnumChatFormatting.RED + I18n.format(NameConst.TT_INVALID_FLUID)); + } + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aIconRegister) { + super.registerIcons(aIconRegister); + shape = aIconRegister.registerIcon(NameConst.RES_KEY + NameConst.ITEM_FLUID_DROP); + } + + public static ItemStack newStack(@Nullable FluidStack fluid) { + if (fluid == null || fluid.amount <= 0) { + return null; + } + ItemStack stack = new ItemStack(ItemAndBlockHolder.DROP, fluid.amount); + NBTTagCompound tag = new NBTTagCompound(); + tag.setString("Fluid", fluid.getFluid().getName()); + stack.setTagCompound(tag); + return stack; + } + + public static FluidStack getFluidStack(ItemStack stack) { + if (stack == null|| stack.getItem() != ItemAndBlockHolder.DROP || !stack.hasTagCompound()) { + return null; + } + NBTTagCompound tag = Objects.requireNonNull(stack.getTagCompound()); + if (!tag.hasKey("Fluid", Constants.NBT.TAG_STRING)) { + return null; + } + Fluid fluid = FluidRegistry.getFluid(tag.getString("Fluid").toLowerCase()); + if (fluid == null) { + return null; + } + FluidStack fluidStack = new FluidStack(fluid, stack.stackSize); + if (tag.hasKey("FluidTag", Constants.NBT.TAG_COMPOUND)) { + fluidStack.tag = tag.getCompoundTag("FluidTag"); + } + return fluidStack; + } + + public static IAEFluidStack getAeFluidStack(@Nullable IAEItemStack stack) { + if (stack == null) { + return null; + } + IAEFluidStack fluidStack = AEFluidStack.create(getFluidStack(stack.getItemStack())); + if (fluidStack == null) { + return null; + } + fluidStack.setStackSize(stack.getStackSize()); + return fluidStack; + } + + public static IAEItemStack newAeStack(@Nullable FluidStack fluid) { + if (fluid == null || fluid.amount <= 0) { + return null; + } + IAEItemStack stack = AEItemStack.create(newStack(fluid)); + if (stack == null) { + return null; + } + stack.setStackSize(fluid.amount); + return stack; + } + + public static IAEItemStack newAeStack(@Nullable IAEFluidStack fluid) { + if (fluid == null || fluid.getStackSize() <= 0) { + return null; + } + IAEItemStack stack = AEItemStack.create(newStack(fluid.getFluidStack())); + if (stack == null) { + return null; + } + stack.setStackSize(fluid.getStackSize()); + return stack; + } + + public ItemFluidDrop register() { + GameRegistry.registerItem(this, NameConst.ITEM_FLUID_DROP, FluidCraft.MODID); + return this; + } + +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemFluidEncodedPattern.java b/src/main/java/com/glodblock/github/common/item/ItemFluidEncodedPattern.java new file mode 100644 index 00000000..0e777d2e --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemFluidEncodedPattern.java @@ -0,0 +1,31 @@ +package com.glodblock.github.common.item; + +import appeng.api.networking.crafting.ICraftingPatternDetails; +import appeng.items.misc.ItemEncodedPattern; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.util.FluidPatternDetails; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public class ItemFluidEncodedPattern extends ItemEncodedPattern { + + public ItemFluidEncodedPattern() { + super(); + this.setUnlocalizedName(NameConst.ITEM_FLUID_ENCODED_PATTERN); + this.setTextureName(FluidCraft.MODID + ":" + NameConst.ITEM_FLUID_ENCODED_PATTERN); + } + + @Override + public ICraftingPatternDetails getPatternForItem(ItemStack is, World w) { + FluidPatternDetails pattern = new FluidPatternDetails(is); + return pattern.readFromStack() ? pattern : null; + } + + public ItemFluidEncodedPattern register() { + GameRegistry.registerItem(this, NameConst.ITEM_FLUID_ENCODED_PATTERN, FluidCraft.MODID); + return this; + } + +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemFluidPacket.java b/src/main/java/com/glodblock/github/common/item/ItemFluidPacket.java new file mode 100644 index 00000000..4f24bd5e --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemFluidPacket.java @@ -0,0 +1,110 @@ +package com.glodblock.github.common.item; + +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEItemStack; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.client.resources.I18n; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IIcon; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Objects; + +public class ItemFluidPacket extends Item { + + public ItemFluidPacket() { + setUnlocalizedName(NameConst.ITEM_FLUID_PACKET); + setMaxStackSize(1); + } + + @Override + public String getItemStackDisplayName(ItemStack stack) { + FluidStack fluid = getFluidStack(stack); + return fluid != null ? String.format("%s, %,d mB", fluid.getLocalizedName(), fluid.amount) + : super.getItemStackDisplayName(stack); + } + + @Override + @SuppressWarnings("unchecked") + public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean flags) { + FluidStack fluid = getFluidStack(stack); + if (fluid != null) { + for (String line : I18n.format(NameConst.TT_FLUID_PACKET).split("\\\\n")) { + tooltip.add(EnumChatFormatting.GRAY + line); + } + } else { + tooltip.add(EnumChatFormatting.RED + I18n.format(NameConst.TT_INVALID_FLUID)); + } + } + + public static FluidStack getFluidStack(ItemStack stack) { + if (stack == null || !stack.hasTagCompound()) { + return null; + } + FluidStack fluid = FluidStack.loadFluidStackFromNBT(Objects.requireNonNull(stack.getTagCompound()).getCompoundTag("FluidStack")); + return (fluid != null && fluid.amount > 0) ? fluid : null; + } + + public static FluidStack getFluidStack(@Nullable IAEItemStack stack) { + return stack != null ? getFluidStack(stack.getItemStack()) : null; + } + + public static ItemStack newStack(@Nullable FluidStack fluid) { + if (fluid == null || fluid.amount == 0) { + return null; + } + ItemStack stack = new ItemStack(ItemAndBlockHolder.PACKET); + NBTTagCompound tag = new NBTTagCompound(); + NBTTagCompound fluidTag = new NBTTagCompound(); + fluid.writeToNBT(fluidTag); + tag.setTag("FluidStack", fluidTag); + stack.setTagCompound(tag); + return stack; + } + + @Nullable + public static IAEItemStack newAeStack(@Nullable FluidStack fluid) { + return AEItemStack.create(newStack(fluid)); + } + + @Override + public int getSpriteNumber() { + return 0; + } + + @Override + @SideOnly(Side.CLIENT) + public void registerIcons(IIconRegister aIconRegister) { + } + + @SideOnly(Side.CLIENT) + public IIcon getIconFromDamage(int p_77617_1_) { + return FluidRegistry.WATER.getStillIcon(); + } + + @Override + @SideOnly(Side.CLIENT) + public int getColorFromItemStack(ItemStack aStack, int aRenderPass) { + Fluid tFluid = FluidRegistry.getFluid(aStack.getItemDamage()); + return tFluid == null ? 16777215 : tFluid.getColor(); + } + + public ItemFluidPacket register() { + GameRegistry.registerItem(this, NameConst.ITEM_FLUID_PACKET, FluidCraft.MODID); + return this; + } +} diff --git a/src/main/java/com/glodblock/github/common/item/ItemPartFluidPatternTerminal.java b/src/main/java/com/glodblock/github/common/item/ItemPartFluidPatternTerminal.java new file mode 100644 index 00000000..0ddbbf18 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/item/ItemPartFluidPatternTerminal.java @@ -0,0 +1,53 @@ +package com.glodblock.github.common.item; + +import appeng.api.AEApi; +import appeng.api.parts.IPartItem; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.util.NameConst; +import cpw.mods.fml.common.registry.GameRegistry; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.renderer.texture.IIconRegister; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class ItemPartFluidPatternTerminal extends Item implements IPartItem { + + public ItemPartFluidPatternTerminal() { + this.setMaxStackSize(64); + this.setUnlocalizedName(NameConst.ITEM_PART_FLUID_PATTERN_TERMINAL); + AEApi.instance().partHelper().setItemBusRenderer(this); + } + + @Nullable + @Override + public PartFluidPatternTerminal createPartFromItemStack(ItemStack is) { + return new PartFluidPatternTerminal(is); + } + + @Override + public boolean onItemUse(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float xOffset, float yOffset, float zOffset) { + return AEApi.instance().partHelper().placeBus(player.getHeldItem(), x, y, z, side, player, world); + } + + public ItemPartFluidPatternTerminal register() { + GameRegistry.registerItem(this, NameConst.ITEM_PART_FLUID_PATTERN_TERMINAL, FluidCraft.MODID); + return this; + } + + @Override + public void registerIcons(IIconRegister _iconRegister) { + } + + @Override + @SideOnly(Side.CLIENT) + public int getSpriteNumber() { + return 0; + } + +} diff --git a/src/main/java/com/glodblock/github/common/parts/FCBasePart.java b/src/main/java/com/glodblock/github/common/parts/FCBasePart.java new file mode 100644 index 00000000..671412d2 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/parts/FCBasePart.java @@ -0,0 +1,507 @@ +package com.glodblock.github.common.parts; + +import appeng.api.config.Settings; +import appeng.api.config.SortDir; +import appeng.api.config.SortOrder; +import appeng.api.config.ViewItems; +import appeng.api.implementations.IPowerChannelState; +import appeng.api.implementations.tiles.IViewCellStorage; +import appeng.api.networking.GridFlags; +import appeng.api.networking.events.MENetworkBootingStatusChange; +import appeng.api.networking.events.MENetworkEventSubscribe; +import appeng.api.networking.events.MENetworkPowerStatusChange; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.parts.IPartRenderHelper; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.ITerminalHost; +import appeng.api.storage.data.IAEFluidStack; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.util.IConfigManager; +import appeng.client.texture.CableBusTextures; +import appeng.core.sync.GuiBridge; +import appeng.me.GridAccessException; +import appeng.parts.AEBasePart; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.inventory.IAEAppEngInventory; +import appeng.tile.inventory.InvOperation; +import appeng.util.ConfigManager; +import appeng.util.IConfigManagerHost; +import appeng.util.Platform; +import com.glodblock.github.client.textures.FCPartsTexture; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import io.netty.buffer.ByteBuf; +import net.minecraft.client.renderer.RenderBlocks; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.IIcon; +import net.minecraft.util.MathHelper; +import net.minecraft.util.Vec3; +import net.minecraftforge.common.util.ForgeDirection; + +import java.io.IOException; +import java.util.List; + +public abstract class FCBasePart extends AEBasePart implements IPowerChannelState, ITerminalHost, IConfigManagerHost, IViewCellStorage, IAEAppEngInventory { + + protected static final int POWERED_FLAG = 4; + protected static final int CHANNEL_FLAG = 16; + private static final int BOOTING_FLAG = 8; + + private byte spin = 0; // 0-3 + private int clientFlags = 0; // sent as byte. + private float opacity = -1; + + private final IConfigManager cm = new ConfigManager( this ); + private final AppEngInternalInventory viewCell = new AppEngInternalInventory( this, 5 ); + + public FCBasePart( final ItemStack is ) + { + this( is, false ); + } + + protected FCBasePart( final ItemStack is, final boolean requireChannel ) + { + super( is ); + + if( requireChannel ) + { + this.getProxy().setFlags( GridFlags.REQUIRE_CHANNEL ); + this.getProxy().setIdlePowerUsage( 1.0 / 2.0 ); + } + else + { + this.getProxy().setIdlePowerUsage( 1.0 / 16.0 ); // lights drain a little bit. + } + this.cm.registerSetting( Settings.SORT_BY, SortOrder.NAME ); + this.cm.registerSetting( Settings.VIEW_MODE, ViewItems.ALL ); + this.cm.registerSetting( Settings.SORT_DIRECTION, SortDir.ASCENDING ); + } + + @MENetworkEventSubscribe + public final void bootingRender( final MENetworkBootingStatusChange c ) + { + if( !this.isLightSource() ) + { + this.getHost().markForUpdate(); + } + } + + @MENetworkEventSubscribe + public final void powerRender( final MENetworkPowerStatusChange c ) + { + this.getHost().markForUpdate(); + } + + @Override + public final void getBoxes( final IPartCollisionHelper bch ) + { + bch.addBox( 2, 2, 14, 14, 14, 16 ); + bch.addBox( 4, 4, 13, 12, 12, 14 ); + } + + @Override + public void onNeighborChanged() + { + this.opacity = -1; + this.getHost().markForUpdate(); + } + + @Override + public void getDrops(final List drops, final boolean wrenched ) + { + super.getDrops( drops, wrenched ); + + for( final ItemStack is : this.viewCell ) + { + if( is != null ) + { + drops.add( is ); + } + } + } + + @Override + public IConfigManager getConfigManager() + { + return this.cm; + } + + @Override + public void readFromNBT( final NBTTagCompound data ) + { + super.readFromNBT( data ); + if( data.hasKey( "opacity" ) ) + { + this.opacity = data.getFloat( "opacity" ); + } + this.spin = data.getByte( "spin" ); + this.cm.readFromNBT( data ); + this.viewCell.readFromNBT( data, "viewCell" ); + } + + @Override + public void writeToNBT( final NBTTagCompound data ) + { + super.writeToNBT( data ); + data.setFloat( "opacity", this.opacity ); + data.setByte( "spin", this.getSpin() ); + this.cm.writeToNBT( data ); + this.viewCell.writeToNBT( data, "viewCell" ); + } + + public GuiBridge getGui(final EntityPlayer player ) + { + return GuiBridge.GUI_ME; + } + + + @Override + public void writeToStream( final ByteBuf data ) throws IOException + { + super.writeToStream( data ); + this.clientFlags = this.getSpin() & 3; + + try + { + if( this.getProxy().getEnergy().isNetworkPowered() ) + { + this.clientFlags = this.getClientFlags() | FCBasePart.POWERED_FLAG; + } + + if( this.getProxy().getPath().isNetworkBooting() ) + { + this.clientFlags = this.getClientFlags() | FCBasePart.BOOTING_FLAG; + } + + if( this.getProxy().getNode().meetsChannelRequirements() ) + { + this.clientFlags = this.getClientFlags() | FCBasePart.CHANNEL_FLAG; + } + } + catch( final GridAccessException e ) + { + // um.. nothing. + } + + data.writeByte( (byte) this.getClientFlags() ); + } + + @Override + public boolean readFromStream( final ByteBuf data ) throws IOException + { + super.readFromStream( data ); + final int oldFlags = this.getClientFlags(); + this.clientFlags = data.readByte(); + this.spin = (byte) ( this.getClientFlags() & 3 ); + return this.getClientFlags() != oldFlags; + } + + @Override + public final int getLightLevel() + { + return this.blockLight( this.isPowered() ? ( this.isLightSource() ? 15 : 9 ) : 0 ); + } + + public boolean onPartActivate0(final EntityPlayer player, final Vec3 pos ) + { + final TileEntity te = this.getTile(); + + if( !player.isSneaking() && Platform.isWrench( player, player.inventory.getCurrentItem(), te.xCoord, te.yCoord, te.zCoord ) ) + { + if( Platform.isServer() ) + { + if( this.getSpin() > 3 ) + { + this.spin = 0; + } + + switch( this.getSpin() ) + { + case 0: + this.spin = 1; + break; + case 1: + this.spin = 3; + break; + case 2: + this.spin = 0; + break; + case 3: + this.spin = 2; + break; + } + + this.getHost().markForUpdate(); + this.saveChanges(); + } + return true; + } + else + { + return super.onPartActivate( player, pos ); + } + } + + @Override + public boolean onPartActivate( final EntityPlayer player, final Vec3 pos ) + { + if( !onPartActivate0( player, pos ) ) + { + if( !player.isSneaking() ) + { + if( Platform.isClient() ) + { + return true; + } + + Platform.openGUI( player, this.getHost().getTile(), this.getSide(), this.getGui( player ) ); + + return true; + } + } + return false; + } + + @Override + public final void onPlacement( final EntityPlayer player, final ItemStack held, final ForgeDirection side ) + { + super.onPlacement( player, held, side ); + + final byte rotation = (byte) ( MathHelper.floor_double( ( player.rotationYaw * 4F ) / 360F + 2.5D ) & 3 ); + if( side == ForgeDirection.UP ) + { + this.spin = rotation; + } + else if( side == ForgeDirection.DOWN ) + { + this.spin = rotation; + } + } + + private int blockLight(final int emit ) + { + if( this.opacity < 0 ) + { + final TileEntity te = this.getTile(); + this.opacity = 255 - te.getWorldObj().getBlockLightOpacity( te.xCoord + this.getSide().offsetX, te.yCoord + this.getSide().offsetY, te.zCoord + this.getSide().offsetZ ); + } + + return (int) ( emit * ( this.opacity / 255.0f ) ); + } + + @Override + public final boolean isPowered() + { + try + { + if( Platform.isServer() ) + { + return this.getProxy().getEnergy().isNetworkPowered(); + } + else + { + return ( ( this.getClientFlags() & FCBasePart.POWERED_FLAG ) == FCBasePart.POWERED_FLAG ); + } + } + catch( final GridAccessException e ) + { + return false; + } + } + + @Override + public IMEMonitor getItemInventory() + { + try + { + return this.getProxy().getStorage().getItemInventory(); + } + catch( final GridAccessException e ) + { + // err nope? + } + return null; + } + + @Override + public IMEMonitor getFluidInventory() + { + try + { + return this.getProxy().getStorage().getFluidInventory(); + } + catch( final GridAccessException e ) + { + // err nope? + } + return null; + } + + @Override + public void updateSetting( final IConfigManager manager, final Enum settingName, final Enum newValue ) + { + + } + + @Override + public IInventory getViewCellStorage() + { + return this.viewCell; + } + + @Override + public void onChangeInventory(final IInventory inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack ) + { + this.getHost().markForSave(); + } + + @Override + public final boolean isActive() + { + if( !this.isLightSource() ) + { + return ( ( this.getClientFlags() & ( FCBasePart.CHANNEL_FLAG | FCBasePart.POWERED_FLAG ) ) == ( FCBasePart.CHANNEL_FLAG | FCBasePart.POWERED_FLAG ) ); + } + else + { + return this.isPowered(); + } + } + + public final int getClientFlags() + { + return this.clientFlags; + } + + public final byte getSpin() + { + return this.spin; + } + + @Override + @SideOnly( Side.CLIENT ) + public void renderInventory(final IPartRenderHelper rh, final RenderBlocks renderer ) + { + rh.setBounds( 2, 2, 14, 14, 14, 16 ); + + final IIcon sideTexture = CableBusTextures.PartMonitorSides.getIcon(); + final IIcon backTexture = CableBusTextures.PartMonitorBack.getIcon(); + + rh.setTexture( sideTexture, sideTexture, backTexture, FCPartsTexture.PartTerminalBroad.getIcon(), sideTexture, sideTexture ); + rh.renderInventoryBox( renderer ); + + rh.setInvColor( this.getColor().whiteVariant ); + rh.renderInventoryFace( this.getFrontBright().getIcon(), ForgeDirection.SOUTH, renderer ); + + rh.setInvColor( this.getColor().mediumVariant ); + rh.renderInventoryFace( this.getFrontDark().getIcon(), ForgeDirection.SOUTH, renderer ); + + rh.setInvColor( this.getColor().blackVariant ); + rh.renderInventoryFace( this.getFrontColored().getIcon(), ForgeDirection.SOUTH, renderer ); + + rh.setBounds( 4, 4, 13, 12, 12, 14 ); + rh.renderInventoryBox( renderer ); + } + + @Override + @SideOnly( Side.CLIENT ) + public void renderStatic( final int x, final int y, final int z, final IPartRenderHelper rh, final RenderBlocks renderer ) + { + this.setRenderCache( rh.useSimplifiedRendering( x, y, z, this, this.getRenderCache() ) ); + + final IIcon sideTexture = CableBusTextures.PartMonitorSides.getIcon(); + final IIcon backTexture = CableBusTextures.PartMonitorBack.getIcon(); + + rh.setTexture( sideTexture, sideTexture, backTexture, FCPartsTexture.PartTerminalBroad.getIcon(), sideTexture, sideTexture ); + + rh.setBounds( 2, 2, 14, 14, 14, 16 ); + rh.renderBlock( x, y, z, renderer ); + + if( this.getLightLevel() > 0 ) + { + final int l = 13; + Tessellator.instance.setBrightness( l << 20 | l << 4 ); + } + + renderer.uvRotateBottom = renderer.uvRotateEast = renderer.uvRotateNorth = renderer.uvRotateSouth = renderer.uvRotateTop = renderer.uvRotateWest = this.getSpin(); + + Tessellator.instance.setColorOpaque_I( this.getColor().whiteVariant ); + rh.renderFace( x, y, z, this.getFrontBright().getIcon(), ForgeDirection.SOUTH, renderer ); + + Tessellator.instance.setColorOpaque_I( this.getColor().mediumVariant ); + rh.renderFace( x, y, z, this.getFrontDark().getIcon(), ForgeDirection.SOUTH, renderer ); + + Tessellator.instance.setColorOpaque_I( this.getColor().blackVariant ); + rh.renderFace( x, y, z, this.getFrontColored().getIcon(), ForgeDirection.SOUTH, renderer ); + + renderer.uvRotateBottom = renderer.uvRotateEast = renderer.uvRotateNorth = renderer.uvRotateSouth = renderer.uvRotateTop = renderer.uvRotateWest = 0; + + final IIcon sideStatusTexture = CableBusTextures.PartMonitorSidesStatus.getIcon(); + + rh.setTexture( sideStatusTexture, sideStatusTexture, backTexture, this.getItemStack().getIconIndex(), sideStatusTexture, sideStatusTexture ); + + rh.setBounds( 4, 4, 13, 12, 12, 14 ); + rh.renderBlock( x, y, z, renderer ); + + final boolean hasChan = ( this.getClientFlags() & ( FCBasePart.POWERED_FLAG | FCBasePart.CHANNEL_FLAG ) ) == ( FCBasePart.POWERED_FLAG | FCBasePart.CHANNEL_FLAG ); + final boolean hasPower = ( this.getClientFlags() & FCBasePart.POWERED_FLAG ) == FCBasePart.POWERED_FLAG; + + if( hasChan ) + { + final int l = 14; + Tessellator.instance.setBrightness( l << 20 | l << 4 ); + Tessellator.instance.setColorOpaque_I( this.getColor().blackVariant ); + } + else if( hasPower ) + { + final int l = 9; + Tessellator.instance.setBrightness( l << 20 | l << 4 ); + Tessellator.instance.setColorOpaque_I( this.getColor().whiteVariant ); + } + else + { + Tessellator.instance.setBrightness( 0 ); + Tessellator.instance.setColorOpaque_I( 0x000000 ); + } + + final IIcon sideStatusLightTexture = CableBusTextures.PartMonitorSidesStatusLights.getIcon(); + + rh.renderFace( x, y, z, sideStatusLightTexture, ForgeDirection.EAST, renderer ); + rh.renderFace( x, y, z, sideStatusLightTexture, ForgeDirection.WEST, renderer ); + rh.renderFace( x, y, z, sideStatusLightTexture, ForgeDirection.UP, renderer ); + rh.renderFace( x, y, z, sideStatusLightTexture, ForgeDirection.DOWN, renderer ); + } + + /** + * The texture used for the bright front layer. + *

+ * The final texture can overlap any of the the texture in no particular order. + */ + public abstract FCPartsTexture getFrontBright(); + + /** + * The texture used for the colored (medium) front layer. + *

+ * The final texture can overlap any of the the texture in no particular order. + */ + public abstract FCPartsTexture getFrontColored(); + + /** + * The texture used for the dark front layer. + *

+ * The final texture can overlap any of the the texture in no particular order. + */ + public abstract FCPartsTexture getFrontDark(); + + /** + * Should the part emit light. This actually only affects the light level, light source use a level of 15 and non + * light source 9. + */ + public abstract boolean isLightSource(); + +} diff --git a/src/main/java/com/glodblock/github/common/parts/PartFluidPatternTerminal.java b/src/main/java/com/glodblock/github/common/parts/PartFluidPatternTerminal.java new file mode 100644 index 00000000..2602187e --- /dev/null +++ b/src/main/java/com/glodblock/github/common/parts/PartFluidPatternTerminal.java @@ -0,0 +1,288 @@ +package com.glodblock.github.common.parts; + +import appeng.api.config.SecurityPermissions; +import appeng.api.implementations.ICraftingPatternItem; +import appeng.api.networking.IGridHost; +import appeng.api.networking.crafting.ICraftingPatternDetails; +import appeng.api.storage.data.IAEItemStack; +import appeng.core.sync.GuiBridge; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.inventory.InvOperation; +import appeng.util.Platform; +import com.glodblock.github.client.textures.FCPartsTexture; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidEncodedPattern; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.FluidPatternDetails; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.Vec3; + +import java.util.List; +import java.util.Objects; + +public class PartFluidPatternTerminal extends FCBasePart { + + private static final FCPartsTexture FRONT_BRIGHT_ICON = FCPartsTexture.PartFluidPatternTerminal_Bright; + private static final FCPartsTexture FRONT_DARK_ICON = FCPartsTexture.PartFluidPatternTerminal_Colored; + private static final FCPartsTexture FRONT_COLORED_ICON = FCPartsTexture.PartFluidPatternTerminal_Dark; + + private final AppEngInternalInventory crafting = new AppEngInternalInventory( this, 9 ); + private final AppEngInternalInventory output = new AppEngInternalInventory( this, 3 ); + private final AppEngInternalInventory pattern = new AppEngInternalInventory( this, 2 ); + + private boolean craftingMode = true; + private boolean substitute = false; + + public PartFluidPatternTerminal(ItemStack is) { + super(is, true); + } + + @Override + public void getDrops(final List drops, final boolean wrenched ) + { + for( final ItemStack is : this.pattern ) + { + if( is != null ) + { + drops.add( is ); + } + } + } + + @Override + public boolean onPartActivate( final EntityPlayer player, final Vec3 pos ) { + TileEntity te = this.getTile(); + BlockPos tePos = new BlockPos(te); + if (Platform.isWrench(player, player.inventory.getCurrentItem(), tePos.getX(), tePos.getY(), tePos.getZ())) { + return super.onPartActivate(player, pos); + } + if (Platform.isServer()) { + if (Util.hasPermission(player, SecurityPermissions.INJECT, (IGridHost) this) || Util.hasPermission(player, SecurityPermissions.EXTRACT, (IGridHost) this)) { + InventoryHandler.openGui(player, te.getWorldObj(), tePos, Objects.requireNonNull(Util.from(getSide())), GuiType.FLUID_PATTERN_TERMINAL); + } + else { + player.addChatComponentMessage(new ChatComponentText("You don't have permission to view.")); + } + } + return true; + } + + @Override + public void readFromNBT( final NBTTagCompound data ) + { + super.readFromNBT( data ); + this.setCraftingRecipe( data.getBoolean( "craftingMode" ) ); + this.setSubstitution( data.getBoolean( "substitute" ) ); + this.pattern.readFromNBT( data, "pattern" ); + this.output.readFromNBT( data, "outputList" ); + this.crafting.readFromNBT( data, "craftingGrid" ); + } + + @Override + public void writeToNBT( final NBTTagCompound data ) + { + super.writeToNBT( data ); + data.setBoolean( "craftingMode", this.craftingMode ); + data.setBoolean( "substitute", this.substitute ); + this.pattern.writeToNBT( data, "pattern" ); + this.output.writeToNBT( data, "outputList" ); + this.crafting.writeToNBT( data, "craftingGrid" ); + } + + @Override + public GuiBridge getGui(final EntityPlayer p ) + { + int x = (int) p.posX; + int y = (int) p.posY; + int z = (int) p.posZ; + if( this.getHost().getTile() != null ) + { + x = this.getTile().xCoord; + y = this.getTile().yCoord; + z = this.getTile().zCoord; + } + + if( GuiBridge.GUI_PATTERN_TERMINAL.hasPermissions( this.getHost().getTile(), x, y, z, this.getSide(), p ) ) + { + return GuiBridge.GUI_PATTERN_TERMINAL; + } + return GuiBridge.GUI_ME; + } + + public void onChangeInventory0(final IInventory inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack ) + { + if( inv == this.pattern && slot == 1 ) + { + final ItemStack is = this.pattern.getStackInSlot( 1 ); + if( is != null && is.getItem() instanceof ICraftingPatternItem) + { + final ICraftingPatternItem pattern = (ICraftingPatternItem) is.getItem(); + final ICraftingPatternDetails details = pattern.getPatternForItem( is, this.getHost().getTile().getWorldObj() ); + if( details != null ) + { + this.setCraftingRecipe( details.isCraftable() ); + this.setSubstitution( details.canSubstitute() ); + + for( int x = 0; x < this.crafting.getSizeInventory() && x < details.getInputs().length; x++ ) + { + final IAEItemStack item = details.getInputs()[x]; + this.crafting.setInventorySlotContents( x, item == null ? null : item.getItemStack() ); + } + + for( int x = 0; x < this.output.getSizeInventory() && x < details.getOutputs().length; x++ ) + { + final IAEItemStack item = details.getOutputs()[x]; + this.output.setInventorySlotContents( x, item == null ? null : item.getItemStack() ); + } + } + } + } + else if( inv == this.crafting ) + { + this.fixCraftingRecipes(); + } + + this.getHost().markForSave(); + } + + @Override + public void onChangeInventory(final IInventory inv, final int slot, final InvOperation mc, final ItemStack removedStack, final ItemStack newStack ) + { + if (inv == this.pattern && slot == 1) { + final ItemStack is = inv.getStackInSlot(1); + if (is != null && is.getItem() instanceof ItemFluidEncodedPattern) { + final ItemFluidEncodedPattern pattern = (ItemFluidEncodedPattern) is.getItem(); + final ICraftingPatternDetails details = pattern.getPatternForItem( is, this.getHost().getTile().getWorldObj() ); + if( details != null ) + { + this.setCraftingRecipe( details.isCraftable() ); + this.setSubstitution( details.canSubstitute() ); + + for( int x = 0; x < this.crafting.getSizeInventory() && x < details.getInputs().length; x++ ) + { + final IAEItemStack item = details.getInputs()[x]; + if (item != null && item.getItem() instanceof ItemFluidDrop) { + ItemStack packet = ItemFluidPacket.newStack(ItemFluidDrop.getFluidStack(item.getItemStack())); + this.crafting.setInventorySlotContents(x, packet); + } + else this.crafting.setInventorySlotContents( x, item == null ? null : item.getItemStack() ); + } + + for( int x = 0; x < this.output.getSizeInventory() && x < details.getOutputs().length; x++ ) + { + final IAEItemStack item = details.getOutputs()[x]; + if (item != null && item.getItem() instanceof ItemFluidDrop) { + ItemStack packet = ItemFluidPacket.newStack(ItemFluidDrop.getFluidStack(item.getItemStack())); + this.output.setInventorySlotContents(x, packet); + } + else this.output.setInventorySlotContents( x, item == null ? null : item.getItemStack() ); + } + } + this.getHost().markForSave(); + return; + } + } + onChangeInventory0(inv, slot, mc, removedStack, newStack); + } + + private void fixCraftingRecipes() + { + if( this.craftingMode ) + { + for( int x = 0; x < this.crafting.getSizeInventory(); x++ ) + { + final ItemStack is = this.crafting.getStackInSlot( x ); + if( is != null ) + { + is.stackSize = 1; + } + } + } + } + + public boolean isCraftingRecipe() + { + return this.craftingMode; + } + + public void setCraftingRecipe( final boolean craftingMode ) + { + this.craftingMode = craftingMode; + this.fixCraftingRecipes(); + } + + public boolean isSubstitution() + { + return this.substitute; + } + + public void setSubstitution( boolean canSubstitute ) + { + this.substitute = canSubstitute; + } + + public void onChangeCrafting(IAEItemStack[] newCrafting, IAEItemStack[] newOutput) { + IInventory crafting = this.getInventoryByName("crafting"); + IInventory output = this.getInventoryByName("output"); + if (crafting instanceof AppEngInternalInventory && output instanceof AppEngInternalInventory) { + for (int x = 0; x < crafting.getSizeInventory() && x < newCrafting.length; x++) { + final IAEItemStack item = newCrafting[x]; + crafting.setInventorySlotContents(x, item == null ? null: item.getItemStack()); + } + for (int x = 0; x < output.getSizeInventory() && x < newOutput.length; x++) { + final IAEItemStack item = newOutput[x]; + output.setInventorySlotContents(x, item == null ? null : item.getItemStack()); + } + } + } + + @Override + public IInventory getInventoryByName( final String name ) + { + if( name.equals( "crafting" ) ) + { + return this.crafting; + } + + if( name.equals( "output" ) ) + { + return this.output; + } + + if( name.equals( "pattern" ) ) + { + return this.pattern; + } + + return super.getInventoryByName( name ); + } + + @Override + public FCPartsTexture getFrontBright() { + return FRONT_BRIGHT_ICON; + } + + @Override + public FCPartsTexture getFrontColored() { + return FRONT_COLORED_ICON; + } + + @Override + public FCPartsTexture getFrontDark() { + return FRONT_DARK_ICON; + } + + @Override + public boolean isLightSource() { + return false; + } +} diff --git a/src/main/java/com/glodblock/github/common/tile/TileFluidDiscretizer.java b/src/main/java/com/glodblock/github/common/tile/TileFluidDiscretizer.java new file mode 100644 index 00000000..68f3aab9 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/tile/TileFluidDiscretizer.java @@ -0,0 +1,295 @@ +package com.glodblock.github.common.tile; + +import appeng.api.config.Actionable; +import appeng.api.networking.GridFlags; +import appeng.api.networking.crafting.ICraftingGrid; +import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.events.*; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.MachineSource; +import appeng.api.networking.storage.IBaseMonitor; +import appeng.api.networking.storage.IStorageGrid; +import appeng.api.storage.*; +import appeng.api.storage.data.IAEFluidStack; +import appeng.api.storage.data.IAEItemStack; +import appeng.api.storage.data.IAEStack; +import appeng.api.storage.data.IItemList; +import appeng.helpers.IPriorityHost; +import appeng.me.GridAccessException; +import appeng.me.cache.CraftingGridCache; +import appeng.me.storage.MEInventoryHandler; +import appeng.tile.grid.AENetworkTile; +import appeng.util.Platform; +import com.glodblock.github.common.item.ItemFluidDrop; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TileFluidDiscretizer extends AENetworkTile implements IPriorityHost, ICellContainer { + + private final BaseActionSource ownActionSource = new MachineSource(this); + private final FluidDiscretizingInventory fluidDropInv = new FluidDiscretizingInventory(); + private final FluidCraftingInventory fluidCraftInv = new FluidCraftingInventory(); + private boolean prevActiveState = false; + + public TileFluidDiscretizer() { + super(); + getProxy().setIdlePowerUsage(3D); + getProxy().setFlags(GridFlags.REQUIRE_CHANNEL); + } + + @Override + public boolean canBeRotated() { + return false; + //based + } + + @Override + @SuppressWarnings("rawtypes") + public List getCellArray(StorageChannel channel) { + /*conflict = !checkDiscreatizer(); + if (conflict) return Collections.emptyList();*/ + if (getProxy().isActive()) { + if (channel == StorageChannel.ITEMS) { + return Collections.singletonList(fluidDropInv.invHandler); + } else if (channel == StorageChannel.FLUIDS) { + return Collections.singletonList(fluidCraftInv.invHandler); + } + } + return Collections.emptyList(); + } + + @Override + public int getPriority() { + return Integer.MAX_VALUE; + } + + @Override + public void setPriority(int newValue) { + //do nothing + } + + @Override + public void blinkCell(int slot) { + //do nothing + } + + @Override + public void gridChanged() { + IMEMonitor fluidGrid = getFluidGrid(); + if (fluidGrid != null) { + fluidGrid.addListener(fluidDropInv, fluidGrid); + } + } + + @Override + public void saveChanges(IMEInventory cellInventory) { + markDirty(); + } + + private IMEMonitor getFluidGrid() { + try { + return getProxy().getGrid().getCache(IStorageGrid.class).getFluidInventory(); + } catch (GridAccessException e) { + return null; + } + } + + private IEnergyGrid getEnergyGrid() { + try { + return getProxy().getGrid().getCache(IEnergyGrid.class); + } catch (GridAccessException e) { + return null; + } + } + + private void updateState() { + boolean isActive = getProxy().isActive(); + if (isActive != prevActiveState) { + prevActiveState = isActive; + try { + getProxy().getGrid().postEvent(new MENetworkCellArrayUpdate()); + } catch (GridAccessException e) { + // NO-OP + } + } + } + + @MENetworkEventSubscribe + public void onPowerUpdate(MENetworkPowerStatusChange event) { + updateState(); + } + + @MENetworkEventSubscribe + public void onChannelUpdate(MENetworkChannelsChanged event) { + updateState(); + } + + @MENetworkEventSubscribe + public void onStorageUpdate(MENetworkStorageEvent event) { + updateState(); + } + + private boolean checkDiscreatizer() { + int amount = getProxy().getNode().getGrid().getMachines(this.getClass()).size(); + return amount <= 1; + } + + private class FluidDiscretizingInventory implements IMEInventory, IMEMonitorHandlerReceiver { + + private final MEInventoryHandler invHandler = new MEInventoryHandler<>(this, getChannel()); + private List itemCache = null; + + FluidDiscretizingInventory() { + invHandler.setPriority(Integer.MAX_VALUE); + } + + @Override + public IAEItemStack injectItems(IAEItemStack request, Actionable type, BaseActionSource src) { + /*if (conflict) return request;*/ + IAEFluidStack fluidStack = ItemFluidDrop.getAeFluidStack(request); + if (fluidStack == null) { + return request; + } + IMEMonitor fluidGrid = getFluidGrid(); + if (fluidGrid == null) { + return request; + } + IEnergyGrid energyGrid = getEnergyGrid(); + if (energyGrid == null) { + return request; + } + if (type == Actionable.SIMULATE) { + return ItemFluidDrop.newAeStack(fluidGrid.injectItems(fluidStack.copy(), Actionable.SIMULATE, src)); + } + else { + return ItemFluidDrop.newAeStack(Platform.poweredInsert(energyGrid, fluidGrid, fluidStack, ownActionSource)); + } + } + + @Override + public IAEItemStack extractItems(IAEItemStack request, Actionable mode, BaseActionSource src) { + /*if (conflict) return null;*/ + IAEFluidStack fluidStack = ItemFluidDrop.getAeFluidStack(request); + if (fluidStack == null) { + return null; + } + IMEMonitor fluidGrid = getFluidGrid(); + if (fluidGrid == null) { + return null; + } + IEnergyGrid energyGrid = getEnergyGrid(); + if (energyGrid == null) { + return null; + } + if (mode == Actionable.SIMULATE) { + return ItemFluidDrop.newAeStack(fluidGrid.extractItems(fluidStack.copy(), Actionable.SIMULATE, src)); + } + else { + return ItemFluidDrop.newAeStack(Platform.poweredExtraction(energyGrid, fluidGrid, fluidStack, ownActionSource)); + } + } + + @Override + public IItemList getAvailableItems(IItemList out) { + /*if (conflict) return out;*/ + if (itemCache == null) { + itemCache = new ArrayList<>(); + IMEMonitor fluidGrid = getFluidGrid(); + if (fluidGrid != null) { + for (IAEFluidStack fluid : fluidGrid.getStorageList()) { + IAEItemStack stack = ItemFluidDrop.newAeStack(fluid); + if (stack != null) { + itemCache.add(stack); + } + } + } + } + for (IAEItemStack stack : itemCache) { + out.addStorage(stack); + } + return out; + } + + @Override + public StorageChannel getChannel() { + return StorageChannel.ITEMS; + } + + @Override + public boolean isValid(Object verificationToken) { + IMEMonitor fluidGrid = getFluidGrid(); + return fluidGrid != null && fluidGrid == verificationToken /*&& !conflict*/; + } + + @Override + public void postChange(IBaseMonitor monitor, Iterable change, BaseActionSource actionSource) { + itemCache = null; + try { + List mappedChanges = new ArrayList<>(); + for (IAEFluidStack fluidStack : change) { + IAEItemStack itemStack = ItemFluidDrop.newAeStack(fluidStack); + if (itemStack != null) { + mappedChanges.add(itemStack); + } + } + getProxy().getGrid().getCache(IStorageGrid.class) + .postAlterationOfStoredItems(getChannel(), mappedChanges, ownActionSource); + } catch (GridAccessException e) { + // NO-OP + } + } + + @Override + public void onListUpdate() { + // NO-OP + } + } + + private class FluidCraftingInventory implements IMEInventory { + + private final MEInventoryHandler invHandler = new MEInventoryHandler<>(this, getChannel()); + + FluidCraftingInventory() { + invHandler.setPriority(Integer.MAX_VALUE); + } + + @Override + @SuppressWarnings("rawtypes") + public IAEFluidStack injectItems(IAEFluidStack input, Actionable type, BaseActionSource src) { + /*if (conflict) return null;*/ + ICraftingGrid craftingGrid; + try { + craftingGrid = getProxy().getGrid().getCache(ICraftingGrid.class); + } catch (GridAccessException e) { + return null; + } + if (craftingGrid instanceof CraftingGridCache) { + IAEStack remaining = ((CraftingGridCache)craftingGrid).injectItems( + ItemFluidDrop.newAeStack(input), type, ownActionSource); + if (remaining instanceof IAEItemStack) { + return ItemFluidDrop.getAeFluidStack((IAEItemStack) remaining); + } + } + return null; + } + + @Override + public IAEFluidStack extractItems(IAEFluidStack request, Actionable mode, BaseActionSource src) { + return null; + } + + @Override + public IItemList getAvailableItems(IItemList out) { + return out; + } + + @Override + public StorageChannel getChannel() { + return StorageChannel.FLUIDS; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/common/tile/TileFluidInterface.java b/src/main/java/com/glodblock/github/common/tile/TileFluidInterface.java new file mode 100644 index 00000000..200cf645 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/tile/TileFluidInterface.java @@ -0,0 +1,81 @@ +package com.glodblock.github.common.tile; + +import appeng.api.config.Actionable; +import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.MachineSource; +import appeng.api.networking.storage.IStorageGrid; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.data.IAEFluidStack; +import appeng.me.GridAccessException; +import appeng.tile.misc.TileInterface; +import appeng.util.Platform; +import appeng.util.item.AEFluidStack; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidHandler; + +public class TileFluidInterface extends TileInterface implements IFluidHandler +{ + + private final BaseActionSource ownActionSource = new MachineSource(this); + + private IMEMonitor getFluidGrid() { + try { + return getProxy().getGrid().getCache(IStorageGrid.class).getFluidInventory(); + } catch (GridAccessException e) { + return null; + } + } + + private IEnergyGrid getEnergyGrid() { + try { + return getProxy().getGrid().getCache(IEnergyGrid.class); + } catch (GridAccessException e) { + return null; + } + } + + @Override + public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { + IMEMonitor fluidGrid = getFluidGrid(); + IEnergyGrid energyGrid = getEnergyGrid(); + if (energyGrid == null || fluidGrid == null || resource == null) + return 0; + int ori = resource.amount; + IAEFluidStack remove; + if (doFill) { + remove = Platform.poweredInsert(energyGrid, fluidGrid, AEFluidStack.create(resource), ownActionSource); + } else { + remove = fluidGrid.injectItems(AEFluidStack.create(resource), Actionable.SIMULATE, ownActionSource); + } + return remove == null ? ori : (int) (ori - remove.getStackSize()); + } + + @Override + public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { + return null; + } + + @Override + public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { + return null; + } + + @Override + public boolean canFill(ForgeDirection from, Fluid fluid) { + return true; + } + + @Override + public boolean canDrain(ForgeDirection from, Fluid fluid) { + return false; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) { + return new FluidTankInfo[]{new FluidTankInfo(null, 0)}; + } +} diff --git a/src/main/java/com/glodblock/github/common/tile/TileFluidPacketDecoder.java b/src/main/java/com/glodblock/github/common/tile/TileFluidPacketDecoder.java new file mode 100644 index 00000000..0aea4e25 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/tile/TileFluidPacketDecoder.java @@ -0,0 +1,102 @@ +package com.glodblock.github.common.tile; + +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.security.BaseActionSource; +import appeng.api.networking.security.MachineSource; +import appeng.api.networking.storage.IStorageGrid; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.storage.IMEMonitor; +import appeng.api.storage.data.IAEFluidStack; +import appeng.helpers.Reflected; +import appeng.me.GridAccessException; +import appeng.tile.TileEvent; +import appeng.tile.events.TileEventType; +import appeng.tile.grid.AENetworkTile; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.inventory.IAEAppEngInventory; +import appeng.tile.inventory.InvOperation; +import appeng.util.Platform; +import appeng.util.item.AEFluidStack; +import com.glodblock.github.common.item.ItemFluidPacket; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +public class TileFluidPacketDecoder extends AENetworkTile implements IGridTickable, IAEAppEngInventory { + + private final AppEngInternalInventory inventory = new AppEngInternalInventory(this, 1); + private final BaseActionSource ownActionSource = new MachineSource(this); + + @Reflected + public TileFluidPacketDecoder() { + getProxy().setIdlePowerUsage(1D); + getProxy().setFlags(GridFlags.REQUIRE_CHANNEL); + } + + public IInventory getInventory() { + return inventory; + } + + @Override + public boolean canBeRotated() { + return false; + } + + @Override + public TickingRequest getTickingRequest(IGridNode node) { + return new TickingRequest(5, 120, false, true); + } + + @Override + public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) { + ItemStack stack = inventory.getStackInSlot(0); + if (stack == null || !(stack.getItem() instanceof ItemFluidPacket)) { + return TickRateModulation.SLEEP; + } + FluidStack fluid = ItemFluidPacket.getFluidStack(stack); + if (fluid == null || fluid.amount <= 0) { + inventory.setInventorySlotContents(0, null); + return TickRateModulation.SLEEP; + } + IAEFluidStack aeFluid = AEFluidStack.create(fluid); + IEnergyGrid energyGrid = node.getGrid().getCache(IEnergyGrid.class); + IMEMonitor fluidGrid = node.getGrid().getCache(IStorageGrid.class).getFluidInventory(); + IAEFluidStack remaining = Platform.poweredInsert(energyGrid, fluidGrid, aeFluid, ownActionSource); + if (remaining != null) { + if (remaining.getStackSize() == aeFluid.getStackSize()) { + return TickRateModulation.SLOWER; + } + inventory.setInventorySlotContents(0, ItemFluidPacket.newStack(remaining.getFluidStack())); + return TickRateModulation.FASTER; + } else { + inventory.setInventorySlotContents(0, null); + return TickRateModulation.SLEEP; + } + } + + @Override + public void onChangeInventory(IInventory inv, int slot, InvOperation mc, ItemStack removedStack, ItemStack newStack) { + try { + getProxy().getTick().alertDevice(getProxy().getNode()); + } catch (GridAccessException e) { + // NO-OP + } + } + + @TileEvent( TileEventType.WORLD_NBT_WRITE ) + public NBTTagCompound writeToNBTEvent(NBTTagCompound data) { + inventory.writeToNBT(data, "Inventory"); + return data; + } + + @TileEvent( TileEventType.WORLD_NBT_READ ) + public void readFromNBTEvent(NBTTagCompound data) { + inventory.readFromNBT(data, "Inventory"); + } + +} diff --git a/src/main/java/com/glodblock/github/common/tile/TileFluidPatternEncoder.java b/src/main/java/com/glodblock/github/common/tile/TileFluidPatternEncoder.java new file mode 100644 index 00000000..a0f1cac5 --- /dev/null +++ b/src/main/java/com/glodblock/github/common/tile/TileFluidPatternEncoder.java @@ -0,0 +1,77 @@ +package com.glodblock.github.common.tile; + +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEItemStack; +import appeng.tile.AEBaseTile; +import appeng.tile.TileEvent; +import appeng.tile.events.TileEventType; +import appeng.tile.inventory.AppEngInternalInventory; +import appeng.tile.inventory.IAEAppEngInventory; +import appeng.tile.inventory.InvOperation; +import com.glodblock.github.inventory.AeStackInventory; +import com.glodblock.github.inventory.AeStackInventoryImpl; +import com.glodblock.github.util.BlockPos; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; + +import java.util.List; + +public class TileFluidPatternEncoder extends AEBaseTile implements IAEAppEngInventory { + + private final AppEngInternalInventory patternInv = new AppEngInternalInventory(this, 2); + private final AeStackInventoryImpl crafting = new AeStackInventoryImpl<>(StorageChannel.ITEMS, 9, this); + private final AeStackInventoryImpl output = new AeStackInventoryImpl<>(StorageChannel.ITEMS, 3, this); + + public IInventory getInventory() { + return patternInv; + } + + public AeStackInventory getCraftingSlots() { + return crafting; + } + + public AeStackInventory getOutputSlots() { + return output; + } + + @Override + public boolean canBeRotated() { + return false; + } + + @Override + public void getDrops(final World w, final int x, final int y, final int z, final List drops ) { + getDrops(w, new BlockPos(x, y, z), drops); + } + + public void getDrops(World world, BlockPos pos, List drops) { + for (ItemStack stack : patternInv) { + if (stack != null) { + drops.add(stack); + } + } + } + + @TileEvent( TileEventType.WORLD_NBT_READ ) + public void readFromNBTEvent(NBTTagCompound data) { + patternInv.readFromNBT(data, "Inventory"); + crafting.readFromNbt(data, "CraftingSlots"); + output.readFromNbt(data, "OutputSlots"); + } + + @TileEvent( TileEventType.WORLD_NBT_WRITE ) + public NBTTagCompound writeToNBTEvent(NBTTagCompound data) { + patternInv.writeToNBT(data, "Inventory"); + crafting.writeToNbt(data, "CraftingSlots"); + output.writeToNbt(data, "OutputSlots"); + return data; + } + + @Override + public void onChangeInventory(IInventory inv, int slot, InvOperation mc, ItemStack removedStack, ItemStack newStack) { + + } + +} diff --git a/src/main/java/com/glodblock/github/coremod/FCClassTransformer.java b/src/main/java/com/glodblock/github/coremod/FCClassTransformer.java new file mode 100644 index 00000000..cd20951c --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/FCClassTransformer.java @@ -0,0 +1,59 @@ +package com.glodblock.github.coremod; + +import com.glodblock.github.coremod.transform.CraftingCpuTransformer; +import com.glodblock.github.coremod.transform.CraftingTreeNodeTransformer; +import com.glodblock.github.coremod.transform.DualityInterfaceTransformer; +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; + +public class FCClassTransformer implements IClassTransformer { + + @Override + public byte[] transform(String name, String transformedName, byte[] code) { + Transform tform; + switch (transformedName) { + case "appeng.crafting.CraftingTreeNode": + tform = CraftingTreeNodeTransformer.INSTANCE; + break; + case "appeng.me.cluster.implementations.CraftingCPUCluster": + tform = CraftingCpuTransformer.INSTANCE; + break; + case "appeng.helpers.DualityInterface": + tform = DualityInterfaceTransformer.INSTANCE; + break; + /*case "appeng.container.implementations.ContainerInterfaceTerminal": + tform = ContainerInterfaceTerminalTransformer.INSTANCE; + break;*/ //unfinished + default: + return code; + } + System.out.println("[FCAE2] 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/glodblock/github/coremod/FluidCraftCore.java b/src/main/java/com/glodblock/github/coremod/FluidCraftCore.java new file mode 100644 index 00000000..b79dd40d --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/FluidCraftCore.java @@ -0,0 +1,40 @@ +package com.glodblock.github.coremod; + +import cpw.mods.fml.relauncher.IFMLLoadingPlugin; + +import javax.annotation.Nullable; +import java.util.Map; + +@IFMLLoadingPlugin.MCVersion("1.7.10") +@IFMLLoadingPlugin.TransformerExclusions("com.glodblock.github.coremod") +public class FluidCraftCore implements IFMLLoadingPlugin { + + @Override + public String[] getASMTransformerClass() { + return new String[] { FluidCraftCore.class.getPackage().getName() + ".FCClassTransformer" }; + } + + @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/glodblock/github/coremod/hooker/CoreModHooks.java b/src/main/java/com/glodblock/github/coremod/hooker/CoreModHooks.java new file mode 100644 index 00000000..b66158d1 --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/hooker/CoreModHooks.java @@ -0,0 +1,87 @@ +package com.glodblock.github.coremod.hooker; + +import appeng.api.networking.IGridNode; +import appeng.api.networking.IMachineSet; +import appeng.api.storage.data.IAEItemStack; +import appeng.me.MachineSet; +import appeng.tile.misc.TileInterface; +import appeng.util.InventoryAdaptor; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.inventory.FluidConvertingInventoryAdaptor; +import com.glodblock.github.inventory.FluidConvertingInventoryCrafting; +import com.glodblock.github.loader.ItemAndBlockHolder; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.SetBackedMachineSet; +import com.glodblock.github.util.Util; +import com.google.common.collect.Sets; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.ForgeDirection; + +import javax.annotation.Nullable; +import java.util.HashSet; +import java.util.Set; + +public class CoreModHooks { + + public static InventoryCrafting wrapCraftingBuffer(InventoryCrafting inv) { + return new FluidConvertingInventoryCrafting(Ae2Reflect.getCraftContainer(inv), Ae2Reflect.getCraftWidth(inv), inv.getSizeInventory()/Ae2Reflect.getCraftWidth(inv)); + } + + public static IAEItemStack wrapFluidPacketStack(IAEItemStack stack) { + if (stack.getItem() == ItemAndBlockHolder.PACKET) { + IAEItemStack dropStack = ItemFluidDrop.newAeStack(ItemFluidPacket.getFluidStack(stack.getItemStack())); + if (dropStack != null) { + return dropStack; + } + } + return stack; + } + + @Nullable + public static InventoryAdaptor wrapInventory(@Nullable TileEntity tile, ForgeDirection face) { + return tile != null ? FluidConvertingInventoryAdaptor.wrap(tile, Util.from(face)) : null; + } + + public static long getCraftingByteCost(IAEItemStack stack) { + return stack.getItem() instanceof ItemFluidDrop + ? (long)Math.ceil(stack.getStackSize() / 1000D) : stack.getStackSize(); + } + + public static IAEItemStack[] flattenFluidPackets(IAEItemStack[] stacks) { + for (int i = 0; i < stacks.length; i++) { + if (stacks[i].getItem() instanceof ItemFluidPacket) { + stacks[i] = ItemFluidDrop.newAeStack(ItemFluidPacket.getFluidStack(stacks[i])); + } + } + return stacks; + } + + /*public static IMachineSet getMachines(IGrid grid, Class c) { + if (c == TileInterface.class) { + return unionMachineSets(grid.getMachines(c), grid.getMachines(TileDualInterface.class)); + } else if (c == PartInterface.class) { + return unionMachineSets(grid.getMachines(c), grid.getMachines(PartDualInterface.class)); + } else { + return grid.getMachines(c); + } + }*/ //unfinished + + private static IMachineSet unionMachineSets(IMachineSet a, IMachineSet b) { + if (a.isEmpty()) { + return b; + } else if (b.isEmpty()) { + return a; + } else if (a instanceof MachineSet && b instanceof MachineSet) { + return new SetBackedMachineSet(TileInterface.class, Sets.union((MachineSet)a, (MachineSet)b)); + } else { + Set union = new HashSet<>(); + a.forEach(union::add); + b.forEach(union::add); + return new SetBackedMachineSet(TileInterface.class, union); + } + } + +} diff --git a/src/main/java/com/glodblock/github/coremod/transform/ContainerInterfaceTerminalTransformer.java b/src/main/java/com/glodblock/github/coremod/transform/ContainerInterfaceTerminalTransformer.java new file mode 100644 index 00000000..a2cfd941 --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/transform/ContainerInterfaceTerminalTransformer.java @@ -0,0 +1,58 @@ +package com.glodblock.github.coremod.transform; + +import com.glodblock.github.coremod.FCClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class ContainerInterfaceTerminalTransformer extends FCClassTransformer.ClassMapper { + + public static final ContainerInterfaceTerminalTransformer INSTANCE = new ContainerInterfaceTerminalTransformer(); + + private ContainerInterfaceTerminalTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformContainerInterfaceTerminal(Opcodes.ASM5, downstream); + } + + private static class TransformContainerInterfaceTerminal extends ClassVisitor { + + TransformContainerInterfaceTerminal(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("detectAndSendChanges") || name.equals("func_75142_b") || name.equals("regenList")) { + return new TransformDetectAndSendChanges(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformDetectAndSendChanges extends MethodVisitor { + + TransformDetectAndSendChanges(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 + && owner.equals("appeng/api/networking/IGrid") && name.equals("getMachines")) { + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "com/glodblock/github/coremod/hooker/CoreModHooks", + "getMachines", + "(Lappeng/api/networking/IGrid;Ljava/lang/Class;)Lappeng/api/networking/IMachineSet;", + false); + } else { + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + } +} diff --git a/src/main/java/com/glodblock/github/coremod/transform/CraftingCpuTransformer.java b/src/main/java/com/glodblock/github/coremod/transform/CraftingCpuTransformer.java new file mode 100644 index 00000000..f65a9e61 --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/transform/CraftingCpuTransformer.java @@ -0,0 +1,86 @@ +package com.glodblock.github.coremod.transform; + +import com.glodblock.github.coremod.FCClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class CraftingCpuTransformer extends FCClassTransformer.ClassMapper { + + public static final CraftingCpuTransformer INSTANCE = new CraftingCpuTransformer(); + + private CraftingCpuTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformCraftingCPUCluster(Opcodes.ASM5, downstream); + } + + private static class TransformCraftingCPUCluster extends ClassVisitor { + + TransformCraftingCPUCluster(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("executeCrafting")) { + return new TransformExecuteCrafting(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformExecuteCrafting extends MethodVisitor { + + private boolean gotInventory = false; + + TransformExecuteCrafting(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + if (opcode == Opcodes.GETFIELD + && owner.equals("appeng/me/cluster/implementations/CraftingCPUCluster") && name.equals("inventory")) { + gotInventory = true; + } + super.visitFieldInsn(opcode, owner, name, desc); + } + + @Override + public void visitLineNumber(int line, Label start) { + gotInventory = false; + super.visitLineNumber(line, start); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + if (gotInventory) { + if (opcode == Opcodes.INVOKESTATIC + && owner.equals("appeng/util/item/AEItemStack") && name.equals("create")) { + gotInventory = false; + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "com/glodblock/github/coremod/hooker/CoreModHooks", + "wrapFluidPacketStack", + "(Lappeng/api/storage/data/IAEItemStack;)Lappeng/api/storage/data/IAEItemStack;", + false); + } + } else if (opcode == Opcodes.INVOKESPECIAL + && owner.equals("net/minecraft/inventory/InventoryCrafting") && name.equals("")) { + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "com/glodblock/github/coremod/hooker/CoreModHooks", + "wrapCraftingBuffer", + "(Lnet/minecraft/inventory/InventoryCrafting;)Lnet/minecraft/inventory/InventoryCrafting;", + false); + } + } + + } + +} diff --git a/src/main/java/com/glodblock/github/coremod/transform/CraftingTreeNodeTransformer.java b/src/main/java/com/glodblock/github/coremod/transform/CraftingTreeNodeTransformer.java new file mode 100644 index 00000000..901423af --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/transform/CraftingTreeNodeTransformer.java @@ -0,0 +1,77 @@ +package com.glodblock.github.coremod.transform; + +import com.glodblock.github.coremod.FCClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class CraftingTreeNodeTransformer extends FCClassTransformer.ClassMapper { + + public static final CraftingTreeNodeTransformer INSTANCE = new CraftingTreeNodeTransformer(); + + private CraftingTreeNodeTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformCraftingTreeNode(Opcodes.ASM5, downstream); + } + + private static class TransformCraftingTreeNode extends ClassVisitor { + + TransformCraftingTreeNode(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (name.equals("request")) { + return new TransformRequest(api, super.visitMethod(access, name, desc, signature, exceptions)); + } + return super.visitMethod(access, name, desc, signature, exceptions); + } + + } + + private static class TransformRequest extends MethodVisitor { + + private boolean writingBytes = false; + + TransformRequest(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + if (opcode == Opcodes.GETFIELD && owner.equals("appeng/crafting/CraftingTreeNode") && name.equals("bytes")) { + writingBytes = true; + } + super.visitFieldInsn(opcode, owner, name, desc); + } + + @Override + public void visitLineNumber(int line, Label start) { + writingBytes = false; // no write here + super.visitLineNumber(line, start); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (writingBytes && opcode == Opcodes.INVOKEINTERFACE + && owner.equals("appeng/api/storage/data/IAEItemStack") && name.equals("getStackSize")) { + writingBytes = false; + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "com/glodblock/github/coremod/hooker/CoreModHooks", + "getCraftingByteCost", + "(Lappeng/api/storage/data/IAEItemStack;)J", + false); + } else { + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + } + +} diff --git a/src/main/java/com/glodblock/github/coremod/transform/DualityInterfaceTransformer.java b/src/main/java/com/glodblock/github/coremod/transform/DualityInterfaceTransformer.java new file mode 100644 index 00000000..00cf5131 --- /dev/null +++ b/src/main/java/com/glodblock/github/coremod/transform/DualityInterfaceTransformer.java @@ -0,0 +1,62 @@ +package com.glodblock.github.coremod.transform; + +import com.glodblock.github.coremod.FCClassTransformer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +public class DualityInterfaceTransformer extends FCClassTransformer.ClassMapper { + + public static final DualityInterfaceTransformer INSTANCE = new DualityInterfaceTransformer(); + + private DualityInterfaceTransformer() { + // NO-OP + } + + @Override + protected ClassVisitor getClassMapper(ClassVisitor downstream) { + return new TransformDualityInterface(Opcodes.ASM5, downstream); + } + + private static class TransformDualityInterface extends ClassVisitor { + + TransformDualityInterface(int api, ClassVisitor cv) { + super(api, cv); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + switch (name) { + case "pushItemsOut": + case "pushPattern": + case "isBusy": + return new TransformInvAdaptorCalls(api, super.visitMethod(access, name, desc, signature, exceptions)); + default: + return super.visitMethod(access, name, desc, signature, exceptions); + } + } + + } + + private static class TransformInvAdaptorCalls extends MethodVisitor { + + TransformInvAdaptorCalls(int api, MethodVisitor mv) { + super(api, mv); + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (opcode == Opcodes.INVOKESTATIC && owner.equals("appeng/util/InventoryAdaptor") && name.equals("getAdaptor")) { + super.visitMethodInsn(Opcodes.INVOKESTATIC, + "com/glodblock/github/coremod/hooker/CoreModHooks", + "wrapInventory", + "(Lnet/minecraft/tileentity/TileEntity;Lnet/minecraftforge/common/util/ForgeDirection;)Lappeng/util/InventoryAdaptor;", + false); + } else { + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + } + + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/AEFluidInventory.java b/src/main/java/com/glodblock/github/inventory/AEFluidInventory.java new file mode 100644 index 00000000..b029b0b3 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/AEFluidInventory.java @@ -0,0 +1,411 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEFluidStack; +import appeng.core.AELog; +import appeng.util.Platform; +import appeng.util.item.AEFluidStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; + +import java.util.Objects; + +public class AEFluidInventory implements IAEFluidTank +{ + private final IAEFluidStack[] fluids; + private final IAEFluidInventory handler; + private final int capacity; + private FluidTankInfo[] props = null; + + public AEFluidInventory( final IAEFluidInventory handler, final int slots, final int capcity ) + { + this.fluids = new IAEFluidStack[slots]; + this.handler = handler; + this.capacity = capcity; + } + + public AEFluidInventory( final IAEFluidInventory handler, final int slots ) + { + this( handler, slots, Integer.MAX_VALUE ); + } + + @Override + public void setFluidInSlot( final int slot, final IAEFluidStack fluid ) + { + if( slot >= 0 && slot < this.getSlots() ) + { + if( Objects.equals( this.fluids[slot], fluid ) ) + { + if( fluid != null && fluid.getStackSize() != this.fluids[slot].getStackSize() ) + { + this.fluids[slot].setStackSize( Math.min( fluid.getStackSize(), this.capacity ) ); + this.onContentChanged( slot ); + } + } + else + { + if( fluid == null ) + { + this.fluids[slot] = null; + } + else + { + this.fluids[slot] = fluid.copy(); + this.fluids[slot].setStackSize( Math.min( fluid.getStackSize(), this.capacity ) ); + } + + this.onContentChanged( slot ); + } + } + } + + private void onContentChanged( final int slot ) + { + if( this.handler != null && Platform.isServer() ) + { + this.handler.onFluidInventoryChanged( this, slot ); + } + } + + @Override + public IAEFluidStack getFluidInSlot( final int slot ) + { + if( slot >= 0 && slot < this.getSlots() ) + { + return this.fluids[slot]; + } + return null; + } + + @Override + public int getSlots() + { + return this.fluids.length; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) + { + if( this.props == null ) + { + this.props = new FluidTankInfo[this.getSlots()]; + for( int i = 0; i < this.getSlots(); ++i ) + { + this.props[i] = new FluidTankInfo(new FluidTankPropertiesWrapper( i )); + } + + } + return this.props; + } + + public int fill( final int slot, final FluidStack resource, final boolean doFill ) + { + if( resource == null || resource.amount <= 0 ) + { + return 0; + } + + final IAEFluidStack fluid = this.fluids[slot]; + + if( fluid != null && !fluid.getFluidStack().isFluidEqual( resource ) ) + { + return 0; + } + + int amountToStore = this.capacity; + + if( fluid != null ) + { + amountToStore -= fluid.getStackSize(); + } + + amountToStore = Math.min( amountToStore, resource.amount ); + + if( doFill ) + { + if( fluid == null ) + { + this.setFluidInSlot( slot, AEFluidStack.create( resource ) ); + } + else + { + fluid.setStackSize( fluid.getStackSize() + amountToStore ); + this.onContentChanged( slot ); + } + } + + return amountToStore; + } + + public FluidStack drain( final int slot, final FluidStack resource, final boolean doDrain ) + { + final IAEFluidStack fluid = this.fluids[slot]; + if(fluid == null || !fluid.getFluidStack().equals(resource)) + { + return null; + } + return this.drain( slot, resource.amount, doDrain ); + } + + public FluidStack drain( final int slot, final int maxDrain, boolean doDrain ) + { + final IAEFluidStack fluid = this.fluids[slot]; + if( fluid == null || maxDrain <= 0 ) + { + return null; + } + + int drained = maxDrain; + if( fluid.getStackSize() < drained ) + { + drained = (int) fluid.getStackSize(); + } + + FluidStack stack = new FluidStack( fluid.getFluid(), drained ); + if( doDrain ) + { + fluid.setStackSize( fluid.getStackSize() - drained ); + if( fluid.getStackSize() <= 0 ) + { + this.fluids[slot] = null; + } + this.onContentChanged( slot ); + } + return stack; + } + + @Override + public int fill(ForgeDirection from, final FluidStack fluid, final boolean doFill ) + { + if( fluid == null || fluid.amount <= 0 ) + { + return 0; + } + + final FluidStack insert = fluid.copy(); + + int totalFillAmount = 0; + for( int slot = 0; slot < this.getSlots(); ++slot ) + { + int fillAmount = this.fill( slot, insert, doFill ); + totalFillAmount += fillAmount; + insert.amount -= fillAmount; + if( insert.amount <= 0 ) + { + break; + } + } + return totalFillAmount; + } + + @Override + public FluidStack drain(ForgeDirection from, final FluidStack fluid, final boolean doDrain ) + { + if( fluid == null || fluid.amount <= 0 ) + { + return null; + } + + final FluidStack resource = fluid.copy(); + + FluidStack totalDrained = null; + for( int slot = 0; slot < this.getSlots(); ++slot ) + { + FluidStack drain = this.drain( slot, resource, doDrain ); + if( drain != null ) + { + if( totalDrained == null ) + { + totalDrained = drain; + } + else + { + totalDrained.amount += drain.amount; + } + + resource.amount -= drain.amount; + if( resource.amount <= 0 ) + { + break; + } + } + } + return totalDrained; + } + + @Override + public FluidStack drain( ForgeDirection from, final int maxDrain, final boolean doDrain ) + { + if( maxDrain == 0 ) + { + return null; + } + + FluidStack totalDrained = null; + int toDrain = maxDrain; + + for( int slot = 0; slot < this.getSlots(); ++slot ) + { + if( totalDrained == null ) + { + totalDrained = this.drain( slot, toDrain, doDrain ); + if( totalDrained != null ) + { + toDrain -= totalDrained.amount; + } + } + else + { + FluidStack copy = totalDrained.copy(); + copy.amount = toDrain; + FluidStack drain = this.drain( slot, copy, doDrain ); + if( drain != null ) + { + totalDrained.amount += drain.amount; + toDrain -= drain.amount; + } + } + + if( toDrain <= 0 ) + { + break; + } + } + return totalDrained; + } + + @Override + public boolean canFill(ForgeDirection from, Fluid fluid) { + return fill(from, new FluidStack(fluid, 1), false) == 1; + } + + @Override + public boolean canDrain(ForgeDirection from, Fluid fluid) { + return drain(from, new FluidStack(fluid, 1), false) != null; + } + + public void writeToNBT( final NBTTagCompound data, final String name ) + { + final NBTTagCompound c = new NBTTagCompound(); + this.writeToNBT( c ); + data.setTag( name, c ); + } + + private void writeToNBT( final NBTTagCompound target ) + { + for( int x = 0; x < this.fluids.length; x++ ) + { + try + { + final NBTTagCompound c = new NBTTagCompound(); + + if( this.fluids[x] != null ) + { + this.fluids[x].writeToNBT( c ); + } + + target.setTag( "#" + x, c ); + } + catch( final Exception ignored ) + { + } + } + } + + public void readFromNBT( final NBTTagCompound data, final String name ) + { + final NBTTagCompound c = data.getCompoundTag( name ); + if( c != null ) + { + this.readFromNBT( c ); + } + } + + private void readFromNBT( final NBTTagCompound target ) + { + for( int x = 0; x < this.fluids.length; x++ ) + { + try + { + final NBTTagCompound c = target.getCompoundTag( "#" + x ); + + if( c != null ) + { + this.fluids[x] = AEFluidStack.loadFluidStackFromNBT( c ); + } + } + catch( final Exception e ) + { + AELog.debug( e ); + } + } + } + + private class FluidTankPropertiesWrapper implements IFluidTank + { + private final int slot; + + public FluidTankPropertiesWrapper( final int slot ) + { + this.slot = slot; + } + + @Override + public FluidStack getFluid() + { + return AEFluidInventory.this.fluids[this.slot] == null ? null : AEFluidInventory.this.fluids[this.slot].getFluidStack(); + } + + @Override + public int getFluidAmount() { + return getFluid() == null ? 0 : getFluid().amount; + } + + @Override + public int getCapacity() + { + return Math.min( AEFluidInventory.this.capacity, Integer.MAX_VALUE ); + } + + @Override + public FluidTankInfo getInfo() { + return null; + } + + @Override + public int fill(FluidStack resource, boolean doFill) { + if (resource == null || (getFluid() != null && !resource.equals(getFluid()))) return 0; + int acc = 0; + if (getFluid() == null) { + acc = Math.min(resource.amount, getCapacity()); + } + if (getFluid() != null) { + acc = Math.max(resource.amount, getCapacity() - getFluid().amount); + } + if (doFill) { + if (getFluid() == null) + AEFluidInventory.this.setFluidInSlot(this.slot, AEFluidStack.create(new FluidStack(resource.getFluid(), acc))); + else getFluid().amount += acc; + } + return acc; + } + + @Override + public FluidStack drain(int maxDrain, boolean doDrain) { + if (getFluid() == null) return null; + FluidStack remove = new FluidStack(getFluid().getFluid(), 1); + int amt = Math.min(maxDrain, getFluid().amount); + remove.amount = amt; + if (doDrain) { + if (amt == getFluid().amount) { + AEFluidInventory.this.setFluidInSlot(this.slot, null); + } + else getFluid().amount -= amt; + } + return remove; + } + } +} diff --git a/src/main/java/com/glodblock/github/inventory/AEFluidTank.java b/src/main/java/com/glodblock/github/inventory/AEFluidTank.java new file mode 100644 index 00000000..a2d2ea41 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/AEFluidTank.java @@ -0,0 +1,90 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEFluidStack; +import appeng.util.Platform; +import appeng.util.item.AEFluidStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTank; +import net.minecraftforge.fluids.FluidTankInfo; + +public class AEFluidTank extends FluidTank implements IAEFluidTank +{ + private final IAEFluidInventory host; + + public AEFluidTank( IAEFluidInventory host, int capacity ) + { + super( capacity ); + this.host = host; + if( host instanceof TileEntity) { + this.tile = (TileEntity) host; + } + } + + protected void onContentsChanged() + { + if( this.host != null && Platform.isServer() ) + { + this.host.onFluidInventoryChanged( this, 0 ); + } + } + + @Override + public void setFluidInSlot( int slot, IAEFluidStack fluid ) + { + if( slot == 0 ) + { + this.setFluid( fluid == null ? null : fluid.getFluidStack() ); + this.onContentsChanged(); + } + } + + @Override + public IAEFluidStack getFluidInSlot(int slot ) + { + if( slot == 0 ) + { + return AEFluidStack.create( this.getFluid() ); + } + return null; + } + + @Override + public int getSlots() + { + return 1; + } + + @Override + public int fill(ForgeDirection from, FluidStack resource, boolean doFill) { + return super.fill(resource, doFill); + } + + @Override + public FluidStack drain(ForgeDirection from, FluidStack resource, boolean doDrain) { + if (resource == null || !resource.equals(this.getFluid())) return null; + return super.drain(resource.amount, doDrain); + } + + @Override + public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) { + return super.drain(maxDrain, doDrain); + } + + @Override + public boolean canFill(ForgeDirection from, Fluid fluid) { + return super.fill(new FluidStack(fluid, 1), false) == 1; + } + + @Override + public boolean canDrain(ForgeDirection from, Fluid fluid) { + return drain(ForgeDirection.UNKNOWN, new FluidStack(fluid, 1), false) != null; + } + + @Override + public FluidTankInfo[] getTankInfo(ForgeDirection from) { + return new FluidTankInfo[]{super.getInfo()}; + } +} diff --git a/src/main/java/com/glodblock/github/inventory/AeItemStackHandler.java b/src/main/java/com/glodblock/github/inventory/AeItemStackHandler.java new file mode 100644 index 00000000..01305c0f --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/AeItemStackHandler.java @@ -0,0 +1,84 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEItemStack; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; + +public class AeItemStackHandler implements IInventory { + + private final AeStackInventory inv; + + public AeItemStackHandler(AeStackInventory inv) { + this.inv = inv; + } + + public AeStackInventory getAeInventory() { + return inv; + } + + @Override + public int getSizeInventory() { + return inv.getSlotCount(); + } + + public ItemStack getStackInSlot(int slot) { + IAEItemStack stack = inv.getStack(slot); + return stack != null ? stack.getItemStack() : null; + } + + @Override + public ItemStack decrStackSize(int p_70298_1_, int p_70298_2_) { + return null; + } + + @Override + public ItemStack getStackInSlotOnClosing(int p_70304_1_) { + return null; + } + + @Override + public void setInventorySlotContents(int p_70299_1_, ItemStack p_70299_2_) { + + } + + @Override + public String getInventoryName() { + return "AeItemStackHandler"; + } + + @Override + public boolean hasCustomInventoryName() { + return false; + } + + @Override + public int getInventoryStackLimit() { + return 64; + } + + @Override + public void markDirty() { + } + + @Override + public boolean isUseableByPlayer(EntityPlayer p_70300_1_) { + return false; + } + + @Override + public void openInventory() { + + } + + @Override + public void closeInventory() { + + } + + @Override + public boolean isItemValidForSlot(int p_94041_1_, ItemStack p_94041_2_) { + return false; + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/AeStackInventory.java b/src/main/java/com/glodblock/github/inventory/AeStackInventory.java new file mode 100644 index 00000000..2dba2d2c --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/AeStackInventory.java @@ -0,0 +1,19 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEStack; + +import javax.annotation.Nullable; +import java.util.stream.Stream; + +public interface AeStackInventory> extends Iterable { + + int getSlotCount(); + + @Nullable + T getStack(int slot); + + void setStack(int slot, @Nullable T stack); + + Stream stream(); + +} diff --git a/src/main/java/com/glodblock/github/inventory/AeStackInventoryImpl.java b/src/main/java/com/glodblock/github/inventory/AeStackInventoryImpl.java new file mode 100644 index 00000000..53bc590d --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/AeStackInventoryImpl.java @@ -0,0 +1,102 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.StorageChannel; +import appeng.api.storage.data.IAEStack; +import appeng.tile.inventory.IAEAppEngInventory; +import appeng.util.item.AEFluidStack; +import appeng.util.item.AEItemStack; +import com.glodblock.github.util.ObjectArrayIterator; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraftforge.common.util.Constants; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Iterator; +import java.util.stream.Stream; + +public class AeStackInventoryImpl > implements AeStackInventory { + + private final StorageChannel channel; + private final T[] inv; + private final IAEAppEngInventory owner; + + @SuppressWarnings("unchecked") + public AeStackInventoryImpl(StorageChannel channel, int slotCount, @Nullable IAEAppEngInventory owner) { + this.channel = channel; + this.inv = (T[])new IAEStack[slotCount]; + this.owner = owner; + } + + public AeStackInventoryImpl(StorageChannel channel, int slotCount) { + this(channel, slotCount, null); + } + + @Override + public int getSlotCount() { + return inv.length; + } + + @Override + @Nullable + public T getStack(int slot) { + return inv[slot]; + } + + @Override + public void setStack(int slot, @Nullable T stack) { + inv[slot] = stack; + if (owner != null) { + owner.saveChanges(); + } + } + + @Override + public Iterator iterator() { + return new ObjectArrayIterator<>(inv); + } + + @Override + public Stream stream() { + return Arrays.stream(inv); + } + + public void writeToNbt(NBTTagCompound tag) { + NBTTagList stacksTag = new NBTTagList(); + for (T stack : inv) { + if (stack == null) { + stacksTag.appendTag(new NBTTagCompound()); + } else { + NBTTagCompound stackTag = new NBTTagCompound(); + stack.writeToNBT(stackTag); + stacksTag.appendTag(stackTag); + } + } + tag.setTag("Contents", stacksTag); + } + + public void writeToNbt(NBTTagCompound parentTag, String key) { + NBTTagCompound tag = new NBTTagCompound(); + writeToNbt(tag); + parentTag.setTag(key, tag); + } + + @SuppressWarnings("unchecked") + public void readFromNbt(NBTTagCompound tag) { + NBTTagList stacksTag = tag.getTagList("Contents", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < inv.length; i++) { + NBTTagCompound stackTag = stacksTag.getCompoundTagAt(i); + if (channel == StorageChannel.ITEMS) { + inv[i] = stackTag == null ? null : (T) AEItemStack.loadItemStackFromNBT(stackTag); + } + else { + inv[i] = stackTag == null ? null : (T) AEFluidStack.loadFluidStackFromNBT(stackTag); + } + } + } + + public void readFromNbt(NBTTagCompound parentTag, String key) { + readFromNbt(parentTag.getCompoundTag(key)); + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryAdaptor.java b/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryAdaptor.java new file mode 100644 index 00000000..69e5c2e1 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryAdaptor.java @@ -0,0 +1,161 @@ +package com.glodblock.github.inventory; + +import appeng.api.config.FuzzyMode; +import appeng.util.InventoryAdaptor; +import appeng.util.inv.IInventoryDestination; +import appeng.util.inv.ItemSlot; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.tile.TileFluidInterface; +import com.glodblock.github.util.Ae2Reflect; +import com.glodblock.github.util.Util; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidHandler; + +import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Iterator; + +public class FluidConvertingInventoryAdaptor extends InventoryAdaptor { + + public static InventoryAdaptor wrap(TileEntity capProvider, EnumFacing face) { + // sometimes i wish 1.7.10 has cap system. + ForgeDirection f = Util.from(face); + TileEntity inter = capProvider.getWorldObj().getTileEntity(capProvider.xCoord + f.offsetX, capProvider.yCoord + f.offsetY, capProvider.zCoord + f.offsetZ); + if (!(inter instanceof TileFluidInterface)) return InventoryAdaptor.getAdaptor(capProvider, f); + IInventory item = capProvider instanceof IInventory ? (IInventory) capProvider : null; + IFluidHandler fluid = capProvider instanceof IFluidHandler ? (IFluidHandler) capProvider : null; + return new FluidConvertingInventoryAdaptor(item, fluid, face); + } + + private final InventoryAdaptor invItems; + private final IFluidHandler invFluids; + private final ForgeDirection side; + + public FluidConvertingInventoryAdaptor(@Nullable IInventory invItems, @Nullable IFluidHandler invFluids, EnumFacing facing) { + this.invItems = invItems != null ? InventoryAdaptor.getAdaptor(invItems, Util.from(facing)) : null; + this.invFluids = invFluids; + this.side = Util.from(facing); + } + + @Override + public ItemStack addItems(ItemStack toBeAdded) { + if (toBeAdded.getItem() instanceof ItemFluidPacket) { + if (invFluids != null) { + FluidStack fluid = ItemFluidPacket.getFluidStack(toBeAdded); + if (fluid != null) { + int filled = invFluids.fill(side, fluid, true); + if (filled > 0) { + fluid.amount -= filled; + return ItemFluidPacket.newStack(fluid); + } + } + } + return toBeAdded; + } + return invItems != null ? invItems.addItems(toBeAdded) : toBeAdded; + } + + @Override + public ItemStack simulateAdd(ItemStack toBeSimulated) { + if (toBeSimulated.getItem() instanceof ItemFluidPacket) { + if (invFluids != null) { + FluidStack fluid = ItemFluidPacket.getFluidStack(toBeSimulated); + if (fluid != null) { + int filled = invFluids.fill(side, fluid, false); + if (filled > 0) { + fluid.amount -= filled; + return ItemFluidPacket.newStack(fluid); + } + } + } + return toBeSimulated; + } + return invItems != null ? invItems.simulateAdd(toBeSimulated) : toBeSimulated; + } + + @Override + public ItemStack removeItems(int amount, ItemStack filter, IInventoryDestination destination) { + return invItems != null ? invItems.removeItems(amount, filter, destination) : null; + } + + @Override + public ItemStack simulateRemove(int amount, ItemStack filter, IInventoryDestination destination) { + return invItems != null ? invItems.simulateRemove(amount, filter, destination) : null; + } + + @Override + public ItemStack removeSimilarItems(int amount, ItemStack filter, FuzzyMode fuzzyMode, IInventoryDestination destination) { + return invItems != null ? invItems.removeSimilarItems(amount, filter, fuzzyMode, destination) : null; + } + + @Override + public ItemStack simulateSimilarRemove(int amount, ItemStack filter, FuzzyMode fuzzyMode, IInventoryDestination destination) { + return invItems != null ? invItems.simulateSimilarRemove(amount, filter, fuzzyMode, destination) : null; + } + + @Override + public boolean containsItems() { + if (invFluids != null) { + for (FluidTankInfo tank : invFluids.getTankInfo(side)) { + FluidStack fluid = tank.fluid; + if (fluid != null && fluid.amount > 0) { + return true; + } + } + } + return invItems != null && invItems.containsItems(); + } + + public boolean hasSlots() { + return (invFluids != null && invFluids.getTankInfo(side).length > 0) + || (invItems != null); + } + + @Override + public Iterator iterator() { + return new SlotIterator( + invFluids != null ? invFluids.getTankInfo(side) : new FluidTankInfo[0], + invItems != null ? invItems.iterator() : Collections.emptyIterator()); + } + + private static class SlotIterator implements Iterator { + + private final FluidTankInfo[] tanks; + private final Iterator itemSlots; + private int nextSlotIndex = 0; + + SlotIterator(FluidTankInfo[] tanks, Iterator itemSlots) { + this.tanks = tanks; + this.itemSlots = itemSlots; + } + + @Override + public boolean hasNext() { + return nextSlotIndex < tanks.length || itemSlots.hasNext(); + } + + @Override + public ItemSlot next() { + if (nextSlotIndex < tanks.length) { + FluidStack fluid = tanks[nextSlotIndex].fluid; + ItemSlot slot = new ItemSlot(); + slot.setSlot(nextSlotIndex++); + slot.setItemStack(fluid != null ? ItemFluidPacket.newStack(fluid) : null); + Ae2Reflect.setItemSlotExtractable(slot, false); + return slot; + } else { + ItemSlot slot = itemSlots.next(); + slot.setSlot(nextSlotIndex++); + return slot; + } + } + + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryCrafting.java b/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryCrafting.java new file mode 100644 index 00000000..48bf3156 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/FluidConvertingInventoryCrafting.java @@ -0,0 +1,32 @@ +package com.glodblock.github.inventory; + +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidPacket; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +public class FluidConvertingInventoryCrafting extends InventoryCrafting { + + public FluidConvertingInventoryCrafting(Container container, int width, int height) { + super(container, width, height); + } + + @Override + public void setInventorySlotContents(int index, ItemStack stack) { + if (stack.getItem() instanceof ItemFluidDrop) { + FluidStack fluid = ItemFluidDrop.getFluidStack(stack); + if (fluid != null) { + super.setInventorySlotContents(index, ItemFluidPacket.newStack(new FluidStack(fluid, stack.stackSize))); + } else { + // wtf? + super.setInventorySlotContents(index, ItemFluidPacket.newStack(new FluidStack(FluidRegistry.WATER, 1000))); + } + } else { + super.setInventorySlotContents(index, stack); + } + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/IAEFluidInventory.java b/src/main/java/com/glodblock/github/inventory/IAEFluidInventory.java new file mode 100644 index 00000000..79ed92c7 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/IAEFluidInventory.java @@ -0,0 +1,7 @@ +package com.glodblock.github.inventory; + +@FunctionalInterface +public interface IAEFluidInventory +{ + void onFluidInventoryChanged( final IAEFluidTank inv, final int slot ); +} diff --git a/src/main/java/com/glodblock/github/inventory/IAEFluidTank.java b/src/main/java/com/glodblock/github/inventory/IAEFluidTank.java new file mode 100644 index 00000000..8fde96a1 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/IAEFluidTank.java @@ -0,0 +1,13 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEFluidStack; +import net.minecraftforge.fluids.IFluidHandler; + +public interface IAEFluidTank extends IFluidHandler { + void setFluidInSlot( final int slot, final IAEFluidStack fluid ); + + IAEFluidStack getFluidInSlot( final int slot ); + + int getSlots(); + +} diff --git a/src/main/java/com/glodblock/github/inventory/InventoryHandler.java b/src/main/java/com/glodblock/github/inventory/InventoryHandler.java new file mode 100644 index 00000000..91193715 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/InventoryHandler.java @@ -0,0 +1,51 @@ +package com.glodblock.github.inventory; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.network.CPacketSwitchGuis; +import com.glodblock.github.util.BlockPos; +import cpw.mods.fml.common.network.IGuiHandler; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public class InventoryHandler implements IGuiHandler { + + public static void switchGui(GuiType guiType) { + FluidCraft.proxy.netHandler.sendToServer(new CPacketSwitchGuis(guiType)); + } + + public static void openGui(EntityPlayer player, World world, BlockPos pos, EnumFacing face, GuiType guiType) { + player.openGui(FluidCraft.INSTANCE, + (guiType.ordinal() << 3) | face.ordinal(), world, pos.getX(), pos.getY(), pos.getZ()); + } + + @Nullable + @Override + public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { + int faceOrd = id & 0x7; + if (faceOrd > EnumFacing.values().length) { + return null; + } + EnumFacing face = EnumFacing.getFront(faceOrd); + GuiType type = GuiType.getByOrdinal(id >>> 3); + return type != null ? type.guiFactory.createServerGui(player, world, x, y, z, face) : null; + } + + @SideOnly(Side.CLIENT) + @Nullable + @Override + public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { + int faceOrd = id & 0x7; + if (faceOrd > EnumFacing.values().length) { + return null; + } + EnumFacing face = EnumFacing.getFront(faceOrd); + GuiType type = GuiType.getByOrdinal(id >>> 3); + return type != null ? type.guiFactory.createClientGui(player, world, x, y, z, face) : null; + } +} diff --git a/src/main/java/com/glodblock/github/inventory/PatternConsumer.java b/src/main/java/com/glodblock/github/inventory/PatternConsumer.java new file mode 100644 index 00000000..ae8ce6d8 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/PatternConsumer.java @@ -0,0 +1,9 @@ +package com.glodblock.github.inventory; + +import appeng.api.storage.data.IAEItemStack; + +public interface PatternConsumer { + + void acceptPattern(IAEItemStack[] inputs, IAEItemStack[] outputs); + +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/GuiFactory.java b/src/main/java/com/glodblock/github/inventory/gui/GuiFactory.java new file mode 100644 index 00000000..df59218e --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/gui/GuiFactory.java @@ -0,0 +1,20 @@ +package com.glodblock.github.inventory.gui; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public interface GuiFactory { + + @Nullable + Object createServerGui(EntityPlayer player, World world, int x, int y, int z, EnumFacing face); + + @SideOnly(Side.CLIENT) + @Nullable + Object createClientGui(EntityPlayer player, World world, int x, int y, int z, EnumFacing face); + +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/GuiType.java b/src/main/java/com/glodblock/github/inventory/gui/GuiType.java new file mode 100644 index 00000000..b8e69fa3 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/gui/GuiType.java @@ -0,0 +1,107 @@ +package com.glodblock.github.inventory.gui; + +import appeng.api.storage.ITerminalHost; +import appeng.container.implementations.ContainerCraftAmount; +import appeng.container.implementations.ContainerCraftingStatus; +import com.glodblock.github.client.gui.*; +import com.glodblock.github.client.gui.container.ContainerFluidCraftConfirm; +import com.glodblock.github.client.gui.container.ContainerFluidPacketDecoder; +import com.glodblock.github.client.gui.container.ContainerFluidPatternEncoder; +import com.glodblock.github.client.gui.container.ContainerFluidPatternTerminal; +import com.glodblock.github.common.parts.PartFluidPatternTerminal; +import com.glodblock.github.common.tile.TileFluidPacketDecoder; +import com.glodblock.github.common.tile.TileFluidPatternEncoder; +import com.google.common.collect.ImmutableList; +import net.minecraft.entity.player.EntityPlayer; + +import javax.annotation.Nullable; +import java.util.List; + +public enum GuiType { + + FLUID_PAT_TERM_CRAFTING_STATUS(new PartGuiFactory(PartFluidPatternTerminal.class) { + @Override + protected Object createServerGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new ContainerCraftingStatus(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new GuiFluidPatternTerminalCraftingStatus(player.inventory, inv); + } + }), + + FLUID_PATTERN_TERMINAL(new PartGuiFactory(ITerminalHost.class) { + @Override + protected Object createServerGui(EntityPlayer player, ITerminalHost inv) { + return new ContainerFluidPatternTerminal(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, ITerminalHost inv) { + return new GuiFluidPatternTerminal(player.inventory, inv); + } + }), + + FLUID_PATTERN_ENCODER(new TileGuiFactory(TileFluidPatternEncoder.class) { + @Override + protected Object createServerGui(EntityPlayer player, TileFluidPatternEncoder inv) { + return new ContainerFluidPatternEncoder(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, TileFluidPatternEncoder inv) { + return new GuiFluidPatternEncoder(player.inventory, inv); + } + }), + + FLUID_CRAFTING_CONFIRM(new PartGuiFactory(PartFluidPatternTerminal.class) { + @Override + protected Object createServerGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new ContainerFluidCraftConfirm(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new GuiFluidCraftConfirm(player.inventory, inv); + } + }), + + FLUID_CRAFTING_AMOUNT(new PartGuiFactory(PartFluidPatternTerminal.class) { + @Override + protected Object createServerGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new ContainerCraftAmount(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, PartFluidPatternTerminal inv) { + return new GuiFluidCraftAmount(player.inventory, inv); + } + }), + + FLUID_PACKET_DECODER(new TileGuiFactory(TileFluidPacketDecoder.class) { + @Override + protected Object createServerGui(EntityPlayer player, TileFluidPacketDecoder inv) { + return new ContainerFluidPacketDecoder(player.inventory, inv); + } + + @Override + protected Object createClientGui(EntityPlayer player, TileFluidPacketDecoder inv) { + return new GuiFluidPacketDecoder(player.inventory, inv); + } + }); + + public static final List VALUES = ImmutableList.copyOf(values()); + + @Nullable + public static GuiType getByOrdinal(int ordinal) { + return ordinal < 0 || ordinal >= VALUES.size() ? null : VALUES.get(ordinal); + } + + public final GuiFactory guiFactory; + + GuiType(GuiFactory guiFactory) { + this.guiFactory = guiFactory; + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/MouseRegionManager.java b/src/main/java/com/glodblock/github/inventory/gui/MouseRegionManager.java new file mode 100644 index 00000000..e87bef8b --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/gui/MouseRegionManager.java @@ -0,0 +1,84 @@ +package com.glodblock.github.inventory.gui; + +import com.glodblock.github.util.Ae2Reflect; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.util.ResourceLocation; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class MouseRegionManager { + private final GuiContainer gui; + private final List regions = new ArrayList<>(); + private final FontRenderer font; + + public MouseRegionManager(GuiContainer gui, FontRenderer font) { + this.gui = gui; + this.font = font; + } + + public void addRegion(int x, int y, int width, int height, Handler handler) { + regions.add(new Region(x, y, width, height, handler)); + } + + public boolean onClick(int mX, int mY, int button) { + mX -= Ae2Reflect.getGuiLeft(gui); + mY -= Ae2Reflect.getGuiTop(gui); + for (Region region : regions) { + if (region.containsMouse(mX, mY) && region.handler.onClick(button)) { + gui.mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 1F)); + return false; + } + } + return true; + } + + public void render(int mX, int mY) { + mX -= Ae2Reflect.getGuiLeft(gui); + mY -= Ae2Reflect.getGuiTop(gui); + for (Region region : regions) { + if (region.containsMouse(mX, mY)) { + List tooltip = region.handler.getTooltip(); + if (tooltip != null) { + return; + } + } + } + } + + private static class Region { + + private final int x, y, width, height; + private final Handler handler; + + Region(int x, int y, int width, int height, Handler handler) { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + this.handler = handler; + } + + boolean containsMouse(int mX, int mY) { + return mX >= x && mX < x + width && mY >= y && mY < y + height; + } + + } + + public interface Handler { + + @Nullable + default List getTooltip() { + return null; + } + + default boolean onClick(int button) { + return false; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/PartGuiFactory.java b/src/main/java/com/glodblock/github/inventory/gui/PartGuiFactory.java new file mode 100644 index 00000000..11e5a725 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/gui/PartGuiFactory.java @@ -0,0 +1,28 @@ +package com.glodblock.github.inventory.gui; + +import appeng.api.parts.IPart; +import appeng.api.parts.IPartHost; +import com.glodblock.github.util.Util; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; + +import javax.annotation.Nullable; + +public abstract class PartGuiFactory extends TileGuiFactory { + + PartGuiFactory(Class invClass) { + super(invClass); + } + + @Nullable + @Override + protected T getInventory(TileEntity tile, EnumFacing face) { + if (tile instanceof IPartHost) { + IPart part = ((IPartHost)tile).getPart(Util.from(face)); + if (invClass.isInstance(part)) { + return invClass.cast(part); + } + } + return null; + } +} diff --git a/src/main/java/com/glodblock/github/inventory/gui/TileGuiFactory.java b/src/main/java/com/glodblock/github/inventory/gui/TileGuiFactory.java new file mode 100644 index 00000000..4ac4eab1 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/gui/TileGuiFactory.java @@ -0,0 +1,66 @@ +package com.glodblock.github.inventory.gui; + +import appeng.container.AEBaseContainer; +import appeng.container.ContainerOpenContext; +import com.glodblock.github.util.Util; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.world.World; + +import javax.annotation.Nullable; + +public abstract class TileGuiFactory implements GuiFactory { + + protected final Class invClass; + + TileGuiFactory(Class invClass) { + this.invClass = invClass; + } + + @Nullable + protected T getInventory(TileEntity tile, EnumFacing face) { + return invClass.isInstance(tile) ? invClass.cast(tile) : null; + } + + @Nullable + @Override + public Object createServerGui(EntityPlayer player, World world, int x, int y, int z, EnumFacing face) { + TileEntity tile = world.getTileEntity(x, y, z); + if (tile == null) { + return null; + } + T inv = getInventory(tile, face); + if (inv == null) { + return null; + } + Object gui = createServerGui(player, inv); + if (gui instanceof AEBaseContainer) { + ContainerOpenContext ctx = new ContainerOpenContext(inv); + ctx.setWorld(world); + ctx.setX(x); + ctx.setY(y); + ctx.setZ(z); + ctx.setSide(Util.from(face)); + ((AEBaseContainer)gui).setOpenContext(ctx); + } + return gui; + } + + @Nullable + protected abstract Object createServerGui(EntityPlayer player, T inv); + + @Nullable + @Override + public Object createClientGui(EntityPlayer player, World world, int x, int y, int z, EnumFacing face) { + TileEntity tile = world.getTileEntity(x, y, z); + if (tile == null) { + return null; + } + T inv = getInventory(tile, face); + return inv != null ? createClientGui(player, inv) : null; + } + + @Nullable + protected abstract Object createClientGui(EntityPlayer player, T inv); +} diff --git a/src/main/java/com/glodblock/github/inventory/slot/SlotFluid.java b/src/main/java/com/glodblock/github/inventory/slot/SlotFluid.java new file mode 100644 index 00000000..9390a301 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/slot/SlotFluid.java @@ -0,0 +1,15 @@ +package com.glodblock.github.inventory.slot; + +import appeng.api.storage.data.IAEItemStack; + +import javax.annotation.Nullable; + +public interface SlotFluid { + + @Nullable + IAEItemStack getAeStack(); + + void setAeStack(@Nullable IAEItemStack stack, boolean sync); + + +} diff --git a/src/main/java/com/glodblock/github/inventory/slot/SlotSingleItem.java b/src/main/java/com/glodblock/github/inventory/slot/SlotSingleItem.java new file mode 100644 index 00000000..f664ec39 --- /dev/null +++ b/src/main/java/com/glodblock/github/inventory/slot/SlotSingleItem.java @@ -0,0 +1,102 @@ +package com.glodblock.github.inventory.slot; + +import com.glodblock.github.util.Util; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +public class SlotSingleItem extends Slot { + + private final Slot delegate; + + public SlotSingleItem(Slot delegate) { + super(delegate.inventory, delegate.getSlotIndex(), delegate.xDisplayPosition, delegate.yDisplayPosition); + this.delegate = delegate; + } + + @Override + public ItemStack getStack() { + ItemStack stack = delegate.getStack(); + return stack == null ? null : Util.copyStackWithSize(stack, 1); + } + + // delegated + + @Override + public void onSlotChange(ItemStack p_75220_1_, ItemStack p_75220_2_) { + delegate.onSlotChange(p_75220_1_, p_75220_2_); + } + + @Override + public void onPickupFromSlot(EntityPlayer thePlayer, ItemStack stack) { + delegate.onPickupFromSlot(thePlayer, stack); + } + + @Override + public boolean isItemValid(ItemStack stack) { + return delegate.isItemValid(stack); + } + + @Override + public boolean getHasStack() { + return delegate.getHasStack(); + } + + @Override + public void putStack(ItemStack stack) { + delegate.putStack(stack); + } + + @Override + public void onSlotChanged() { + delegate.onSlotChanged(); + } + + @Override + public int getSlotStackLimit() { + return delegate.getSlotStackLimit(); + } + + @Override + public ItemStack decrStackSize(int amount) { + return delegate.decrStackSize(amount); + } + + @Override + public boolean isSlotInInventory(IInventory inv, int slotIn) { + return delegate.isSlotInInventory(inv, slotIn); + } + + @Override + public boolean canTakeStack(EntityPlayer playerIn) { + return delegate.canTakeStack(playerIn); + } + + @Override + @SideOnly(Side.CLIENT) + public boolean func_111238_b() { + return delegate.func_111238_b(); + } + + @Override + @SideOnly(Side.CLIENT) + public ResourceLocation getBackgroundIconTexture() { + return delegate.getBackgroundIconTexture(); + } + + @Override + @SideOnly(Side.CLIENT) + public void setBackgroundIconTexture(ResourceLocation texture) { + delegate.setBackgroundIconTexture(texture); + } + + @Override + public int getSlotIndex() { + return delegate.getSlotIndex(); + } + +} diff --git a/src/main/java/com/glodblock/github/loader/ChannelLoader.java b/src/main/java/com/glodblock/github/loader/ChannelLoader.java new file mode 100644 index 00000000..317a7f5c --- /dev/null +++ b/src/main/java/com/glodblock/github/loader/ChannelLoader.java @@ -0,0 +1,22 @@ +package com.glodblock.github.loader; + +import com.glodblock.github.FluidCraft; +import com.glodblock.github.network.*; +import cpw.mods.fml.relauncher.Side; + +public class ChannelLoader implements Runnable { + @Override + public void run() { + int id = 0; + FluidCraft.proxy.netHandler.registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketFluidPatternTermBtns.Handler(), CPacketFluidPatternTermBtns.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketEncodePattern.Handler(), CPacketEncodePattern.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new SPacketMEInventoryUpdate.Handler(), SPacketMEInventoryUpdate.class, id ++, Side.CLIENT); + FluidCraft.proxy.netHandler.registerMessage(new CPacketCraftRequest.Handler(), CPacketCraftRequest.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketInventoryAction.Handler(), CPacketInventoryAction.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketSetTargetItem.Handler(), CPacketSetTargetItem.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketSwitchGuis.Handler(), CPacketSwitchGuis.class, id ++, Side.CLIENT); + FluidCraft.proxy.netHandler.registerMessage(new SPacketFluidSlot.Handler(), SPacketFluidSlot.class, id ++, Side.SERVER); + FluidCraft.proxy.netHandler.registerMessage(new CPacketTransferRecipe.Handler(), CPacketTransferRecipe.class, id ++, Side.SERVER); + } +} diff --git a/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java b/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java new file mode 100644 index 00000000..67d1d1ba --- /dev/null +++ b/src/main/java/com/glodblock/github/loader/ItemAndBlockHolder.java @@ -0,0 +1,24 @@ +package com.glodblock.github.loader; + +import com.glodblock.github.common.block.BlockFluidDiscretizer; +import com.glodblock.github.common.block.BlockFluidInterface; +import com.glodblock.github.common.block.BlockFluidPacketDecoder; +import com.glodblock.github.common.block.BlockFluidPatternEncoder; +import com.glodblock.github.common.item.ItemFluidDrop; +import com.glodblock.github.common.item.ItemFluidEncodedPattern; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.common.item.ItemPartFluidPatternTerminal; + +public class ItemAndBlockHolder { + + public static ItemFluidDrop DROP = new ItemFluidDrop().register(); + public static ItemFluidEncodedPattern PATTERN = new ItemFluidEncodedPattern().register(); + public static ItemPartFluidPatternTerminal FLUID_TERMINAL = new ItemPartFluidPatternTerminal().register(); + public static ItemFluidPacket PACKET = new ItemFluidPacket().register(); + + public static BlockFluidDiscretizer DISCRETIZER = new BlockFluidDiscretizer().register(); + public static BlockFluidPatternEncoder ENCODER = new BlockFluidPatternEncoder().register(); + public static BlockFluidPacketDecoder DECODER = new BlockFluidPacketDecoder().register(); + public static BlockFluidInterface INTERFACE = new BlockFluidInterface().register(); + +} diff --git a/src/main/java/com/glodblock/github/loader/ListenerLoader.java b/src/main/java/com/glodblock/github/loader/ListenerLoader.java new file mode 100644 index 00000000..16fd725f --- /dev/null +++ b/src/main/java/com/glodblock/github/loader/ListenerLoader.java @@ -0,0 +1,12 @@ +package com.glodblock.github.loader; + +import com.glodblock.github.client.ClientHelper; + +public class ListenerLoader implements Runnable { + + @Override + public void run() { + ClientHelper.register(); + } + +} diff --git a/src/main/java/com/glodblock/github/loader/RenderLoader.java b/src/main/java/com/glodblock/github/loader/RenderLoader.java new file mode 100644 index 00000000..8ecf61b2 --- /dev/null +++ b/src/main/java/com/glodblock/github/loader/RenderLoader.java @@ -0,0 +1,14 @@ +package com.glodblock.github.loader; + +import com.glodblock.github.client.render.ItemDropRender; +import com.glodblock.github.client.render.ItemPacketRender; + +public class RenderLoader implements Runnable { + + @Override + public void run() { + new ItemDropRender(); + new ItemPacketRender(); + } + +} diff --git a/src/main/java/com/glodblock/github/nei/FluidPatternTerminalRecipeTransferHandler.java b/src/main/java/com/glodblock/github/nei/FluidPatternTerminalRecipeTransferHandler.java new file mode 100644 index 00000000..ea2b5ebb --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/FluidPatternTerminalRecipeTransferHandler.java @@ -0,0 +1,50 @@ +package com.glodblock.github.nei; + +import codechicken.nei.api.IOverlayHandler; +import codechicken.nei.recipe.IRecipeHandler; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.GuiFluidPatternTerminal; +import com.glodblock.github.nei.object.OrderStack; +import com.glodblock.github.nei.recipes.FluidRecipe; +import com.glodblock.github.network.CPacketTransferRecipe; +import net.minecraft.client.gui.inventory.GuiContainer; + +import java.util.HashSet; +import java.util.List; + +public class FluidPatternTerminalRecipeTransferHandler implements IOverlayHandler { + + public static final FluidPatternTerminalRecipeTransferHandler INSTANCE = new FluidPatternTerminalRecipeTransferHandler(); + public static final HashSet notOtherSet = new HashSet<>(); + public static final HashSet craftSet = new HashSet<>(); + + static { + notOtherSet.add("smelting"); + notOtherSet.add("brewing"); + craftSet.add("crafting"); + craftSet.add("crafting2x2"); + } + + @Override + public void overlayRecipe(GuiContainer firstGui, IRecipeHandler recipe, int recipeIndex, boolean shift) { + if (isCorrectGui(firstGui)) { + List> in = FluidRecipe.getPackageInputs(recipe, recipeIndex); + List> out = FluidRecipe.getPackageOutputs(recipe, recipeIndex, !notUseOther(recipe)); + boolean craft = shouldCraft(recipe); + FluidCraft.proxy.netHandler.sendToServer(new CPacketTransferRecipe(in, out, craft)); + } + } + + private boolean isCorrectGui(GuiContainer firstGui) { + return firstGui instanceof GuiFluidPatternTerminal; + } + + private boolean notUseOther(IRecipeHandler recipeHandler) { + return notOtherSet.contains(recipeHandler.getOverlayIdentifier()); + } + + private boolean shouldCraft(IRecipeHandler recipeHandler) { + return craftSet.contains(recipeHandler.getOverlayIdentifier()); + } + +} diff --git a/src/main/java/com/glodblock/github/nei/NEI_FC_Config.java b/src/main/java/com/glodblock/github/nei/NEI_FC_Config.java new file mode 100644 index 00000000..1b327f05 --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/NEI_FC_Config.java @@ -0,0 +1,27 @@ +package com.glodblock.github.nei; + +import codechicken.nei.api.API; +import codechicken.nei.api.IConfigureNEI; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.client.gui.GuiFluidPatternTerminal; +import com.glodblock.github.nei.recipes.FluidRecipe; + +public class NEI_FC_Config implements IConfigureNEI { + + @Override + public void loadConfig() { + for (String identifier : FluidRecipe.getSupportRecipes()) { + API.registerGuiOverlayHandler(GuiFluidPatternTerminal.class, FluidPatternTerminalRecipeTransferHandler.INSTANCE, identifier); + } + } + + @Override + public String getName() { + return FluidCraft.MODNAME; + } + + @Override + public String getVersion() { + return FluidCraft.VERSION; + } +} diff --git a/src/main/java/com/glodblock/github/nei/object/IFluidDummy.java b/src/main/java/com/glodblock/github/nei/object/IFluidDummy.java new file mode 100644 index 00000000..acc7d17a --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/object/IFluidDummy.java @@ -0,0 +1,9 @@ +package com.glodblock.github.nei.object; + +import net.minecraftforge.fluids.FluidStack; + +public interface IFluidDummy { + + FluidStack getFluidStack(); + +} diff --git a/src/main/java/com/glodblock/github/nei/object/IRecipeExtractor.java b/src/main/java/com/glodblock/github/nei/object/IRecipeExtractor.java new file mode 100644 index 00000000..9419c7a6 --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/object/IRecipeExtractor.java @@ -0,0 +1,13 @@ +package com.glodblock.github.nei.object; + +import codechicken.nei.PositionedStack; + +import java.util.List; + +public interface IRecipeExtractor { + + List> getInputIngredients(List rawInputs); + + List> getOutputIngredients(List rawOutputs); + +} diff --git a/src/main/java/com/glodblock/github/nei/object/IllegalOrderStackID.java b/src/main/java/com/glodblock/github/nei/object/IllegalOrderStackID.java new file mode 100644 index 00000000..203d3a0d --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/object/IllegalOrderStackID.java @@ -0,0 +1,9 @@ +package com.glodblock.github.nei.object; + +public class IllegalOrderStackID extends RuntimeException { + + public IllegalOrderStackID(int id) { + super("Illegal type id: " + id); + } + +} diff --git a/src/main/java/com/glodblock/github/nei/object/OrderStack.java b/src/main/java/com/glodblock/github/nei/object/OrderStack.java new file mode 100644 index 00000000..b2e285eb --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/object/OrderStack.java @@ -0,0 +1,106 @@ +package com.glodblock.github.nei.object; + +import codechicken.nei.PositionedStack; +import com.glodblock.github.util.Util; +import cpw.mods.fml.common.network.ByteBufUtils; +import io.netty.buffer.ByteBuf; +import net.minecraft.init.Items; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.oredict.OreDictionary; + +import javax.annotation.Nullable; + +public class OrderStack { + + private final T RealStack; + private final int index; + + public final static int ITEM = 1; + public final static int FLUID = 2; + public final static int CUSTOM = 3; + + public OrderStack(T stack, int order) { + if (stack == null || order < 0) + throw new IllegalArgumentException("Trying to create a null or negative order stack!"); + this.RealStack = stack; + this.index = order; + } + + public T getStack() { + return RealStack; + } + + public int getIndex() { + return index; + } + + public static OrderStack pack(PositionedStack stack, int index) { + if (stack != null && stack.items != null && stack.items.length > 0) { + if (Items.feather.getDamage(stack.items[0]) == OreDictionary.WILDCARD_VALUE) { + ItemStack tmp = stack.items[0].copy(); + tmp.setItemDamage(0); + return new OrderStack<>(tmp, index); + } + else return new OrderStack<>(stack.items[0].copy(), index); + } + return null; + } + + public static OrderStack packFluid(PositionedStack stack, int index) { + if (stack != null && stack.items != null && stack.items.length > 0) { + FluidStack fluid = Util.getFluidFromItem(stack.items[0]); + if (fluid != null) return new OrderStack<>(fluid.copy(), index); + else return pack(stack, index); + } + return null; + } + + protected void customNBTWriter(NBTTagCompound buf) { + } + + protected T customNBTReader(NBTTagCompound buf) { + return null; + } + + public final void writeToNBT(NBTTagCompound buf) { + if (RealStack instanceof ItemStack) { + NBTTagCompound tmp = new NBTTagCompound(); + tmp.setByte("t", (byte) ITEM); + ((ItemStack) RealStack).writeToNBT(tmp); + buf.setTag(index + ":", tmp); + } + else if (RealStack instanceof FluidStack) { + NBTTagCompound tmp = new NBTTagCompound(); + tmp.setByte("t", (byte) FLUID); + ((FluidStack) RealStack).writeToNBT(tmp); + buf.setTag(index + ":", tmp); + } + else { + NBTTagCompound tmp = new NBTTagCompound(); + tmp.setByte("t", (byte) CUSTOM); + customNBTWriter(tmp); + buf.setTag(index + ":", tmp); + } + } + + public static OrderStack readFromNBT(NBTTagCompound buf, @Nullable OrderStack dummy, int index) { + if (!buf.hasKey(index + ":")) return null; + NBTTagCompound info = buf.getCompoundTag(index + ":"); + byte id = info.getByte("t"); + switch (id) { + case ITEM: + return new OrderStack<>(ItemStack.loadItemStackFromNBT(info), index); + case FLUID: + return new OrderStack<>(FluidStack.loadFluidStackFromNBT(info), index); + case CUSTOM: + if (dummy == null) throw new IllegalOrderStackID(id); + return new OrderStack<>(dummy.customNBTReader(buf), index); + default: + throw new IllegalOrderStackID(id); + } + } + +} diff --git a/src/main/java/com/glodblock/github/nei/recipes/DefaultExtractorLoader.java b/src/main/java/com/glodblock/github/nei/recipes/DefaultExtractorLoader.java new file mode 100644 index 00000000..89c8083c --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/recipes/DefaultExtractorLoader.java @@ -0,0 +1,24 @@ +package com.glodblock.github.nei.recipes; + +import com.glodblock.github.nei.recipes.extractor.GregTech5RecipeExtractor; +import com.glodblock.github.nei.recipes.extractor.VanillaRecipeExtractor; +import com.glodblock.github.util.ModAndClassUtil; +import gregtech.api.util.GT_Recipe; + +public class DefaultExtractorLoader implements Runnable { + + @Override + public void run() { + FluidRecipe.addRecipeMap("smelting", new VanillaRecipeExtractor(false)); + FluidRecipe.addRecipeMap("brewing", new VanillaRecipeExtractor(false)); + FluidRecipe.addRecipeMap("crafting", new VanillaRecipeExtractor(true)); + FluidRecipe.addRecipeMap("crafting2x2", new VanillaRecipeExtractor(true)); + + if (ModAndClassUtil.GT5) { + for (GT_Recipe.GT_Recipe_Map tMap : GT_Recipe.GT_Recipe_Map.sMappings) { + FluidRecipe.addRecipeMap(tMap.mNEIName, new GregTech5RecipeExtractor(tMap.mNEIName.equals("gt.recipe.scanner") || tMap.mNEIName.equals("gt.recipe.fakeAssemblylineProcess"))); + } + } + } + +} diff --git a/src/main/java/com/glodblock/github/nei/recipes/FluidRecipe.java b/src/main/java/com/glodblock/github/nei/recipes/FluidRecipe.java new file mode 100644 index 00000000..afb1a465 --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/recipes/FluidRecipe.java @@ -0,0 +1,42 @@ +package com.glodblock.github.nei.recipes; + +import codechicken.nei.PositionedStack; +import codechicken.nei.recipe.IRecipeHandler; +import com.glodblock.github.nei.object.IRecipeExtractor; +import com.glodblock.github.nei.object.OrderStack; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public final class FluidRecipe { + + private static final HashMap IdentifierMap = new HashMap<>(); + + public static void addRecipeMap(String recipeIdentifier, IRecipeExtractor extractor) { + IdentifierMap.put(recipeIdentifier, extractor); + } + + public static List> getPackageInputs(IRecipeHandler recipe, int index) { + if (recipe == null || !IdentifierMap.containsKey(recipe.getOverlayIdentifier())) return new ArrayList<>(); + IRecipeExtractor extractor = IdentifierMap.get(recipe.getOverlayIdentifier()); + if (extractor == null) return new ArrayList<>(); + List tmp = new ArrayList<>(recipe.getIngredientStacks(index)); + return extractor.getInputIngredients(tmp); + } + + public static List> getPackageOutputs(IRecipeHandler recipe, int index, boolean useOther) { + if (recipe == null || !IdentifierMap.containsKey(recipe.getOverlayIdentifier())) return new ArrayList<>(); + IRecipeExtractor extractor = IdentifierMap.get(recipe.getOverlayIdentifier()); + if (extractor == null) return new ArrayList<>(); + List tmp = new ArrayList<>(Collections.singleton(recipe.getResultStack(index))); + if (useOther) tmp.addAll(recipe.getOtherStacks(index)); + return extractor.getOutputIngredients(tmp); + } + + public static List getSupportRecipes() { + return new ArrayList<>(IdentifierMap.keySet()); + } + +} diff --git a/src/main/java/com/glodblock/github/nei/recipes/extractor/GregTech5RecipeExtractor.java b/src/main/java/com/glodblock/github/nei/recipes/extractor/GregTech5RecipeExtractor.java new file mode 100644 index 00000000..ffcbc6af --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/recipes/extractor/GregTech5RecipeExtractor.java @@ -0,0 +1,94 @@ +package com.glodblock.github.nei.recipes.extractor; + +import codechicken.nei.PositionedStack; +import com.glodblock.github.nei.object.IRecipeExtractor; +import com.glodblock.github.nei.object.OrderStack; +import gregtech.common.items.GT_FluidDisplayItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidRegistry; +import net.minecraftforge.fluids.FluidStack; + +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class GregTech5RecipeExtractor implements IRecipeExtractor { + + boolean r; + + public GregTech5RecipeExtractor(boolean removeSpecial) { + r = removeSpecial; + } + + @Override + public List> getInputIngredients(List rawInputs) { + List compressed = compress(rawInputs); + if (r) compressed.remove(compressed.size() - 1); + List> tmp = new LinkedList<>(); + for (int i = 0; i < compressed.size(); i ++) { + if (compressed.get(i) == null) continue; + ItemStack item = compressed.get(i).items[0]; + OrderStack stack; + if (item.getItem() instanceof GT_FluidDisplayItem) { + NBTTagCompound aNBT = item.getTagCompound(); + int amt = (int) aNBT.getLong("mFluidDisplayAmount"); + if (amt <= 0) continue; + stack = new OrderStack<>(new FluidStack(FluidRegistry.getFluid(item.getItemDamage()), amt), i); + tmp.add(stack); + } else { + stack = OrderStack.pack(compressed.get(i), i); + if (stack != null) tmp.add(stack); + } + } + return tmp; + } + + @Override + public List> getOutputIngredients(List rawOutputs) { + List compressed = compress(rawOutputs); + if (r) compressed.remove(compressed.size() - 1); + List> tmp = new LinkedList<>(); + for (int i = 0; i < compressed.size(); i ++) { + if (compressed.get(i) == null) continue; + ItemStack item = compressed.get(i).items[0]; + OrderStack stack; + if (item.getItem() instanceof GT_FluidDisplayItem) { + NBTTagCompound aNBT = item.getTagCompound(); + int amt = (int) aNBT.getLong("mFluidDisplayAmount"); + if (amt <= 0) continue; + stack = new OrderStack<>(new FluidStack(FluidRegistry.getFluid(item.getItemDamage()), amt), i); + tmp.add(stack); + } else { + stack = OrderStack.pack(compressed.get(i), i); + if (stack != null) tmp.add(stack); + } + } + return tmp; + } + + private List compress(List list) { + List comp = new LinkedList<>(); + for (PositionedStack positionedStack : list) { + if (positionedStack == null) continue; + ItemStack currentStack = positionedStack.items[0]; + if (currentStack.stackSize == 0) continue; + boolean find = false; + for (PositionedStack storedStack : comp) { + if (storedStack == null) continue; + ItemStack firstStack = storedStack.items[0]; + boolean areItemStackEqual = firstStack.isItemEqual(currentStack) && ItemStack.areItemStackTagsEqual(firstStack, currentStack); + if (areItemStackEqual && (firstStack.stackSize + currentStack.stackSize) <= firstStack.getMaxStackSize()) { + find = true; + storedStack.items[0].stackSize = firstStack.stackSize + currentStack.stackSize; + } + } + if (!find) { + comp.add(positionedStack.copy()); + } + } + return comp.stream().filter(Objects::nonNull).collect(Collectors.toList()); + } + +} diff --git a/src/main/java/com/glodblock/github/nei/recipes/extractor/VanillaRecipeExtractor.java b/src/main/java/com/glodblock/github/nei/recipes/extractor/VanillaRecipeExtractor.java new file mode 100644 index 00000000..f56ed528 --- /dev/null +++ b/src/main/java/com/glodblock/github/nei/recipes/extractor/VanillaRecipeExtractor.java @@ -0,0 +1,46 @@ +package com.glodblock.github.nei.recipes.extractor; + +import codechicken.nei.PositionedStack; +import com.glodblock.github.nei.object.IRecipeExtractor; +import com.glodblock.github.nei.object.OrderStack; + +import java.util.LinkedList; +import java.util.List; + +public class VanillaRecipeExtractor implements IRecipeExtractor { + + private boolean c; + + public VanillaRecipeExtractor(boolean isCraft) { + c = isCraft; + } + + @Override + public List> getInputIngredients(List rawInputs) { + List> tmp = new LinkedList<>(); + for (int i = 0; i < rawInputs.size(); i ++) { + if (rawInputs.get(i) == null) continue; + final int col = (rawInputs.get(i).relx - 25) / 18; + final int row = (rawInputs.get(i).rely - 6) / 18; + int index = col + row * 3; + OrderStack stack = OrderStack.pack(rawInputs.get(i), c ? index : i); + if (stack != null) tmp.add(stack); + } + return tmp; + } + + @Override + public List> getOutputIngredients(List rawOutputs) { + List> tmp = new LinkedList<>(); + for (int i = 0; i < rawOutputs.size(); i ++) { + if (rawOutputs.get(i) == null) continue; + final int col = (rawOutputs.get(i).relx - 25) / 18; + final int row = (rawOutputs.get(i).rely - 6) / 18; + int index = col + row * 3; + OrderStack stack = OrderStack.pack(rawOutputs.get(i), c ? index : i); + if (stack != null) tmp.add(stack); + } + return tmp; + } + +} diff --git a/src/main/java/com/glodblock/github/network/CPacketCraftRequest.java b/src/main/java/com/glodblock/github/network/CPacketCraftRequest.java new file mode 100644 index 00000000..d93a60b4 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketCraftRequest.java @@ -0,0 +1,118 @@ +package com.glodblock.github.network; + +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridHost; +import appeng.api.networking.IGridNode; +import appeng.api.networking.crafting.ICraftingGrid; +import appeng.api.networking.crafting.ICraftingJob; +import appeng.container.ContainerOpenContext; +import appeng.container.implementations.ContainerCraftAmount; +import appeng.core.AELog; +import com.glodblock.github.client.gui.container.ContainerFluidCraftConfirm; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.Util; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.ForgeDirection; + +import javax.annotation.Nullable; +import java.util.Objects; +import java.util.concurrent.Future; + +public class CPacketCraftRequest implements IMessage { + + private long amount; + private boolean heldShift; + + public CPacketCraftRequest() { + } + + public CPacketCraftRequest( final int craftAmt, final boolean shift ) { + amount = craftAmt; + heldShift = shift; + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeLong(amount); + buf.writeBoolean(heldShift); + } + + @Override + public void fromBytes(ByteBuf buf) { + amount = buf.readLong(); + heldShift = buf.readBoolean(); + } + + public static class Handler implements IMessageHandler { + + @Nullable + @Override + public IMessage onMessage(CPacketCraftRequest message, MessageContext ctx) { + if( ctx.getServerHandler().playerEntity.openContainer instanceof ContainerCraftAmount) + { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + final ContainerCraftAmount cca = (ContainerCraftAmount) ctx.getServerHandler().playerEntity.openContainer; + final Object target = cca.getTarget(); + if( target instanceof IGridHost) + { + final IGridHost gh = (IGridHost) target; + final IGridNode gn = gh.getGridNode( ForgeDirection.UNKNOWN ); + + if( gn == null ) + { + return null; + } + + final IGrid g = gn.getGrid(); + if( g == null || cca.getItemToCraft() == null ) + { + return null; + } + + cca.getItemToCraft().setStackSize( message.amount ); + + Future futureJob = null; + try + { + final ICraftingGrid cg = g.getCache( ICraftingGrid.class ); + futureJob = cg.beginCraftingJob( cca.getWorld(), cca.getGrid(), cca.getActionSrc(), cca.getItemToCraft(), null ); + + final ContainerOpenContext context = cca.getOpenContext(); + if( context != null ) + { + + final TileEntity te = context.getTile(); + InventoryHandler.openGui(player, player.worldObj, new BlockPos(te), Objects.requireNonNull(Util.from(context.getSide())), GuiType.FLUID_CRAFTING_CONFIRM); + + if( player.openContainer instanceof ContainerFluidCraftConfirm) + { + final ContainerFluidCraftConfirm ccc = (ContainerFluidCraftConfirm) player.openContainer; + ccc.setAutoStart( message.heldShift ); + ccc.setJob( futureJob ); + cca.detectAndSendChanges(); + } + } + } + catch( final Throwable e ) + { + if( futureJob != null ) + { + futureJob.cancel( true ); + } + AELog.debug( e ); + } + } + } + return null; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/network/CPacketEncodePattern.java b/src/main/java/com/glodblock/github/network/CPacketEncodePattern.java new file mode 100644 index 00000000..b025844c --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketEncodePattern.java @@ -0,0 +1,38 @@ +package com.glodblock.github.network; + +import com.glodblock.github.client.gui.container.ContainerFluidPatternEncoder; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; + +import javax.annotation.Nullable; + +public class CPacketEncodePattern implements IMessage { + + @Override + public void toBytes(ByteBuf buf) { + // NO-OP + } + + @Override + public void fromBytes(ByteBuf buf) { + // NO-OP + } + + public static class Handler implements IMessageHandler { + + @Nullable + @Override + public IMessage onMessage(CPacketEncodePattern message, MessageContext ctx) { + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + if (player.openContainer instanceof ContainerFluidPatternEncoder) { + ((ContainerFluidPatternEncoder)player.openContainer).encodePattern(); + } + return null; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/network/CPacketFluidPatternTermBtns.java b/src/main/java/com/glodblock/github/network/CPacketFluidPatternTermBtns.java new file mode 100644 index 00000000..374143e2 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketFluidPatternTermBtns.java @@ -0,0 +1,95 @@ +package com.glodblock.github.network; + +import com.glodblock.github.client.gui.container.ContainerFluidCraftConfirm; +import com.glodblock.github.client.gui.container.ContainerFluidPatternTerminal; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.inventory.Container; + +public class CPacketFluidPatternTermBtns implements IMessage { + + private String Name = ""; + private String Value = ""; + + public CPacketFluidPatternTermBtns( final String name, final String value ) { + Name = name; + Value = value; + } + + public CPacketFluidPatternTermBtns() { + // NO-OP + } + + @Override + public void fromBytes(ByteBuf buf) { + int leName = buf.readInt(); + int leVal = buf.readInt(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < leName; i ++) { + sb.append(buf.readChar()); + } + Name = sb.toString(); + sb = new StringBuilder(); + for (int i = 0; i < leVal; i ++) { + sb.append(buf.readChar()); + } + Value = sb.toString(); + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(Name.length()); + buf.writeInt(Value.length()); + for (int i = 0; i < Name.length(); i ++) { + buf.writeChar(Name.charAt(i)); + } + for (int i = 0; i < Value.length(); i ++) { + buf.writeChar(Value.charAt(i)); + } + } + + public static class Handler implements IMessageHandler { + + @Override + public IMessage onMessage(CPacketFluidPatternTermBtns message, MessageContext ctx) { + String Name = message.Name; + String Value = message.Value; + final Container c = ctx.getServerHandler().playerEntity.openContainer; + if( Name.startsWith( "PatternTerminal." ) && c instanceof ContainerFluidPatternTerminal) + { + final ContainerFluidPatternTerminal cpt = (ContainerFluidPatternTerminal) c; + switch (Name) { + case "PatternTerminal.CraftMode": + cpt.getPatternTerminal().setCraftingRecipe(Value.equals("1")); + break; + case "PatternTerminal.Encode": + if (Value.equals("2")) + cpt.encodeAndMoveToInventory(); + else + cpt.encode(); + break; + case "PatternTerminal.Clear": + cpt.clear(); + break; + case "PatternTerminal.Substitute": + cpt.getPatternTerminal().setSubstitution(Value.equals("1")); + break; + case "PatternTerminal.Double": + cpt.doubleStacks(Value.equals("1")); + break; + } + } else if(Name.equals( "Terminal.Cpu" ) && c instanceof ContainerFluidCraftConfirm) { + final ContainerFluidCraftConfirm qk = (ContainerFluidCraftConfirm) c; + qk.cycleCpu( Value.equals( "Next" ) ); + } + else if( Name.equals( "Terminal.Start" ) && c instanceof ContainerFluidCraftConfirm ) { + final ContainerFluidCraftConfirm qk = (ContainerFluidCraftConfirm) c; + qk.startJob(); + } + return null; + } + + } +} diff --git a/src/main/java/com/glodblock/github/network/CPacketInventoryAction.java b/src/main/java/com/glodblock/github/network/CPacketInventoryAction.java new file mode 100644 index 00000000..681977cb --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketInventoryAction.java @@ -0,0 +1,124 @@ +package com.glodblock.github.network; + +import appeng.api.storage.data.IAEItemStack; +import appeng.container.AEBaseContainer; +import appeng.container.ContainerOpenContext; +import appeng.container.implementations.ContainerCraftAmount; +import appeng.core.sync.GuiBridge; +import appeng.helpers.InventoryAction; +import appeng.util.Platform; +import appeng.util.item.AEItemStack; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.Util; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.tileentity.TileEntity; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.Objects; + +public class CPacketInventoryAction implements IMessage { + + private InventoryAction action; + private int slot; + private long id; + private IAEItemStack stack; + private boolean isEmpty; + + public CPacketInventoryAction() { + } + + public CPacketInventoryAction( final InventoryAction action, final int slot, final int id ) { + this.action = action; + this.slot = slot; + this.id = id; + this.stack = null; + this.isEmpty = true; + } + + public CPacketInventoryAction(final InventoryAction action, final int slot, final int id , IAEItemStack stack) { + this.action = action; + this.slot = slot; + this.id = id; + this.stack = stack; + this.isEmpty = stack == null; + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(action.ordinal()); + buf.writeInt(slot); + buf.writeLong(id); + buf.writeBoolean(isEmpty); + if (!isEmpty) { + try { + stack.writeToPacket(buf); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Override + public void fromBytes(ByteBuf buf) { + action = InventoryAction.values()[buf.readInt()]; + slot = buf.readInt(); + id = buf.readLong(); + isEmpty = buf.readBoolean(); + if (!isEmpty) { + try { + stack = AEItemStack.loadItemStackFromPacket(buf); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public static class Handler implements IMessageHandler { + + @Nullable + @Override + public IMessage onMessage(CPacketInventoryAction message, MessageContext ctx) { + final EntityPlayerMP sender = ctx.getServerHandler().playerEntity; + if( sender.openContainer instanceof AEBaseContainer ) + { + final AEBaseContainer baseContainer = (AEBaseContainer) sender.openContainer; + if( message.action == InventoryAction.AUTO_CRAFT ) + { + final ContainerOpenContext context = baseContainer.getOpenContext(); + if( context != null ) + { + final TileEntity te = context.getTile(); + InventoryHandler.openGui( sender, te.getWorldObj(), new BlockPos(te), Objects.requireNonNull(Util.from(baseContainer.getOpenContext().getSide())), GuiType.FLUID_CRAFTING_AMOUNT ); + + if( sender.openContainer instanceof ContainerCraftAmount ) + { + final ContainerCraftAmount cca = (ContainerCraftAmount) sender.openContainer; + + if( baseContainer.getTargetStack() != null ) + { + cca.getCraftingItem().putStack( baseContainer.getTargetStack().getItemStack() ); + cca.setItemToCraft( baseContainer.getTargetStack() ); + } + + cca.detectAndSendChanges(); + } + } + } + else + { + baseContainer.doAction( sender, message.action, message.slot, message.id ); + } + } + return null; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/network/CPacketSetTargetItem.java b/src/main/java/com/glodblock/github/network/CPacketSetTargetItem.java new file mode 100644 index 00000000..2e9f6cbf --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketSetTargetItem.java @@ -0,0 +1,56 @@ +package com.glodblock.github.network; + +import appeng.api.storage.data.IAEItemStack; +import appeng.container.AEBaseContainer; +import appeng.util.item.AEItemStack; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.inventory.Container; + +import java.io.IOException; + +public class CPacketSetTargetItem implements IMessage { + + private IAEItemStack stack; + + public CPacketSetTargetItem() { + } + + public CPacketSetTargetItem(IAEItemStack stack) { + this.stack = stack; + } + + @Override + public void toBytes(ByteBuf buf) { + try { + stack.writeToPacket(buf); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void fromBytes(ByteBuf buf) { + try { + stack = AEItemStack.loadItemStackFromPacket(buf); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static class Handler implements IMessageHandler { + + @Override + public IMessage onMessage(CPacketSetTargetItem message, MessageContext ctx) { + Container c = ctx.getServerHandler().playerEntity.openContainer; + if (c instanceof AEBaseContainer) { + ((AEBaseContainer) c).setTargetStack(message.stack); + } + return null; + } + + } + +} diff --git a/src/main/java/com/glodblock/github/network/CPacketSwitchGuis.java b/src/main/java/com/glodblock/github/network/CPacketSwitchGuis.java new file mode 100644 index 00000000..7c7ce1c2 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketSwitchGuis.java @@ -0,0 +1,68 @@ +package com.glodblock.github.network; + +import appeng.container.AEBaseContainer; +import appeng.container.ContainerOpenContext; +import com.glodblock.github.inventory.InventoryHandler; +import com.glodblock.github.inventory.gui.GuiType; +import com.glodblock.github.util.BlockPos; +import com.glodblock.github.util.Util; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.Container; +import net.minecraft.tileentity.TileEntity; + +import javax.annotation.Nullable; +import java.util.Objects; + +public class CPacketSwitchGuis implements IMessage { + + private GuiType guiType; + + public CPacketSwitchGuis(GuiType guiType) { + this.guiType = guiType; + } + + public CPacketSwitchGuis() { + // NO-OP + } + + @Override + public void fromBytes(ByteBuf byteBuf) { + guiType = GuiType.getByOrdinal(byteBuf.readByte()); + } + + @Override + public void toBytes(ByteBuf byteBuf) { + byteBuf.writeByte(guiType != null ? guiType.ordinal() : 0); + } + + public static class Handler implements IMessageHandler { + @Nullable + @Override + public IMessage onMessage(CPacketSwitchGuis message, MessageContext ctx) { + if (message.guiType == null) { + return null; + } + EntityPlayerMP player = ctx.getServerHandler().playerEntity; + Container cont = player.openContainer; + if (!(cont instanceof AEBaseContainer)) { + return null; + } + ContainerOpenContext context = ((AEBaseContainer)cont).getOpenContext(); + if (context == null) { + return null; + } + TileEntity te = context.getTile(); + if (te == null) { + return null; + } + InventoryHandler.openGui(player, player.worldObj, new BlockPos(te), Objects.requireNonNull(Util.from(context.getSide())), message.guiType); + return null; + } + + } +} + diff --git a/src/main/java/com/glodblock/github/network/CPacketTransferRecipe.java b/src/main/java/com/glodblock/github/network/CPacketTransferRecipe.java new file mode 100644 index 00000000..864e03d9 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/CPacketTransferRecipe.java @@ -0,0 +1,128 @@ +package com.glodblock.github.network; + +import com.glodblock.github.client.gui.container.ContainerFluidPatternTerminal; +import com.glodblock.github.common.item.ItemFluidPacket; +import com.glodblock.github.nei.object.OrderStack; +import cpw.mods.fml.common.network.ByteBufUtils; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.fluids.FluidStack; + +import javax.annotation.Nullable; +import java.util.LinkedList; +import java.util.List; + +public class CPacketTransferRecipe implements IMessage { + + private List> inputs; + private List> outputs; + private boolean isCraft; + private final static int MAX_INDEX = 16; + + public CPacketTransferRecipe(){ + } + + public CPacketTransferRecipe(List> IN, List> OUT, boolean craft) { + this.inputs = IN; + this.outputs = OUT; + this.isCraft = craft; + } + + //I should use GZIP to compress the message, but i'm too lazy. + //NBT to ByteBuf has a compress stream + @Override + public void toBytes(ByteBuf buf) { + buf.writeBoolean(isCraft); + NBTTagCompound nbt_m = new NBTTagCompound(); + NBTTagCompound nbt_i = new NBTTagCompound(); + NBTTagCompound nbt_o = new NBTTagCompound(); + for (OrderStack stack : inputs) { + stack.writeToNBT(nbt_i); + } + for (OrderStack stack : outputs) { + stack.writeToNBT(nbt_o); + } + nbt_m.setTag("i", nbt_i); + nbt_m.setTag("o", nbt_o); + ByteBufUtils.writeTag(buf, nbt_m); + } + + @Override + public void fromBytes(ByteBuf buf) { + isCraft = buf.readBoolean(); + inputs = new LinkedList<>(); + outputs = new LinkedList<>(); + NBTTagCompound nbt_m = ByteBufUtils.readTag(buf); + NBTTagCompound nbt_i = nbt_m.getCompoundTag("i"); + NBTTagCompound nbt_o = nbt_m.getCompoundTag("o"); + for (int i = 0; i < MAX_INDEX; i ++) { + OrderStack tmp = OrderStack.readFromNBT(nbt_i, null, i); + if (tmp != null) inputs.add(tmp); + } + for (int i = 0; i < MAX_INDEX; i ++) { + OrderStack tmp = OrderStack.readFromNBT(nbt_o, null, i); + if (tmp != null) outputs.add(tmp); + } + } + + public static class Handler implements IMessageHandler { + + @Nullable + @Override + public IMessage onMessage(CPacketTransferRecipe message, MessageContext ctx) { + Container c = ctx.getServerHandler().playerEntity.openContainer; + if (c instanceof ContainerFluidPatternTerminal) { + ContainerFluidPatternTerminal cf = (ContainerFluidPatternTerminal) c; + cf.getPatternTerminal().setCraftingRecipe(message.isCraft); + IInventory inputSlot = cf.getInventoryByName("crafting"); + IInventory outputSlot = cf.getInventoryByName("output"); + for (int i = 0; i < inputSlot.getSizeInventory(); i ++) { + inputSlot.setInventorySlotContents(i, null); + } + for (int i = 0; i < outputSlot.getSizeInventory(); i ++) { + outputSlot.setInventorySlotContents(i, null); + } + for (OrderStack stack : message.inputs) { + if (stack != null) { + int index = stack.getIndex(); + ItemStack stack1; + if (stack.getStack() instanceof ItemStack) { + stack1 = ((ItemStack) stack.getStack()).copy(); + } + else if (stack.getStack() instanceof FluidStack) { + stack1 = ItemFluidPacket.newStack((FluidStack) stack.getStack()); + } + else throw new UnsupportedOperationException("Trying to get an unsupported item!"); + if (index < inputSlot.getSizeInventory()) + inputSlot.setInventorySlotContents(index, stack1); + } + } + for (OrderStack stack : message.outputs) { + if (stack != null) { + int index = stack.getIndex(); + ItemStack stack1; + if (stack.getStack() instanceof ItemStack) { + stack1 = ((ItemStack) stack.getStack()).copy(); + } + else if (stack.getStack() instanceof FluidStack) { + stack1 = ItemFluidPacket.newStack((FluidStack) stack.getStack()); + } + else throw new UnsupportedOperationException("Trying to get an unsupported item!"); + if (index < outputSlot.getSizeInventory()) + outputSlot.setInventorySlotContents(index, stack1); + } + } + c.onCraftMatrixChanged(inputSlot); + c.onCraftMatrixChanged(outputSlot); + } + return null; + } + } + +} diff --git a/src/main/java/com/glodblock/github/network/SPacketFluidSlot.java b/src/main/java/com/glodblock/github/network/SPacketFluidSlot.java new file mode 100644 index 00000000..daa323d6 --- /dev/null +++ b/src/main/java/com/glodblock/github/network/SPacketFluidSlot.java @@ -0,0 +1,68 @@ +package com.glodblock.github.network; + +import appeng.api.storage.data.IAEFluidStack; +import appeng.util.item.AEFluidStack; +import com.glodblock.github.client.gui.IFluidSyncContainer; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.inventory.Container; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class SPacketFluidSlot implements IMessage { + + private Map list; + + public SPacketFluidSlot() { + } + + public SPacketFluidSlot(Map data) { + this.list = data; + } + + @Override + public void fromBytes(ByteBuf buf) { + int size = buf.readInt(); + this.list = new HashMap<>(); + try { + for (int i = 0; i < size; i ++) { + int id = buf.readInt(); + IAEFluidStack fluid = AEFluidStack.loadFluidStackFromPacket(buf); + list.put(id, fluid); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeInt(list.size()); + try { + for (Map.Entry fs : list.entrySet()) { + buf.writeInt(fs.getKey()); + fs.getValue().writeToPacket(buf); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static class Handler implements IMessageHandler { + + @Override + public IMessage onMessage(SPacketFluidSlot message, MessageContext ctx) { + final Container c = ctx.getServerHandler().playerEntity.openContainer; + if( c instanceof IFluidSyncContainer) + { + ( (IFluidSyncContainer) c ).receiveFluidSlots( message.list ); + } + return null; + } + } + +} diff --git a/src/main/java/com/glodblock/github/network/SPacketMEInventoryUpdate.java b/src/main/java/com/glodblock/github/network/SPacketMEInventoryUpdate.java new file mode 100644 index 00000000..271ec56c --- /dev/null +++ b/src/main/java/com/glodblock/github/network/SPacketMEInventoryUpdate.java @@ -0,0 +1,84 @@ +package com.glodblock.github.network; + +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEItemStack; +import com.glodblock.github.client.gui.GuiFCBaseMonitor; +import com.glodblock.github.client.gui.GuiFluidCraftConfirm; +import cpw.mods.fml.common.network.simpleimpl.IMessage; +import cpw.mods.fml.common.network.simpleimpl.IMessageHandler; +import cpw.mods.fml.common.network.simpleimpl.MessageContext; +import io.netty.buffer.ByteBuf; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; + +import java.io.IOException; +import java.nio.BufferOverflowException; +import java.util.LinkedList; +import java.util.List; + +public class SPacketMEInventoryUpdate implements IMessage { + + private List list; + private byte ref; + + public SPacketMEInventoryUpdate() { + ref = 0; + list = new LinkedList<>(); + } + + public SPacketMEInventoryUpdate(byte b) { + this.ref = b; + list = new LinkedList<>(); + } + + @Override + public void fromBytes(ByteBuf buf) { + long amount = buf.readLong(); + ref = buf.readByte(); + list = new LinkedList<>(); + try { + for (int i = 0; i < amount; i ++) { + list.add(AEItemStack.loadItemStackFromPacket(buf)); + } + } catch (IOException io) { + io.printStackTrace(); + } + } + + @Override + public void toBytes(ByteBuf buf) { + buf.writeLong(list.size()); + buf.writeByte(ref); + try { + for (IAEItemStack itemStack : list) { + itemStack.writeToPacket(buf); + } + } catch (IOException io) { + io.printStackTrace(); + } + } + + public void appendItem( final IAEItemStack is ) throws BufferOverflowException { + list.add(is); + } + + public boolean isEmpty() { + return this.list.isEmpty(); + } + + public static class Handler implements IMessageHandler { + + @Override + public IMessage onMessage(SPacketMEInventoryUpdate message, MessageContext ctx) { + final GuiScreen gs = Minecraft.getMinecraft().currentScreen; + if( gs instanceof GuiFCBaseMonitor) + { + ( (GuiFCBaseMonitor) gs ).postUpdate(message.list); + } + else if ( gs instanceof GuiFluidCraftConfirm) { + ( (GuiFluidCraftConfirm) gs ).postUpdate( message.list, message.ref ); + } + return null; + } + } +} diff --git a/src/main/java/com/glodblock/github/proxy/ClientProxy.java b/src/main/java/com/glodblock/github/proxy/ClientProxy.java new file mode 100644 index 00000000..878a28c0 --- /dev/null +++ b/src/main/java/com/glodblock/github/proxy/ClientProxy.java @@ -0,0 +1,23 @@ +package com.glodblock.github.proxy; + +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; + +public class ClientProxy extends CommonProxy { + + @Override + public void preInit(FMLPreInitializationEvent event){ + super.preInit(event); + } + + @Override + public void init(FMLInitializationEvent event){ + super.init(event); + } + + @Override + public void postInit(FMLPostInitializationEvent event){ + super.postInit(event); + } +} diff --git a/src/main/java/com/glodblock/github/proxy/CommonProxy.java b/src/main/java/com/glodblock/github/proxy/CommonProxy.java new file mode 100644 index 00000000..1b02c687 --- /dev/null +++ b/src/main/java/com/glodblock/github/proxy/CommonProxy.java @@ -0,0 +1,20 @@ +package com.glodblock.github.proxy; + +import com.glodblock.github.FluidCraft; +import cpw.mods.fml.common.event.FMLInitializationEvent; +import cpw.mods.fml.common.event.FMLPostInitializationEvent; +import cpw.mods.fml.common.event.FMLPreInitializationEvent; +import cpw.mods.fml.common.network.NetworkRegistry; +import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper; + +public class CommonProxy { + + public final SimpleNetworkWrapper netHandler = NetworkRegistry.INSTANCE.newSimpleChannel(FluidCraft.MODID); + + public void preInit(FMLPreInitializationEvent event){} + + public void init(FMLInitializationEvent event){} + + public void postInit(FMLPostInitializationEvent event){} + +} diff --git a/src/main/java/com/glodblock/github/util/Ae2Reflect.java b/src/main/java/com/glodblock/github/util/Ae2Reflect.java new file mode 100644 index 00000000..74e9ad08 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/Ae2Reflect.java @@ -0,0 +1,237 @@ +package com.glodblock.github.util; + +import appeng.api.implementations.IUpgradeableHost; +import appeng.api.networking.crafting.ICraftingCPU; +import appeng.client.gui.AEBaseGui; +import appeng.client.gui.implementations.GuiCraftingStatus; +import appeng.client.gui.widgets.GuiTabButton; +import appeng.client.render.AppEngRenderItem; +import appeng.container.implementations.ContainerUpgradeable; +import appeng.container.implementations.CraftingCPURecord; +import appeng.helpers.MultiCraftingTracker; +import appeng.util.inv.ItemSlot; +import com.glodblock.github.client.gui.GuiBaseFluidPatternTerminal; +import com.glodblock.github.client.gui.GuiFCBaseMonitor; +import com.glodblock.github.client.gui.container.ContainerFluidPatternTerminal; +import net.minecraft.client.gui.inventory.GuiContainer; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +public class Ae2Reflect { + + private static final Field fInventory_container; + private static final Field fInventory_width; + private static final Field fInventory_stackList; + private static final Field fAEContainer_upgradeable; + private static final Method mItemSlot_setExtractable; + private static final Method mTracker_isBusy; + private static final Method mTracker_cancel; + + static { + try { + fInventory_container = reflectField(InventoryCrafting.class, "eventHandler"); + fInventory_width = reflectField(InventoryCrafting.class, "inventoryWidth"); + fInventory_stackList = reflectField(InventoryCrafting.class, "stackList"); + fAEContainer_upgradeable = reflectField(ContainerUpgradeable.class, "upgradeable"); + mItemSlot_setExtractable = reflectMethod(ItemSlot.class, "setExtractable", boolean.class); + mTracker_isBusy = reflectMethod(MultiCraftingTracker.class, "isBusy", int.class); + mTracker_cancel = reflectMethod(MultiCraftingTracker.class, "cancel"); + } 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 { + Method m = owner.getDeclaredMethod(name, paramTypes); + m.setAccessible(true); + return m; + } + + public static Field reflectField(Class owner, String name) throws NoSuchFieldException { + Field f = owner.getDeclaredField(name); + 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); + } + } + + public static void doCancel(MultiCraftingTracker mct) { + try { + mTracker_cancel.invoke(mct); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mTracker_cancel, e); + } + } + + public static boolean getBusy(MultiCraftingTracker mct, int slot) { + try { + return (boolean) mTracker_isBusy.invoke(mct, slot); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mTracker_isBusy, e); + } + } + + public static IUpgradeableHost getUpgrade(ContainerUpgradeable container) { + return Ae2Reflect.readField(container, fAEContainer_upgradeable); + } + + public static Container getCraftContainer(InventoryCrafting inv) { + return Ae2Reflect.readField(inv, fInventory_container); + } + + public static int getCraftWidth(InventoryCrafting inv) { + return Ae2Reflect.readField(inv, fInventory_width); + } + + public static ItemStack[] getCraftStackList(InventoryCrafting inv) { + return Ae2Reflect.readField(inv, fInventory_stackList); + } + + public static void setItemSlotExtractable(ItemSlot slot, boolean extractable) { + try { + mItemSlot_setExtractable.invoke(slot, extractable); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mItemSlot_setExtractable, e); + } + } + + private static final Field fAEBaseGui_stackSizeRenderer; + private static final Field fGuiCraftingStatus_originalGuiBtn; + private static final Field fGuiPatternTerm_container; + private static final Field fGuiCPUStatus_icon; + private static final Field fGuiMEMonitorable_monitorableContainer; + private static final Field fGuiMEMonitorable_configSrc; + private static final Field fGuiMEMonitorable_craftingStatusBtn; + private static final Field fGuiContainer_guiLeft; + private static final Field fGuiContainer_guiTop; + private static final Field fCPU_cpu; + private static final Field fCPU_myName; + private static final Field fCPU_processors; + private static final Field fCPU_size; + private static final Field fGui_drag; + private static final Method mGuiPatternTerm_inventorySlots; + private static final Method mGuiPatternTerm_reservedSpace; + private static final Method mGuiPatternTerm_reservedSpaceSetter; + + static { + try { + fAEBaseGui_stackSizeRenderer = Ae2Reflect.reflectField(AEBaseGui.class, "aeRenderItem"); + fGuiCraftingStatus_originalGuiBtn = Ae2Reflect.reflectField(GuiCraftingStatus.class, "originalGuiBtn"); + fGuiPatternTerm_container = Ae2Reflect.reflectField(GuiBaseFluidPatternTerminal.class, "container"); + fGuiMEMonitorable_monitorableContainer = Ae2Reflect.reflectField(GuiFCBaseMonitor.class, "monitorableContainer"); + fGuiMEMonitorable_configSrc = Ae2Reflect.reflectField(GuiFCBaseMonitor.class, "configSrc"); + fGuiMEMonitorable_craftingStatusBtn = Ae2Reflect.reflectField(GuiFCBaseMonitor.class, "craftingStatusBtn"); + fGuiCPUStatus_icon = Ae2Reflect.reflectField(GuiCraftingStatus.class, "myIcon"); + fGuiContainer_guiLeft = Ae2Reflect.reflectField(GuiContainer.class, "guiLeft"); + fGuiContainer_guiTop = Ae2Reflect.reflectField(GuiContainer.class, "guiTop"); + fCPU_cpu = Ae2Reflect.reflectField(CraftingCPURecord.class, "cpu"); + fCPU_myName = Ae2Reflect.reflectField(CraftingCPURecord.class, "myName"); + fCPU_processors = Ae2Reflect.reflectField(CraftingCPURecord.class, "processors"); + fCPU_size = Ae2Reflect.reflectField(CraftingCPURecord.class, "size"); + fGui_drag = Ae2Reflect.reflectField(AEBaseGui.class, "drag_click"); + mGuiPatternTerm_inventorySlots = Ae2Reflect.reflectMethod(AEBaseGui.class, "getInventorySlots"); + mGuiPatternTerm_reservedSpace = Ae2Reflect.reflectMethod(GuiFCBaseMonitor.class, "getReservedSpace"); + mGuiPatternTerm_reservedSpaceSetter = Ae2Reflect.reflectMethod(GuiFCBaseMonitor.class, "setReservedSpace", int.class); + } catch (Exception e) { + throw new IllegalStateException("Failed to initialize AE2 reflection hacks!", e); + } + } + + @SuppressWarnings("unchecked") + public static List getInventorySlots(AEBaseGui gui) { + try { + return (List) mGuiPatternTerm_inventorySlots.invoke(gui); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mGuiPatternTerm_inventorySlots, e); + } + } + + public static void rewriteIcon(GuiCraftingStatus gui, ItemStack icon) { + Ae2Reflect.writeField(gui, fGuiCPUStatus_icon, icon); + } + + public static Set getDragClick(AEBaseGui gui) { + return Ae2Reflect.readField(gui, fGui_drag); + } + + public static ICraftingCPU getCPU(CraftingCPURecord cpu) { + return Ae2Reflect.readField(cpu, fCPU_cpu); + } + + public static String getName(CraftingCPURecord cpu) { + return Ae2Reflect.readField(cpu, fCPU_myName); + } + + public static int getProcessors(CraftingCPURecord cpu) { + return Ae2Reflect.readField(cpu, fCPU_processors); + } + + public static long getSize(CraftingCPURecord cpu) { + return Ae2Reflect.readField(cpu, fCPU_size); + } + + public static int getGuiLeft(GuiContainer gui) { + return Ae2Reflect.readField(gui, fGuiContainer_guiLeft); + } + + public static int getGuiTop(GuiContainer gui) { + return Ae2Reflect.readField(gui, fGuiContainer_guiTop); + } + + public static void setReservedSpace(GuiFCBaseMonitor gui, int size) { + try { + mGuiPatternTerm_reservedSpaceSetter.invoke(gui, size); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mGuiPatternTerm_reservedSpaceSetter, e); + } + } + + public static int getReservedSpace(GuiFCBaseMonitor gui) { + try { + return (int) mGuiPatternTerm_reservedSpace.invoke(gui); + } catch (Exception e) { + throw new IllegalStateException("Failed to invoke method: " + mGuiPatternTerm_reservedSpace, e); + } + } + + public static AppEngRenderItem getStackSizeRenderer(AEBaseGui gui) { + return Ae2Reflect.readField(gui, fAEBaseGui_stackSizeRenderer); + } + + public static GuiTabButton getOriginalGuiButton(GuiCraftingStatus gui) { + return Ae2Reflect.readField(gui, fGuiCraftingStatus_originalGuiBtn); + } + + public static void setGuiContainer(GuiBaseFluidPatternTerminal instance, ContainerFluidPatternTerminal container) { + Ae2Reflect.writeField(instance, fGuiPatternTerm_container, container); + Ae2Reflect.writeField(instance, fGuiMEMonitorable_monitorableContainer, container); + Ae2Reflect.writeField(instance, fGuiMEMonitorable_configSrc, container.getConfigManager()); + } + + public static GuiTabButton getCraftingStatusButton(GuiFCBaseMonitor gui) { + return Ae2Reflect.readField(gui, fGuiMEMonitorable_craftingStatusBtn); + } + +} diff --git a/src/main/java/com/glodblock/github/util/BlockPos.java b/src/main/java/com/glodblock/github/util/BlockPos.java new file mode 100644 index 00000000..45897d2e --- /dev/null +++ b/src/main/java/com/glodblock/github/util/BlockPos.java @@ -0,0 +1,46 @@ +package com.glodblock.github.util; + +import net.minecraft.tileentity.TileEntity; + +import javax.annotation.Nonnull; + +public class BlockPos { + + private final int x; + private final int y; + private final int z; + + public BlockPos(int x, int y, int z) { + this.x = x; + this.y = y; + this.z = z; + } + + public BlockPos(@Nonnull TileEntity te) { + this.x = te.xCoord; + this.y = te.yCoord; + this.z = te.zCoord; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof BlockPos) { + BlockPos pos = (BlockPos) obj; + return pos.x == x && pos.y == y && pos.z == z; + } + return false; + } + +} diff --git a/src/main/java/com/glodblock/github/util/FluidPatternDetails.java b/src/main/java/com/glodblock/github/util/FluidPatternDetails.java new file mode 100644 index 00000000..a3cb37da --- /dev/null +++ b/src/main/java/com/glodblock/github/util/FluidPatternDetails.java @@ -0,0 +1,199 @@ +package com.glodblock.github.util; + +import appeng.api.networking.crafting.ICraftingPatternDetails; +import appeng.api.storage.data.IAEItemStack; +import appeng.util.item.AEItemStack; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class FluidPatternDetails implements ICraftingPatternDetails, Comparable{ + + private final ItemStack patternStack; + private IAEItemStack patternStackAe; + private IAEItemStack[] inputs = null, inputsCond = null, outputs = null, outputsCond = null; + private int priority = 0; + + public FluidPatternDetails(ItemStack stack) { + this.patternStack = stack; + this.patternStackAe = Objects.requireNonNull(AEItemStack.create(stack)); // s2g + } + + @Override + public ItemStack getPattern() { + return patternStack; + } + + @Override + public int getPriority() { + return priority; + } + + @Override + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public boolean isCraftable() { + return false; + } + + @Override + public boolean canSubstitute() { + return false; + } + + @Override + public IAEItemStack[] getInputs() { + return checkInitialized(inputs); + } + + @Override + public IAEItemStack[] getCondensedInputs() { + return checkInitialized(inputsCond); + } + + public boolean setInputs(IAEItemStack[] inputs) { + for (IAEItemStack stack : inputs) { // see note at top of class + if (stack == null) { + return false; + } + } + IAEItemStack[] condensed = condenseStacks(inputs); + if (condensed.length == 0) { + return false; + } + this.inputs = inputs; + this.inputsCond = condensed; + return true; + } + + @Override + public IAEItemStack[] getOutputs() { + return checkInitialized(outputs); + } + + @Override + public IAEItemStack[] getCondensedOutputs() { + return checkInitialized(outputsCond); + } + + public boolean setOutputs(IAEItemStack[] outputs) { + for (IAEItemStack stack : outputs) { // see note at top of class + if (stack == null) { + return false; + } + } + IAEItemStack[] condensed = condenseStacks(outputs); + if (condensed.length == 0) { + return false; + } + this.outputs = outputs; + this.outputsCond = condensed; + return true; + } + + private static IAEItemStack[] condenseStacks(IAEItemStack[] stacks) { + // AE item stacks are equivalent iff they are of the same item type (not accounting for stack size) + // thus, it's not the semantically-correct definition of "equal" but it's useful for matching item types + Map accMap = new HashMap<>(); + for (IAEItemStack stack : stacks) { + if (stack != null) { + IAEItemStack acc = accMap.get(stack); + if (acc == null) { + accMap.put(stack, stack.copy()); + } else { + acc.add(stack); + } + } + } + return accMap.values().toArray(new IAEItemStack[0]); + } + + @Override + public ItemStack getOutput(InventoryCrafting craftingInv, World world) { + throw new IllegalStateException("Not a crafting recipe!"); + } + + @Override + public boolean isValidItemForSlot(int slotIndex, ItemStack itemStack, World world) { + throw new IllegalStateException("Not a crafting recipe!"); + } + + private static T checkInitialized(@Nullable T value) { + if (value == null) { + throw new IllegalStateException("Pattern is not initialized!"); + } + return value; + } + + @Override + public int hashCode() { + return patternStackAe.hashCode(); + } + + @Override + public boolean equals(Object obj) { + // ae2 null-checks the pattern stack here for some reason, but doesn't null-check in hashCode() + // this is inconsistent, so i've just decided to assert non-null in the constructor, which is to say that + // the pattern stack can never be null here + return obj instanceof FluidPatternDetails && patternStackAe.equals(((FluidPatternDetails)obj).patternStackAe); + } + + @Override + public int compareTo(FluidPatternDetails o) { + return Integer.compare(o.priority, this.priority); + } + + public ItemStack writeToStack() { + NBTTagCompound tag = new NBTTagCompound(); + tag.setTag("Inputs", writeStackArray(checkInitialized(inputs))); + tag.setTag("Outputs", writeStackArray(checkInitialized(outputs))); + patternStack.setTagCompound(tag); + patternStackAe = Objects.requireNonNull(AEItemStack.create(patternStack)); + return patternStack; + } + + public static NBTTagList writeStackArray(IAEItemStack[] stacks) { + NBTTagList listTag = new NBTTagList(); + for (IAEItemStack stack : stacks) { + if (stack != null) { + // see note at top of class + NBTTagCompound stackTag = new NBTTagCompound(); + stack.writeToNBT(stackTag); + listTag.appendTag(stackTag); + } + } + return listTag; + } + + public boolean readFromStack() { + if (!patternStack.hasTagCompound()) { + return false; + } + NBTTagCompound tag = Objects.requireNonNull(patternStack.getTagCompound()); + // may be possible to enter a partially-correct state if setInputs succeeds but setOutputs failed + // but outside code should treat it as completely incorrect and not attempt to make calls + return setInputs(readStackArray(tag.getTagList("Inputs", Constants.NBT.TAG_COMPOUND), 9)) + && setOutputs(readStackArray(tag.getTagList("Outputs", Constants.NBT.TAG_COMPOUND), 3)); + } + + public static IAEItemStack[] readStackArray(NBTTagList listTag, int maxCount) { + // see note at top of class + IAEItemStack[] stacks = new IAEItemStack[Math.min(listTag.tagCount(), maxCount)]; + for (int i = 0; i < stacks.length; i++) { + stacks[i] = AEItemStack.loadItemStackFromNBT(listTag.getCompoundTagAt(i)); + } + return stacks; + } + +} diff --git a/src/main/java/com/glodblock/github/util/FluidSyncHelper.java b/src/main/java/com/glodblock/github/util/FluidSyncHelper.java new file mode 100644 index 00000000..d5bc1848 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/FluidSyncHelper.java @@ -0,0 +1,94 @@ +package com.glodblock.github.util; + + +import appeng.api.storage.data.IAEFluidStack; +import com.glodblock.github.FluidCraft; +import com.glodblock.github.inventory.AEFluidInventory; +import com.glodblock.github.inventory.IAEFluidTank; +import com.glodblock.github.network.SPacketFluidSlot; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.ICrafting; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class FluidSyncHelper { + private final IAEFluidTank inv; + private final IAEFluidTank cache; + private final int idOffset; + + public FluidSyncHelper(final IAEFluidTank inv, final int idOffset ) + { + this.inv = inv; + this.cache = new AEFluidInventory( null, inv.getSlots() ); + 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.getSlots(); ++i ) + { + if( data.containsKey( i + this.idOffset ) ) + { + this.inv.setFluidInSlot( i, data.get( i + this.idOffset ) ); + } + } + } + + private void sendDiffMap( final Map data, final Iterable listeners ) + { + if( data.isEmpty() ) + { + return; + } + + for( final ICrafting l : listeners ) + { + if( l instanceof EntityPlayerMP) + { + FluidCraft.proxy.netHandler.sendTo( new SPacketFluidSlot( data ), (EntityPlayerMP) l ); + } + } + } + + private Map createDiffMap(final boolean full ) + { + final Map ret = new HashMap<>(); + for( int i = 0; i < this.inv.getSlots(); ++i ) + { + if( full || !this.equalsSlot( i ) ) + { + ret.put( i + this.idOffset, this.inv.getFluidInSlot( i ) ); + } + if( !full ) + { + this.cache.setFluidInSlot( i, this.inv.getFluidInSlot( i ) ); + } + } + return ret; + } + + private boolean equalsSlot(int slot ) + { + final IAEFluidStack stackA = this.inv.getFluidInSlot( slot ); + final IAEFluidStack stackB = this.cache.getFluidInSlot( slot ); + + if( !Objects.equals( stackA, stackB ) ) + { + return false; + } + + return stackA == null || stackA.getStackSize() == stackB.getStackSize(); + } +} diff --git a/src/main/java/com/glodblock/github/util/ModAndClassUtil.java b/src/main/java/com/glodblock/github/util/ModAndClassUtil.java new file mode 100644 index 00000000..49b122d7 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/ModAndClassUtil.java @@ -0,0 +1,30 @@ +package com.glodblock.github.util; + +import appeng.api.config.ActionItems; +import cpw.mods.fml.common.Loader; + +public final class ModAndClassUtil { + + public static boolean GT5 = false; + public static boolean GT6 = false; + + public static boolean isDoubleButton; + + public static void init() { + try { + ActionItems x = ActionItems.DOUBLE; + isDoubleButton = true; + } catch (Exception e) { + isDoubleButton = false; + } + + if (Loader.isModLoaded("gregtech") && !Loader.isModLoaded("gregapi")) + GT5 = true; + if (Loader.isModLoaded("gregapi") && Loader.isModLoaded("gregapi_post")) + GT6 = true; + + } + + + +} diff --git a/src/main/java/com/glodblock/github/util/NameConst.java b/src/main/java/com/glodblock/github/util/NameConst.java new file mode 100644 index 00000000..ca588030 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/NameConst.java @@ -0,0 +1,29 @@ +package com.glodblock.github.util; + +import com.glodblock.github.FluidCraft; + +public class NameConst { + + public static final String BLOCK_FLUID_DISCRETIZER = "fluid_discretizer"; + public static final String BLOCK_FLUID_PATTERN_ENCODER = "fluid_pattern_encoder"; + public static final String BLOCK_FLUID_PACKET_DECODER = "fluid_packet_decoder"; + public static final String BLOCK_FLUID_INTERFACE = "fluid_interface"; + + public static final String ITEM_FLUID_DROP = "fluid_drop"; + public static final String ITEM_FLUID_ENCODED_PATTERN = "fluid_encoded_pattern"; + public static final String ITEM_PART_FLUID_PATTERN_TERMINAL = "part_fluid_pattern_terminal"; + public static final String ITEM_FLUID_PACKET = "fluid_packet"; + + public static final String TT_KEY = FluidCraft.MODID + ".tooltip."; + public static final String TT_INVALID_FLUID = TT_KEY + "invalid_fluid"; + public static final String TT_FLUID_PACKET = TT_KEY + "fluid_packet"; + public static final String TT_ENCODE_PATTERN = TT_KEY + "encode_pattern"; + + public static final String RES_KEY = FluidCraft.MODID + ":"; + + public static final String GUI_KEY = FluidCraft.MODID + ".gui."; + public static final String GUI_FLUID_PATTERN_TERMINAL = GUI_KEY + ITEM_PART_FLUID_PATTERN_TERMINAL; + public static final String GUI_FLUID_PATTERN_ENCODER = GUI_KEY + BLOCK_FLUID_PATTERN_ENCODER; + public static final String GUI_FLUID_PACKET_DECODER = GUI_KEY + BLOCK_FLUID_PACKET_DECODER; + +} diff --git a/src/main/java/com/glodblock/github/util/ObjectArrayIterator.java b/src/main/java/com/glodblock/github/util/ObjectArrayIterator.java new file mode 100644 index 00000000..7f86db6d --- /dev/null +++ b/src/main/java/com/glodblock/github/util/ObjectArrayIterator.java @@ -0,0 +1,163 @@ +package com.glodblock.github.util; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * An {@link java.util.Iterator Iterator} over an array of objects. + *

+ * This iterator does not support {@link #remove}, as the object array cannot be + * structurally modified. + *

+ * The iterator implements a {@link #reset} method, allowing the reset of the iterator + * back to the start if required. + * + * @param the type to iterate over + * @since 3.0 + * @version $Id: ObjectArrayIterator.java 1734648 2016-03-11 23:51:22Z ggregory $ + */ +public class ObjectArrayIterator implements /*Resettable*/ Iterator { + + /** The array */ + final E[] array; + /** The start index to loop from */ + final int startIndex; + /** The end index to loop to */ + final int endIndex; + /** The current iterator index */ + int index = 0; + + //------------------------------------------------------------------------- + /** + * Constructs an ObjectArrayIterator that will iterate over the values in the + * specified array. + * + * @param array the array to iterate over + * @throws NullPointerException if array is null + */ + @SafeVarargs + public ObjectArrayIterator(final E... array) { + this(array, 0, array.length); + } + + /** + * Constructs an ObjectArrayIterator that will iterate over the values in the + * specified array from a specific start index. + * + * @param array the array to iterate over + * @param start the index to start iterating at + * @throws NullPointerException if array is null + * @throws IndexOutOfBoundsException if the start index is out of bounds + */ + public ObjectArrayIterator(final E array[], final int start) { + this(array, start, array.length); + } + + /** + * Construct an ObjectArrayIterator that will iterate over a range of values + * in the specified array. + * + * @param array the array to iterate over + * @param start the index to start iterating at + * @param end the index (exclusive) to finish iterating at + * @throws IndexOutOfBoundsException if the start or end index is out of bounds + * @throws IllegalArgumentException if end index is before the start + * @throws NullPointerException if array is null + */ + public ObjectArrayIterator(final E array[], final int start, final int end) { + super(); + if (start < 0) { + throw new ArrayIndexOutOfBoundsException("Start index must not be less than zero"); + } + if (end > array.length) { + throw new ArrayIndexOutOfBoundsException("End index must not be greater than the array length"); + } + if (start > array.length) { + throw new ArrayIndexOutOfBoundsException("Start index must not be greater than the array length"); + } + if (end < start) { + throw new IllegalArgumentException("End index must not be less than start index"); + } + this.array = array; + this.startIndex = start; + this.endIndex = end; + this.index = start; + } + + // Iterator interface + //------------------------------------------------------------------------- + + /** + * Returns true if there are more elements to return from the array. + * + * @return true if there is a next element to return + */ + @Override + public boolean hasNext() { + return this.index < this.endIndex; + } + + /** + * Returns the next element in the array. + * + * @return the next element in the array + * @throws NoSuchElementException if all the elements in the array + * have already been returned + */ + @Override + public E next() { + if (hasNext() == false) { + throw new NoSuchElementException(); + } + return this.array[this.index++]; + } + + /** + * Throws {@link UnsupportedOperationException}. + * + * @throws UnsupportedOperationException always + */ + @Override + public void remove() { + throw new UnsupportedOperationException("remove() method is not supported for an ObjectArrayIterator"); + } + + // Properties + //------------------------------------------------------------------------- + + /** + * Gets the array that this iterator is iterating over. + * + * @return the array this iterator iterates over + */ + public E[] getArray() { + return this.array; + } + + /** + * Gets the start index to loop from. + * + * @return the start index + */ + public int getStartIndex() { + return this.startIndex; + } + + /** + * Gets the end index to loop to. + * + * @return the end index + */ + public int getEndIndex() { + return this.endIndex; + } + + /** + * Resets the iterator back to the start index. + */ + //@Override + public void reset() { + this.index = this.startIndex; + } + +} diff --git a/src/main/java/com/glodblock/github/util/RenderUtil.java b/src/main/java/com/glodblock/github/util/RenderUtil.java new file mode 100644 index 00000000..a0ef8c1d --- /dev/null +++ b/src/main/java/com/glodblock/github/util/RenderUtil.java @@ -0,0 +1,32 @@ +package com.glodblock.github.util; + +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.util.IIcon; + +public final class RenderUtil { + + public static void renderItemIcon(IIcon icon, double size, double z, float nx, float ny, float nz) { + renderItemIcon(icon, 0.0D, 0.0D, size, size, z, nx, ny, nz); + } + + public static void renderItemIcon(IIcon icon, double xStart, double yStart, double xEnd, double yEnd, double z, float nx, float ny, float nz) { + if (icon == null) { + return; + } + Tessellator.instance.startDrawingQuads(); + Tessellator.instance.setNormal(nx, ny, nz); + if (nz > 0.0F) { + Tessellator.instance.addVertexWithUV(xStart, yStart, z, icon.getMinU(), icon.getMinV()); + Tessellator.instance.addVertexWithUV(xEnd, yStart, z, icon.getMaxU(), icon.getMinV()); + Tessellator.instance.addVertexWithUV(xEnd, yEnd, z, icon.getMaxU(), icon.getMaxV()); + Tessellator.instance.addVertexWithUV(xStart, yEnd, z, icon.getMinU(), icon.getMaxV()); + } else { + Tessellator.instance.addVertexWithUV(xStart, yEnd, z, icon.getMinU(), icon.getMaxV()); + Tessellator.instance.addVertexWithUV(xEnd, yEnd, z, icon.getMaxU(), icon.getMaxV()); + Tessellator.instance.addVertexWithUV(xEnd, yStart, z, icon.getMaxU(), icon.getMinV()); + Tessellator.instance.addVertexWithUV(xStart, yStart, z, icon.getMinU(), icon.getMinV()); + } + Tessellator.instance.draw(); + } + +} diff --git a/src/main/java/com/glodblock/github/util/SetBackedMachineSet.java b/src/main/java/com/glodblock/github/util/SetBackedMachineSet.java new file mode 100644 index 00000000..6336362b --- /dev/null +++ b/src/main/java/com/glodblock/github/util/SetBackedMachineSet.java @@ -0,0 +1,60 @@ +package com.glodblock.github.util; + +import appeng.api.networking.IGridHost; +import appeng.api.networking.IGridNode; +import appeng.api.networking.IMachineSet; + +import javax.annotation.Nonnull; +import java.util.Iterator; +import java.util.Set; +import java.util.Spliterator; +import java.util.function.Consumer; + +public class SetBackedMachineSet implements IMachineSet { + + private final Class machineClass; + private final Set backingSet; + + public SetBackedMachineSet(Class machineClass, Set backingSet) { + this.machineClass = machineClass; + this.backingSet = backingSet; + } + + @Nonnull + @Override + public Class getMachineClass() { + return machineClass; + } + + @Override + public int size() { + return backingSet.size(); + } + + @Override + public boolean isEmpty() { + return backingSet.isEmpty(); + } + + @Override + public boolean contains(Object o) { + //noinspection SuspiciousMethodCalls + return backingSet.contains(o); + } + + @Override + public Iterator iterator() { + return backingSet.iterator(); + } + + @Override + public void forEach(Consumer action) { + backingSet.forEach(action); + } + + @Override + public Spliterator spliterator() { + return backingSet.spliterator(); + } + +} diff --git a/src/main/java/com/glodblock/github/util/Util.java b/src/main/java/com/glodblock/github/util/Util.java new file mode 100644 index 00000000..155f4721 --- /dev/null +++ b/src/main/java/com/glodblock/github/util/Util.java @@ -0,0 +1,102 @@ +package com.glodblock.github.util; + +import appeng.api.config.SecurityPermissions; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridHost; +import appeng.api.networking.IGridNode; +import appeng.api.networking.security.ISecurityGrid; +import appeng.api.parts.IPart; +import com.glodblock.github.common.item.ItemFluidPacket; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidContainerRegistry; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.IFluidContainerItem; + +public final class Util { + + public static EnumFacing from(ForgeDirection direction) { + switch (direction) { + case WEST: return EnumFacing.EAST; + case EAST: return EnumFacing.WEST; + case UNKNOWN: return null; + default: { + int o = direction.ordinal(); + return EnumFacing.values()[o]; + } + } + } + + public static ForgeDirection from(EnumFacing direction) { + switch (direction) { + case WEST: return ForgeDirection.EAST; + case EAST: return ForgeDirection.WEST; + default: { + int o = direction.ordinal(); + return ForgeDirection.values()[o]; + } + } + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, IGrid grid) { + return grid == null || hasPermission(player, permission, (ISecurityGrid) grid.getCache(ISecurityGrid.class)); + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, IGridHost host) { + return hasPermission(player, permission, host, ForgeDirection.UNKNOWN); + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, IGridHost host, ForgeDirection side) { + return host == null || hasPermission(player, permission, host.getGridNode(side)); + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, IGridNode host) { + return host == null || hasPermission(player, permission, host.getGrid()); + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, IPart part) { + return part == null || hasPermission(player, permission, part.getGridNode()); + } + + public static boolean hasPermission(EntityPlayer player, SecurityPermissions permission, ISecurityGrid securityGrid) { + return player == null || permission == null || securityGrid == null || securityGrid.hasPermission(player, permission); + } + + public static ItemStack copyStackWithSize(ItemStack itemStack, int size) + { + if (size == 0 || itemStack == null) + return null; + ItemStack copy = itemStack.copy(); + copy.stackSize = size; + return copy; + } + + public static FluidStack getFluidFromItem(ItemStack stack) { + if (stack != null && (stack.getItem() instanceof IFluidContainerItem || FluidContainerRegistry.isContainer(stack))) { + if (stack.getItem() instanceof IFluidContainerItem) { + FluidStack fluid = ((IFluidContainerItem) stack.getItem()).getFluid(stack); + if (fluid != null) { + FluidStack fluid0 = fluid.copy(); + fluid0.amount *= stack.stackSize; + return fluid0; + } + } + if (FluidContainerRegistry.isContainer(stack)) { + FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(stack); + if (fluid != null) { + FluidStack fluid0 = fluid.copy(); + fluid0.amount *= stack.stackSize; + return fluid0; + } + } + } + return null; + } + + public static boolean isFluidPacket(ItemStack stack) { + return stack != null && stack.getItem() instanceof ItemFluidPacket; + } + +} diff --git a/src/main/resources/assets/ae2fc/lang/en_US.lang b/src/main/resources/assets/ae2fc/lang/en_US.lang new file mode 100644 index 00000000..dde8ce6d --- /dev/null +++ b/src/main/resources/assets/ae2fc/lang/en_US.lang @@ -0,0 +1,17 @@ +item.fluid_drop.name=drop of %s +item.fluid_encoded_pattern.name=Encode Pattern +item.part_fluid_pattern_terminal.name=ME Fluid Pattern Terminal +item.fluid_packet.name=Fluid Packet + +tile.fluid_discretizer.name=Fluid Discretizer +tile.fluid_pattern_encoder.name=Fluid Pattern Encoder +tile.fluid_packet_decoder.name=ME Fluid Packet Decoder +tile.fluid_interface.name=Fluid Interface + +ae2fc.tooltip.invalid_fluid=Invalid fluid! +ae2fc.tooltip.encode_pattern=Encode Patter +ae2fc.tooltip.fluid_packet=Use a Fluid Packet Decoder to\nconvert this back into fluid. + +ae2fc.gui.part_fluid_pattern_terminal=Fluid Pattern Terminal +ae2fc.gui.fluid_pattern_encoder=Fluid Pattern Encoder +ae2fc.gui.fluid_packet_decoder=ME Fluid Packet Decoder diff --git a/src/main/resources/assets/ae2fc/textures/blocks/fluid_discretizer.png b/src/main/resources/assets/ae2fc/textures/blocks/fluid_discretizer.png new file mode 100644 index 0000000000000000000000000000000000000000..d08e7976c6388cc6641e5aa5336e2fbd3688951c GIT binary patch literal 2004 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 JtEG4P{{qKgT(STF literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/fluid_interface_arrow.png b/src/main/resources/assets/ae2fc/textures/blocks/fluid_interface_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..36345e39da884e62c5646a3cf7041de0d964347f GIT binary patch literal 194 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ot`d^Ar`&KA^}_epLbwPV9eCj zKdE-Q<=niL0?ZBxr{5W?WPU3&WxT9&qG^`W`;JRBe|rVyoOPMBHG+ZL?4Wh|yM~wF zH| zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Yuk|ZY#{pS=u0uqSjIII!AfsgOUnqAf1o}L(2 zMkXo{LcAvlRQu=euKvQ0$vb7m2N#{w_}OS9CkR2WUz}gkc7FFCeT0{Obq5$-2CZ3t zecJj7{qjsWYfygeS7DEE*oKbfZP1LB8AAQ&B>H*vlih}5TY`(he{?nTYRgufuIx9& z>B}&-hVWl~?64jvRl!1rxfwEge#6o+>rHsw{L;`f2C^8ZX4lyg0D0No1yFwk=mq2> zsdx0R^aehs-EZEobQ>cGUk;Fb4gQ4q!@}lz#IFN3YX50F`|Z7k-rL$;vJQy49Z^~x zbjBPgW|%T_n#1Gq3S>W@h$rZz3n;I;=!~aOBnB&8k)=wBI`(u$g_q zc#d2KSH_NqI(rjMH37^OZVL2S!`By@>&CfWp@5YuL6r%{2vfd&)PD{B&ZBxS10vGl zVgD$24%t=u)(DBGLeOz*fNu613f{zQiznV z+W;WK+5%_DfC25u2szT8*%JZB0D=-cXJa{o0TST~C)pa~Sa}XS*2Wx9Q{09^6EeK$f+F`0+L%hLRYuBI3kLP!c6M~qL5r-JY)wn%Zsmr!C!m8+>J z6V+8;Lya{xQVo@&*(S|3-$IKmb;MdX-FE4&`yP7i>9V$2{q+0>YqVM8$<)yEmo@Y% z?~e%@t!LlRy#os;7|`{jpoWAi$6oKW zK0UZfKd_l)qv*I6wzv!}HKYh8OucMb%UYenUR0lU?~6Bt^8&Pmv$R6MED%fQyi78( ze($5p2?%ayOXdi2$wcW;7aaE;I0tWj^Y7IyH6@WlSaB!RuTyq8l@SX$p6J=R0S`D$ z(3Rk*dLVPYw1tCD;~Mb}*4b6-%TLtLs%I9c7e@CwxXbJfQf%T+!aZi5@RBtctU>|4 zsUYb=kB{r7`Nc=BWFlf_{Yjx*N=lm^F?L>UPdiV$2`zsPzgsYhIgr`T55?qhJ<+|%` zROP4a^~1t&S6zQUMnL^R1a!O8v3aVF)aLOPKU|T*@@P29UjqB!ddIcO{g}$PcJ3$| zd3@R1HKUvWyPl;*h?Vtt%*knPpCCp@7BW2p<-Su#L#s7UZi(LADJI6MaqQ-1Yb}0se9%m z#^z)o8v9*@o*JhPlPs6?huwb4qrNoOM{g&@49*=#<4`^UcgnK!xBOqP$(i@MS9aM` zqweJ!>rct~;$W%ujS?Hoj+_$IbI#g$qv*XoQFzjOtZDBlJNK*)vyK&u%IQaerqU`ljNtO$70004mX+uL$Nkc;*aB^>EX>4Tx0C=2zkv&Mm zKpe$i(`rR34t5Z6$WS|35WjGgDi*;)X)CnqU~=h)(4-+rad8w}3l4rPRvlcNb#-tR z1i=pwCr2km7b)?7NufoI2gm(*ckglc4iFj@rka5%KKJM7Qwk;nd?N8I(+!Jwop^fF(mC%Fhgn%th|h^f4Z0xl zBiCh@-#C{X7Itp zhU+wkk-#F7NI`^*8a7aYg&3_GDJIgiANTMNIsO#6WO8kQkz*cJsE`~#_#gc4)+|np zyGfxq(Dh>5A0t3;7iiXP`}^3ong4(~Q-+=GV_339x+*ltOVeKcjRRjngP4S&7$Y&pBS3J- z!vJ{LwlvQ2{wha^q)m1KxX@rsJ@02f$)xsJo;lgy@xv>H`4H<8P{o z>3h&Lkw4GR1K?ycjV|^`D3rLnB_d}12G1&Oruoj7fk~!FLKH07*qoM6N<$g1;`LbpQYW literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoder.png b/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoder.png new file mode 100644 index 0000000000000000000000000000000000000000..967533398c3f7f96cd680919165d539df15cc74a GIT binary patch literal 1919 zcmV-_2Y~pAP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O=0%w#2Fm{bv++2!ahd9AmTAoxvOa-5Qf>Pc`SP z7b|KI0okAywg3Iw=^s2IE|BvYLkhv*amgjg=tQ$WdDd*)tk;J*IyZDW2LvRG*6yD% zEP4Z7z%PqlmYfW_zY>(%8rW_J@)9I*DL6DPMV5C3rY=Ew39_;@yZw7xnnQ|uy@YzK zATCPWUmBIOD!~URYG|Se^I__55{mpp8 ztOa~#QDoL``qvO3g9)%aE#EeCdh^CNCo0b$`ea!v9B$w~dn*j+Vc;$dLR)luhh8|_ zNvHb(_Rry-m|j_=?o=!{7P&G!b@jx{+k1_@w|#Zl1uQwNMI9?)no#z-jUbrQ0*d1G z=<6&AB_dr4Vyv2CHk87N1rT?3Tsd1&^#eyom)3Sx9-Zb?Uj?i2gtilk+nDVyJCrqzO?<1F(Y2^&>0Lw^2m@$wWHQ0a% z_vKVS9ekAAfLy>@Kpp)7a?tHz3|l~80$b04 zjfyBU92o&nW)sCa*?<6hFBL|Lvo-)5Bmq6l_l zDQD=+kemZ$@Mlmq+3e;vzlBY=xTP%@gimqBmr%6Cl1i>pZABi}P_@RIYHr*RO3gJ- zEi`ShrItHlt=sOp@1bjtJ@tH|worX2U!cYdHJL~aK0Q$bRdsJ^#tVcD%z&6U2I4vp zKtRjD%tUrh266*4lLT4e1xEUTO=Xigh$HJ7i|>45cR}te+$gx0xXGu;(SYuc$W7d+ z`77L>pjOART_$3C77m{pi|p&tvPArFno6Xj^QyHrk2ZJb8oU+eN6R=MqdFYJG$j4@chBhlxxsU z4!Lz{=WU6#VX5v|Bi);*a*o<>Tt`3n?mO4nn&>o-^TVj-(>i`;h4~i#9}T&(mNuBx zPOIXhRTq!StFWSXzWH>VUOE@n{_MlsmOe{>_lDA!D|s3PgOQ$M`HPnRjQv^zJq`L_ zz(#+@eHlU@rR4_-$p0Gf+i|)#@qzLGj@plc-iF-k=%+v8b&k{Ln_#@`Hs#S!`a_=H zjMImCYHPl1DB;oCK<8+Cw=2JH>GNHASaqHT?C&@UyK=ia=YD+&px>Wlw?_uuj)wCl zaJV)dC-1QqTf(uxvio(bY!l&M(EgQUiE_t*|FrHHFU{uC!+{tdn${dL;d1HFbvxiM z+i?YA4Q=xb!9Z4>vG6g048^9RL6UglR)VP)S2WAaHVTW@&6?004NL zeUUv#!$2IxU(;$uDh_rKamY|RSrET)lqwd%LTM|s>R@u|htQ-UNpW!$Tni3+H=u-+N1AHR!EYl5(c%689)6zNb6Ngz@Qi#uq zM-93l@gvt|m)|&-92R(H*vO{mh{MEUse|PXW@SSqo+OSasz&)j&Si!37H73mW37Ag z7lsPja)#?PhmpV{l1M>>j2bpjfrS{Y8Yw2yv>*5I4>|r6xny!}fRSS!Rj800KlmT~ z?$#_$jJrvpIMDTC+aDu9a2IISZTtJ!wwot_{~5T_+Wu+-nE52V-qs>VK>s#yaoyJB zJ>YT&7<|$tLvkcPO`%u>-p}Zp^1#3?(7WpP*4oGE1CXVzmTrK9Lm*nB>@|;f_jLC5 z@0nJAKZ_G`v2XC#M*si-24YJ`L;!LCasYCZFQone000SaNLh0L04^f{04^f|c%?sf z00007bV*G`2jm6<1p);i-_CX>@2HM@dak zSAh-}0002$NklU5JjJj5vh_WC)gCJIl>-;772mGnJ5r&EQ-AW%61fB zQbZU-AW_7ZEKM=xrPM zD!#=iM;w2C2X;W&W3^2^TivJFNZ5n@5^RSp-M1CnH4-3-qOUurX*wBc-09LhTQPtT z;+5c?oaHg~eLoR&T}K#(D5VfW%sg)+NfOg}BoKSg`2i5*cOEPK0wn+d002ovPDHLk FV1iAQccuUU literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderBottom.png b/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderBottom.png new file mode 100644 index 0000000000000000000000000000000000000000..38ca52d36ed9d548b1e34560913819177c8ab72b GIT binary patch literal 2028 zcmV-P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+ND=*mh319{qHJf34$WJ9OK71vx8awpb@*us_N;U z7dw%l!j%iA+O&WE?&dGK5HJ`|9777h%H@+!m_>;8zVdv6_4B!2tO`$hvjC3W{MNbcgQd9a^Y<73Ckc}2$u#O z*(Fz96#9##iW`a0yIELZ`c2bcfhe9U$W#O|2D0QooB7G4?^^GpmH^DE8Mq0?QHBCz zFgKnuO&Ec`f=V;M+MW|~5$l9PnqiKz!ECSF#6|FxTW%8VWFAR>7dLRd*AGKpe3O6y zCluHKjv#<#LQfw(rZhGEV{!dow=q%c8JrYzhf zmxV0vN%Dt%lpM`sAEpPZ! z=FFh(51E_1nMM!1yXjfW;RCIdx=F8?ZxdCb* zL2-Ch;8M`jhK-D<1If5U;8r#d4v}&l_5)AA(^b^oY)E=NSZQV`(h>$MI*N-A}DYBx2#?dFH(y913LjbK> z+f0%FEMHMexO67j;P9r)DV)d8 zknY`lpVlvDC%q}Ndu#D4IPnF|uD29+3V-lvEPgnBKF8)nH&0#jzS4i+n?E(M;v)YY z$v?&BU-qfh^^aT+jQ{`vglR)VP)S2WAaHVTW@&6?004NLeUUv#!$2IxU(;$uDh_rK zamY|RSrET)lqwd%LTM|s>R@u|htQ-UNpW!$Tni3+H=u-+N1AHR!EYl5(c%689)6zNb6Ngz@Qi#uqM-93l@gvt|m)|&-92R(H z*vO{mh{MEUse|PXW@SSqo+OSasz&)j&Si!37H73mW37Ag7lsPja)#?PhmpV{l1M>> zj2bpjfrS{Y8Yw2yv>*5I4>|r6xny!}fRSS!Rj800KlmT~?$#_$jJrvpIMDTC+aDu9 za2IISZTtJ!wwot_{~5T_+Wu+-nE52V-qs>VK>s#yaoyJBJ>YT&7<|$tLvkcPO`%u> z-p}Zp^1#3?(7WpP*4oGE1CXVzmTrK9Lm*nB>@|;f_jLC5@0nJAKZ_G`v2XC#M*si- z24YJ`L;!LCasYCZFQone000SaNLh0L04^f{04^f|c%?sf00007bV*G`2jm6<1S%%p zCodKN000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}00030Nkl)?y!0Nb|le!np@0L%-Az;mYJAVL>OZ*#y~^>=)H5EC+B%y3RD$$$K7uLsyYFH z2^10Z-Y zd6@F*VcMsfN~oD!J748~DzV0R4o>c>qAD;m#u&Iet+lJM8IafXpX}cItxq|%o_E@N zpY~HjD^YFk1jx}$X-|M;Of|PjY^`w|2d%X~tWSXYCIIBy=llbC7s;Wh`|m>l0000< KMNUMnLSTXoJ+w;z literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderSide.png b/src/main/resources/assets/ae2fc/textures/blocks/fluid_pattern_encoderSide.png new file mode 100644 index 0000000000000000000000000000000000000000..b2b560145610c64d4124845fc61538cc344f0ce9 GIT binary patch literal 2159 zcmV-#2$1)QP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+ND=nmMf_a{AU%t1SAH*av0C)9lYgtKmxYYuHn9$ zR01hL5)l%(RQun*UHyZHnYFS`f{)&7JT}_M37w#?C(W9)o%QA8QaJ(b}TH$z6xI$U0w>rHt5 ztig}bOX5(o@e^OIKp9+s=W$`%^g;6lo5RZINBw47D;!?vKD`wN>TTc_2EHx1xv}R@ zJLT#=K<+c$lhY@U#2&-^;*pt1Jw4&^_FhBp?b=rYwMq$OUARv7=CDG0{{L zKz%`|7)ZHywS1vzH%@zn0xVa;R3<1REcvmh-%R?h^geQlNUfZqi(npU2sZ|EqsBBa zLVbbiCcwMA2IL~vfI>H64%lGQ`BIS`Jz~pDq7CLbv{!PI)NB1vWMSLE88VFKgVWeWq+<** z#*|<)L38pdVSa)nb2iy#mwlETa?Gh9e2Oiu_#!2gSW@L`D)PAcDmBztQ=^7ZYPLDm ze3KShY^fvGy6LvN?z{BRV^4>*#p+x61J-D<#*?WrPY!F4id?Ol@d71-Gcd-Mz_<(s z5YRk0(}Aqz!Q9|XM@CjShLdh^Q%NWCY?ZN%*mXMGJ(&9=-juiB^2WbqP7Uh*lDWy7 zYVL`*53JR3ZI{W|m4yRSZIFGPTjGE}+@>7V?tIr8lSZ4mwGC+b`O$C}Jxx%T5a8_z z-Gr8ZQvGJCD%a+6t>f-UCF{n`?XH#@KpA~p5He+06ASR46I}(FWYz180_69ECRDv- z-g@h@PI;*{ewpsrqq@5&yhd}}^N*{?MRsp4sy82hbTKY{x-9F=o3+`S4i%r4`R!PD zn`G|WBi^doS%=783(6(Tl}yA9a($#w0^cU!_ki8wrI}Q^`0WvGI1nVaj)dawB<78_ zdwAp&-?qa4r-$m{;$2nuoA9A-ht%e4vm)t&PGR&g-pPGRinlQSlq71c7)kQgf|)d? z=1pkI2f%u=Kph18Mle4R{57Q)YagKgnRDMafT?bbzD5v(z8wJlcL1tCJ}_>;Z96K( z56B(h_W9xJ1GvxISKlJ|OTb1?U7>dpGd)hTcn2;YS0&v~!w!so6N5h^rY2-{D?Zyw z@(pxF?pe#C4v5tLDs9yrw7Fr)Z|Q2&iKqB>g@m>v`y4TL6K)@2v5$X>*Px{FWBhhZ zsk8HRon;b>xY+vEvyW9e6CJw)QvepD4orN;Rtg0J6>%?c_6vssI21glR)VP)S2WAaHVTW@&6?004NL zeUUv#!$2IxU(;$uDh_rKamY|RSrET)lqwd%LTM|s>R@u|htQ-UNpW!$Tni3+H=u-+N1AHR!EYl5(c%689)6zNb6Ngz@Qi#uq zM-93l@gvt|m)|&-92R(H*vO{mh{MEUse|PXW@SSqo+OSasz&)j&Si!37H73mW37Ag z7lsPja)#?PhmpV{l1M>>j2bpjfrS{Y8Yw2yv>*5I4>|r6xny!}fRSS!Rj800KlmT~ z?$#_$jJrvpIMDTC+aDu9a2IISZTtJ!wwot_{~5T_+Wu+-nE52V-qs>VK>s#yaoyJB zJ>YT&7<|$tLvkcPO`%u>-p}Zp^1#3?(7WpP*4oGE1CXVzmTrK9Lm*nB>@|;f_jLC5 z@0nJAKZ_G`v2XC#M*si-24YJ`L;!LCasYCZFQone000SaNLh0L04^f{04^f|c%?sf z00007bV*G`2jm6<1qB_CX>@2HM@dak zSAh-}0003#Nkl-(Lzk52%a%bDrq+LuNEn9t`7heN#g0346U z-sHt%fvV;>old_5gTa8&XoUA35#bre<1sNt+P0-_+q(i0A%s8(;Uz%`0p}d5N{sPN zkWwP0#Nlw@754i*0L$ePRlO59=SV5#1Og zR2X)2AV-$1O-jP;mf>ARfKaBL}ETFKtgy2~He(1gTQh3L2@$@OvLt3Vn!LvOS*p{C)p> zV{u__-`IgMNs{(e=PNaMKbPHmcf<4Goox!QC*%1yQb~H|i|pRD=hLO@lJvy>puQ}Y z-Pef6LzVix!PH(D16q;}AMM4|TVVnL zOSXgQWu(!Z@dm(qWh&U|NsH zny%u^r9RO2a$LNcbo%1Hr?CzTStL@x>Nz%UbHUR#e*krUe2)Nx)^&4@``QwQxkM^v zPJn(RD>P>yR^}M9ir}xJ)n}Kwa(!&9(nPbxe#hG0l6RP%u6f^pRlSBTb%! z6x`@J=4Mp^;lUkXtAM`VNYo`%4WIHG@3pJp?}F#r2nA1p3v=biI7$=d-VmT|!Sgcdp!e_m5k1bDO_^wsrNriOW}76VHGB;YYQ* z-|znG&12`EC7Xx7bIyKqcd++|RwYlrYh1pXdZ@+ME`@}oH{CSc8{rQ-* o@fM!E_Wt9v1@2d_e%>?5>VD>g8h*Eiqq{oAW7FHVeI8IvSwqP$dE z5%c9_j1G(cwI7FAOqar?2eBkgzD~xukyon+l5}z0tFH5P=N=-=SFy`#RPFeoKuglh zY$wEIgK|)#4KJ|d?;k(Qz;i8mE$2Wdv}w~@+KK4u&T^IPY!K6xXYYWS4iW@Du9BPw=YZVEV;>fh%~L;ZmaE#%A$s*o2IEjLo*CTAe4AJ;JBj% z@$`V9L}L9K;+4gqBN|ES#cxo{New=R%@G zg}PK%ayVO5YNSXNT+7!B6y{(io4zkGvYCW_@ZSB;_xB@Dlpb!KnI|d8Bxlf)7mH|a z_e*A#KJKqt9_XeC!X!z5OD0L&sVn!7y)%2DN84^vL3&C?^k6Zo^EQs?Ttj&4ANi&U z)wQHddsb+_8`!-&Qqlvs=o85@ZBSy9wm2Gyz7~lFcK++TebM}sa%rwwd_jI59?lM3 z-`$fBKEU0n!#8i|3cD{yhbsHypWsw(cWn6imuDBGCzB&rGY`M{N!u(hESKKTKYIEL DnAW@{ literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/pattern_terminal_medium.png b/src/main/resources/assets/ae2fc/textures/blocks/pattern_terminal_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..a9271aa88a68991712c9cac54f5b085063f3190d GIT binary patch literal 1506 zcmbVMON}p;hK z^0B$uS7!x5n5$LGD}3IYjG0~h|JuDR#HW|T>Zw=|=D$kDj+ZX1-V%hJ3#8FxP5W)= zP+vwaZDD!fha4>ki-!jxa@H^dE!-x7DgOP_FCrkWDW1&RsvTOmL#i7QUfo!3I2&t@ z;fjY3fyDvj1U_a64E$~o!+|M|cp+aW$BGC>5VmHDONl_xwClj45e9j=AURr22Sr2H z3aX*!-UPa;6&1CpXjw_spjv=g157@VON(3&u9S~XWbvITb{GqxqV)THxu2D3)K)aZ zFcei+bY0>IDINw44WuB>Oc}~Jb|MloLIaR6q89BjQ{=C{#3A zO-vdAT_?qby{J19?m7zYVjl-A=2$Joh8@ai+@b$LonAj8fVk3TG9co~*hAjFJT+Cn*9`bdu;}SDYM^=k~yuw%@^= z^ndbV42!A9`Y6InZSJk7WSzoQSCV2KTcP~hz|q~d!k)r;w@8+0o8p_a(cyr1w}`i~ zyT^Yvc?(>xm6sZ&&;K}oV*U!4vEKjU*4FhqZSRvy$N&7jy60N`QpNS|-2CC(!^MyG z315DFaP!v}=l0yrTzepY|Lxub*L%&?na%HRocL{FP(Ql+Oja`Qze~=4c60y5doNsm zPoKSeb@&at|1g7JIrG-i!jH}Qnz^}kgYEe1(EiMa7q6a8x^S(sT)uqdgOC3Kw>0JS literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/blocks/terminal_broad.png b/src/main/resources/assets/ae2fc/textures/blocks/terminal_broad.png new file mode 100644 index 0000000000000000000000000000000000000000..5c35091fce4f35640d0db7a983d7841c1169f988 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!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}|R53ifny46*P}e$f-@ zD0ocEgR9MWOGxT}>DMcpHtt+#6=uehz2d~b;|2^WjLE`>PGoK55<1j;g&`o};Ek3K vE5tKW8P+Z7;cdQ>B;lY3)NT`Zkbz;nX?M|=Kl;2t;~6|%{an^LB{Ts5_i#@b literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/gui/fluid_packet_decoder.png b/src/main/resources/assets/ae2fc/textures/gui/fluid_packet_decoder.png new file mode 100644 index 0000000000000000000000000000000000000000..4e17ee57f57343de084152ca5880f992b338e240 GIT binary patch literal 5240 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 Ss@OFDaR5_PQ>d1E9r|BoPFNKH literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/gui/fluid_pattern_encoder.png b/src/main/resources/assets/ae2fc/textures/gui/fluid_pattern_encoder.png new file mode 100644 index 0000000000000000000000000000000000000000..045611f803177de95e5f09ef81db6f0cf749c8a1 GIT binary patch literal 7072 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/items/fluid_drop.png b/src/main/resources/assets/ae2fc/textures/items/fluid_drop.png new file mode 100644 index 0000000000000000000000000000000000000000..32ae84ab4b9a1ea82bc5381046b30a1f97c75ca9 GIT binary patch literal 1482 zcmV;*1vUDKP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-_ZvfLmH{O1%o0+JAd<6xGm+#ttKgO^#}OY*kL zPh!R(3n8`D0*~uIz7O{e7ki&vP)jk#=y8P{G8YKR?^j+u`LO!q!Joo|-dz(6m0*

>d<`EHy)RpFh7c73;U2ICX+LFbyz)mslTZo*s!vRu+ z*a|QtV8BNbrAXeGjR<*6AgBO2OW+L*kSaHTnSU~19Kl4UDNQb?L&vXoLzC36;> zftq8soN~^kuqy~u;H$vCK(p~CTiA4qn{8>!Td63Y8miVYa67i-Wuv(p95<3uNC zV2lHSahnXFpm}oU6NSK&xyhN&j7^~okUF_>jV5EDFmz%aba8iM?oGToO>g3j|HPb| z)O`hW4%EHm?G@JA{LaICcd74&Ib?rnI4p7Gq}ho|J0_yxSVcUDb{S7vg%ucW_#=!Q)6LKfR& zJ>NjM&+oE*bZ@L{dBN#ZboVJ3{~i5%j%?t+Np?R0bQ^OC3uEX>4Tx0C=2zkv&MmKpe$iTT4YM4i+pTlA$_T5G~>;RV;#q(pG5I!Q|2}Xws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;lcSTOir6bq^0;@1i`*{oJ3UUoIF7@Cd|nOw&!`4dR(i zQ{%i(9AafrCO#*g(CLE2k6f1=e&bxS*~>FSdNw^z93mD1~ z4$GXkI4jjUYu}Teq!>uke%!@BZ2ME>lE_s7 zBgX=2P$1fV@IUz7tx=qwa*~2^!2e=dA7emZ7icvs>-*TUS|@<-8MsnA{#p~5{Up87 z(IQ8{z&3Dk-O;2y;Bp5DKWU;a+LDi^w^#(;&*+=-z~C*=x90TL-pAn`u^?e6X0GwuF<0NFlr(3}*PEC2ui24YJ`L;(K){{a7>y{D4^000SaNLh0L z01FcU01FcV0GgZ_00007bV*G`2jm4F7c~-b##-Y5002@+L_t(I%VS^|1$aSVVBmi@ zH@E*}YrcH>^8ektcjGmL1)m{eVqy#w+5Yn7%l|K5zQpSaCVaLN;{u!pyn6Ktg(exe kxw#P)hy)EGTk{A40Hu&LM7JIYO8@`>07*qoM6N<$f--Tqu>b%7 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/ae2fc/textures/items/fluid_encoded_pattern.png b/src/main/resources/assets/ae2fc/textures/items/fluid_encoded_pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..1a19d070b68e39ad4939f3925def3c08c5b67a41 GIT binary patch literal 801 zcmV++1K#|JP)EX>4Tx04R}tkvm8OK@>$#;wK`Cg(*ZVwooet@rR{EQ3QpML@;XM?#4tRyN2C} zjiq2KSomrD6*kt|TG$GL;0MIY(n`@rWPP&cHW7N zN3@ybtQLBqmH;q7NH^WwRD3K!#r1jT*K*sfvMSg8xf)Z8t_cB6c-YK2dEo`&!L@u& z@vd;#T+&m*XTn{MG${O_bfdtpiZeb3Rc6>p#72a}!dj+`jWRZu980)gxIJark{_HY za7yvIVk^697W-5$Y*@6X<4Tv^#yAs95k6X(L#U&>9kqZ7+-yap1A+BXi zag4Hph3@-NfAD*@Zo03xkkk-?nrF{@?|{%Yd~bQ)*OBLaA4Am>T-qi7${`GWh`v?Q z+7F;<1J18Xny~{Ho6z!@(;eNHs$>V#X?Q!8Z%RV*4b;yS=2pDtt0R%G_223g10003XNklb=24O&M%DZtqZtnJEUh(u4tkwRVWs5q^zm9l=^VObn!y5QXq0M|LL8OcVlA z%KvAO$2F=p_4(%fz~=n$dDW&E&&W6E-ktxTOFc0elXa>6J6FTprint3CUdeVgx^m8 z3q0Y$xJZ{8N+~F%CgiG3C#yY~!Q5mXV=}pka}$c}BA~9(G{)~n_wLAx{G)Vv!hF-sOPCYe9iD^00000NkvXXu0mjfv0-#Z literal 0 HcmV?d00001