diff --git a/.github/workflows/optimize-images.yml b/.github/workflows/optimize-images.yml new file mode 100644 index 00000000..16d85ce1 --- /dev/null +++ b/.github/workflows/optimize-images.yml @@ -0,0 +1,17 @@ +name: Optimize images + +on: + pull_request: + branches: [ master, main, release/** ] + paths: + - "**/*.png" + + push: + branches: [ master, main, release/** ] + paths: + - "**/*.png" + +jobs: + optimize-images: + uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master + secrets: inherit diff --git a/dependencies.gradle b/dependencies.gradle index fa483a52..622b071a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -36,57 +36,41 @@ def asmVersion = "9.7" +// Make sure these are false before committing +def arch = false +def ae2 = false +def carpenters = false +def avaritia = false +def eio = false +def fl = false +def xu = false +def fmp = false +def gt5u = false +def ic2 = false +def oc = false +def tc = false +def waila = false + +def all = false + dependencies { - implementation("com.github.GTNewHorizons:StructureLib:1.4.24:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.8.32:dev") - implementation("com.github.GTNewHorizons:ModularUI:1.3.1:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.23-1.7.10:dev") - - compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.2-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-754-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.3") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.78:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.3-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.7:dev") { transitive = false } - compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.8:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.0:dev") { transitive = false } - compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - compileOnly("com.github.GTNewHorizons:FloodLights:1.5.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.2:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.140:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.25:dev") { transitive = false } - compileOnly("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") { transitive = false } - compileOnly("curse.maven:mekanism-268560:2475797") { transitive = false } - compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.40-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.8-GTNH:dev") { transitive = false } - compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.9.15:dev") { transitive = false } - - compileOnly rfg.deobf("maven.modrinth:immibis-microblocks:59.1.2") - compileOnly rfg.deobf("maven.modrinth:immibis-core:59.1.4") - - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.2-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.3") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:1.0.0-beta68a:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-754-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.78:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.3-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.0:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.8:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.0:dev") - runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.140:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.5:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.2:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.25:dev") - runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.40-GTNH:dev") - runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.0:dev") - - compileOnly("com.google.auto.value:auto-value-annotations:1.10.1") { transitive = false } + implementation("com.github.GTNewHorizons:StructureLib:1.4.39:dev") + implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.75-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.11.19:dev") + + api("org.joml:joml:1.10.8") + api("it.unimi.dsi:fastutil:8.5.18") + + devOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.162:dev") + devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.104-GTNH:dev") + + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.42:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") + + compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.4:dev") + + compileOnly("com.google.auto.value:auto-value-annotations:1.10.1") annotationProcessor("com.google.auto.value:auto-value:1.10.1") compileOnly('org.projectlombok:lombok:1.18.34') @@ -97,6 +81,51 @@ dependencies { testImplementation(platform('org.junit:junit-bom:5.9.2')) testImplementation('org.junit.jupiter:junit-jupiter') + testImplementation('org.junit.platform:junit-platform-engine') + testImplementation('org.junit.platform:junit-platform-launcher') + + def addCompat = { + enabled, spec -> { + compileOnly(spec); + + if (enabled || all) { + runtimeOnly(spec); + } + } + }; + + addCompat(ae2, "com.github.GTNewHorizons:WanionLib:1.10.1:dev") + addCompat(ae2, "com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.93-gtnh:dev") + addCompat(ae2, "com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-987-GTNH:dev") + + addCompat(arch, "com.github.GTNewHorizons:ArchitectureCraft:1.12.13") + addCompat(carpenters, "com.github.GTNewHorizons:CarpentersBlocks:3.7.3-GTNH") + + addCompat(avaritia, "com.github.GTNewHorizons:Avaritia:1.97:dev") + addCompat(avaritia, "com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") + + compileOnly("curse.maven:mekanism-268560:2475797") { transitive = false } + addCompat(eio, "curse.maven:cofh-lib-220333:2388748") + addCompat(eio, "com.github.GTNewHorizons:EnderIO:2.10.31:dev") + addCompat(eio, "com.github.GTNewHorizons:EnderStorage:1.8.3:dev") + + addCompat(xu, rfg.deobf("curse.maven:extra-utilities-225561:2264384")) + + addCompat(fl, "com.github.GTNewHorizons:FloodLights:1.5.6:dev") + + addCompat(fmp, rfg.deobf("maven.modrinth:immibis-microblocks:59.1.2")) + addCompat(fmp, rfg.deobf("maven.modrinth:immibis-core:59.1.4")) + addCompat(fmp, "com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") + + addCompat(gt5u, "com.github.GTNewHorizons:GT5-Unofficial:5.09.52.617:dev") + + addCompat(ic2, "net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") + + addCompat(oc, "com.github.GTNewHorizons:OpenComputers:1.12.47-GTNH:dev") + + addCompat(tc, "thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") + + addCompat(waila, "com.github.GTNewHorizons:waila:1.19.30:dev") } // deps may transitively add Baubles, so we replace it @@ -105,7 +134,7 @@ project.getConfigurations() final DependencySubstitutions ds = c.getResolutionStrategy() .getDependencySubstitution(); ds.substitute(ds.module("com.github.GTNewHorizons:Baubles")) - .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.4-GTNH")) + .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.21-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); diff --git a/gradle.properties b/gradle.properties index ce672041..cb72dfd3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -39,9 +39,33 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co # `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE. developmentEnvironmentUserName = Developer -# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8. -# See https://github.com/bsideup/jabel for details on how this works. -enableModernJavaSyntax = true +# Enables modern Java syntax support. Valid values: +# - false: No modern syntax, Java 8 only +# - jabel: Jabel syntax-only support, compiles to J8 bytecode +# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs) +# - modern: Native modern Java bytecode, no downgrading +enableModernJavaSyntax = jabel + +# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues, +# and should *not* be used in most situations. -1 disables this. +# forceToolchainVersion = -1 + +# Target JVM version for JVM Downgrader bytecode downgrading. +# Only used when enableModernJavaSyntax = jvmDowngrader +# downgradeTargetVersion = 8 + +# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only). +# Classes will be available in META-INF/versions/N/ for each version N in this list. +# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade). +# jvmDowngraderMultiReleaseVersions = 21,25 + +# Specifies how JVM Downgrader API stubs are provided. Options: +# - shade: Shade minimized stubs into the jar +# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint) +# - external: Another dependency provides stubs (no constraint, no warning) +# - (empty): Warning reminding you to configure stubs +# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md +# jvmDowngraderStubsProvider = # Enables injecting missing generics into the decompiled source code for a better coding experience. # Turns most publicly visible List, Map, etc. into proper List, Map types. @@ -200,3 +224,6 @@ curseForgeRelations = # This is meant to be set in $HOME/.gradle/gradle.properties. # ideaCheckSpotlessOnBuild = true +# Non-GTNH properties +org.gradle.configuration-cache = true +org.gradle.parallel = true diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..fa4ed510 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,12 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/c2dd35c9d0aaf0ba6ad0791320f99dfc/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/e5810bd7fd1f8a586644409d395a7e55/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/cf726b4a1c84b50457225f9bba6d7650/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/fa1e318c287360478e3c83a9a3ef1007/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/7b3c4877c0749019e6805bb61e421497/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d76df094a9cbbabd3b08251f9e61444a/redirect +toolchainVersion=25 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55b..d997cfc6 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da4..dbc3ce4a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a93..0262dcbd 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac2..c4bdd3ab 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/settings.gradle b/settings.gradle index becf041b..bc075499 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,5 @@ pluginManagement { } plugins { - id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.48' + id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26' } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/asm/DeMemberator.java b/src/main/java/com/recursive_pineapple/matter_manipulator/asm/DeMemberator.java index 37fff070..c9be9323 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/asm/DeMemberator.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/asm/DeMemberator.java @@ -35,6 +35,8 @@ public class DeMemberator implements IClassTransformer { @Override public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (!name.startsWith("com.recursive_pineapple.matter_manipulator")) return basicClass; + if (parser.find(basicClass)) { ClassReader reader = new ClassReader(basicClass); ClassNode node = new ClassNode(Opcodes.ASM5); diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/client/rendering/BoxRenderer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/client/rendering/BoxRenderer.java index b5b840ae..3e4214c8 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/client/rendering/BoxRenderer.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/client/rendering/BoxRenderer.java @@ -1,6 +1,8 @@ package com.recursive_pineapple.matter_manipulator.client.rendering; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.OpenGlHelper; @@ -8,7 +10,9 @@ import net.minecraft.util.AxisAlignedBB; import com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator; +import com.gtnewhorizon.gtnhlib.client.renderer.LocalTessellator; import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; +import com.gtnewhorizon.gtnhlib.client.renderer.cel.model.quad.ModelQuadViewMutable; import com.gtnewhorizon.gtnhlib.client.renderer.shader.ShaderProgram; import com.gtnewhorizon.gtnhlib.client.renderer.vbo.VertexBuffer; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat; @@ -25,7 +29,7 @@ public class BoxRenderer { private final ShaderProgram program; private final int time_location; - private final VertexBuffer buffer = new VertexBuffer(DefaultVertexFormat.POSITION_COLOR_TEXTURE, GL11.GL_QUADS); + private final VertexBuffer buffer = new VertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_COLOR, GL11.GL_QUADS); public BoxRenderer() { program = new ShaderProgram( @@ -37,15 +41,15 @@ public BoxRenderer() { time_location = program.getUniformLocation("time"); } - private CapturingTessellator tes; + private LocalTessellator tes; + private List collectedQuads; /** * Starts rendering fancy boxes. Should only be called once per frame, to allow quad sorting. */ public void start(double partialTickTime) { - TessellatorManager.startCapturing(); - - tes = (CapturingTessellator) TessellatorManager.get(); + tes = TessellatorManager.enterLocalMode(); + collectedQuads = new ArrayList<>(); tes.startDrawing(GL11.GL_QUADS); @@ -139,6 +143,8 @@ public void drawAround(AxisAlignedBB aabb, Vector3f colour) { tes.addVertexWithUV(dX, 0, dZ, dZ + dX, 0); tes.addVertexWithUV(dX, dY, dZ, dZ + dX, dY); tes.addVertexWithUV(0, dY, dZ, dZ + 0, dY); + + tes.collectQuads(collectedQuads); // spotless:on tes.restoreTranslation(); @@ -148,14 +154,12 @@ public void drawAround(AxisAlignedBB aabb, Vector3f colour) { * Actually draws the stored boxes. */ public void finish() { - final var quads = TessellatorManager.stopCapturingToPooledQuads(); + tes.collectQuads(collectedQuads); + TessellatorManager.exitLocalMode(); - QuadViewComparator quadSorter = new QuadViewComparator(); - quads.sort(quadSorter); + collectedQuads.sort(new QuadViewComparator()); - ByteBuffer bytes = CapturingTessellator.quadsToBuffer(quads, DefaultVertexFormat.POSITION_COLOR_TEXTURE); - - tes.clearQuads(); + ByteBuffer bytes = CapturingTessellator.quadsToBuffer(collectedQuads, buffer.getVertexFormat()); GL11.glEnable(GL11.GL_BLEND); OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO); @@ -165,12 +169,15 @@ public void finish() { GL20.glUniform1f(time_location, (((float) (System.currentTimeMillis() % 2500)) / 1000f)); - buffer.upload(bytes); + buffer.uploadStream(bytes); buffer.render(); ShaderProgram.clear(); GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_BLEND); + + tes = null; + collectedQuads = null; } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java index eeacd1e3..86385559 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEAnalysisResult.java @@ -11,6 +11,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import net.minecraftforge.common.util.ForgeDirection; @@ -32,13 +34,18 @@ import appeng.util.Platform; import appeng.util.SettingsFrom; +import com.glodblock.github.inventory.IDualHost; import com.google.gson.JsonElement; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; +import com.recursive_pineapple.matter_manipulator.asm.Optional; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.building.providers.IItemProvider; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; import com.recursive_pineapple.matter_manipulator.common.utils.BigItemStack; import com.recursive_pineapple.matter_manipulator.common.utils.ItemId; import com.recursive_pineapple.matter_manipulator.common.utils.MMUtils; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; public class AEAnalysisResult implements ITileAnalysisIntegration { @@ -51,6 +58,7 @@ public class AEAnalysisResult implements ITileAnalysisIntegration { public PortableItemStack[] mAEFacades = null; public InventoryAnalysis mAECells = null; public InventoryAnalysis mAEPatterns = null; + public InventoryAnalysis mAEFluidConfig = null; public static final ForgeDirection[] ALL_DIRECTIONS = ForgeDirection.values(); @@ -103,6 +111,11 @@ public AEAnalysisResult(TileEntity te) { } } + // check if the tile has AE2FC fluid interface config + if (Mods.AE2FluidCraft.isModLoaded()) { + analyzeFluidConfig(te); + } + // check all sides for parts (+UNKNOWN for cables) if (te instanceof IPartHost partHost) { mAEParts = new AEPartData[AEAnalysisResult.ALL_DIRECTIONS.length]; @@ -134,6 +147,27 @@ public AEAnalysisResult(TileEntity te) { } } + @Optional(Names.AE2_FLUID_CRAFT) + private void analyzeFluidConfig(TileEntity te) { + if (te instanceof IDualHost dualHost) { + IInventory fluidConfig = dualHost.getConfig(); + if (fluidConfig != null) { + mAEFluidConfig = InventoryAnalysis.fromInventory(fluidConfig, false); + } + } + } + + @Optional(Names.AE2_FLUID_CRAFT) + private void applyFluidConfig(IBlockApplyContext ctx, TileEntity te) { + if (te instanceof IDualHost dualHost) { + IInventory fluidConfig = dualHost.getConfig(); + if (fluidConfig != null) { + mAEFluidConfig.apply(ctx, fluidConfig, false, false); + dualHost.getDualityFluid().loadConfigFromPacket(fluidConfig); + } + } + } + @Override public boolean apply(IBlockApplyContext ctx) { TileEntity te = ctx.getTileEntity(); @@ -170,6 +204,11 @@ public boolean apply(IBlockApplyContext ctx) { } } + // apply AE2FC fluid interface config + if (Mods.AE2FluidCraft.isModLoaded() && mAEFluidConfig != null) { + applyFluidConfig(ctx, te); + } + // set ae tile custom name if (mAECustomName != null && te instanceof ICustomNameObject customName && !(te instanceof TileCableBus)) { customName.setCustomName(mAECustomName); @@ -198,7 +237,9 @@ public boolean apply(IBlockApplyContext ctx) { var result = ctx.tryConsumeItems(Arrays.asList(BigItemStack.create(expectedStack)), IPseudoInventory.CONSUME_SIMULATED); if (!result.leftBoolean()) { - ctx.warn("Could not extract item: " + expectedStack.getDisplayName()); + ctx.warn( + new ChatComponentTranslation("mm.info.warning.could_not_extract_item", new ChatComponentItemName(expectedStack)) + ); continue; } } @@ -210,7 +251,11 @@ public boolean apply(IBlockApplyContext ctx) { if (actualItem == null && expectedItem != null) { if (expectedStack != null && !partHost.canAddPart(expectedStack, dir)) { ctx.error( - "Invalid location (" + MMUtils.getDirectionDisplayName(dir, true) + ") for part (" + expectedStack.getDisplayName() + ")" + new ChatComponentTranslation( + "mm.info.error.invalid_location", + new ChatComponentTranslation(MMUtils.getDirectionUnlocalizedName(dir, true)), + new ChatComponentItemName(expectedStack) + ) ); continue; } @@ -251,7 +296,9 @@ public boolean apply(IBlockApplyContext ctx) { var result = ctx.tryConsumeItems(Arrays.asList(BigItemStack.create(expectedStack)), IPseudoInventory.CONSUME_SIMULATED); if (!result.leftBoolean()) { - ctx.warn("Could not extract item: " + expectedStack.getDisplayName()); + ctx.warn( + new ChatComponentTranslation("mm.info.warning.could_not_extract_item", new ChatComponentItemName(expectedStack)) + ); continue; } } @@ -270,7 +317,7 @@ public boolean apply(IBlockApplyContext ctx) { if (newPart == null) continue; if (!ctx.tryConsumeItems(expectedStack)) { - ctx.warn("Could not extract item: " + expectedStack.getDisplayName()); + ctx.warn(new ChatComponentTranslation("mm.info.warning.could_not_extract_item", new ChatComponentItemName(expectedStack))); continue; } @@ -328,7 +375,7 @@ private boolean installPart( if (!partHost.canAddPart(partStack, side)) { return false; } if (!context.tryConsumeItems(partStack)) { - context.warn("Could not find " + partStack.getDisplayName()); + context.warn(new ChatComponentTranslation("mm.info.warning.could_not_find_item", new ChatComponentItemName(partStack))); return false; } @@ -421,12 +468,12 @@ public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { } @Override - public void getItemTag(NBTTagCompound tag) { + public void getItemTag(ItemStack stack) { } @Override - public void getItemDetails(List details) { + public void getItemDetailsChat(List details) { } @@ -471,6 +518,7 @@ public AEAnalysisResult clone() { dup.mAEParts = mAEParts == null ? null : MMUtils.mapToArray(mAEParts, AEPartData[]::new, x -> x == null ? null : x.clone()); dup.mAECells = mAECells == null ? null : mAECells.clone(); dup.mAEPatterns = mAEPatterns == null ? null : mAEPatterns.clone(); + dup.mAEFluidConfig = mAEFluidConfig == null ? null : mAEFluidConfig.clone(); return dup; } @@ -488,6 +536,7 @@ public int hashCode() { result = prime * result + Arrays.hashCode(mAEParts); result = prime * result + ((mAECells == null) ? 0 : mAECells.hashCode()); result = prime * result + ((mAEPatterns == null) ? 0 : mAEPatterns.hashCode()); + result = prime * result + ((mAEFluidConfig == null) ? 0 : mAEFluidConfig.hashCode()); return result; } @@ -514,6 +563,9 @@ public boolean equals(Object obj) { if (mAEPatterns == null) { if (other.mAEPatterns != null) return false; } else if (!mAEPatterns.equals(other.mAEPatterns)) return false; + if (mAEFluidConfig == null) { + if (other.mAEFluidConfig != null) return false; + } else if (!mAEFluidConfig.equals(other.mAEFluidConfig)) return false; return true; } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java index caf52694..4dc87a2f 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AEPartData.java @@ -15,15 +15,13 @@ import appeng.api.parts.IPartHost; import appeng.api.parts.IPartItem; import appeng.api.parts.PartItemStack; -import appeng.api.util.IConfigurableObject; -import appeng.helpers.ICustomNameObject; -import appeng.helpers.IOreFilterable; -import appeng.helpers.IPriorityHost; import appeng.me.GridAccessException; import appeng.me.cache.P2PCache; +import appeng.parts.AEBasePart; import appeng.parts.automation.UpgradeInventory; import appeng.parts.p2p.PartP2PTunnel; import appeng.parts.p2p.PartP2PTunnelNormal; +import appeng.util.SettingsFrom; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.building.providers.IItemProvider; @@ -35,20 +33,15 @@ public class AEPartData { public PortableItemStack mPart; - public String mSettingsName = null; - public NBTTagCompound mSettings = null; - public String mCustomName = null; + + public NBTTagCompound mPartSettings = null; public PortableItemStack[] mAEUpgrades = null; - public InventoryAnalysis mConfig = null; public InventoryAnalysis mAEPatterns = null; - public String mOreDict = null; public boolean mP2POutput = false; public long mP2PFreq = 0; - public int priority = 0; - private transient Optional> mPartClass; public AEPartData() { @@ -61,22 +54,7 @@ public AEPartData() { public AEPartData(IPart part) { mPart = new PortableItemStack(part.getItemStack(PartItemStack.Wrench)); - if (part instanceof ICustomNameObject customName) { - mCustomName = customName.hasCustomName() ? customName.getCustomName() : null; - } - - if (part instanceof IOreFilterable filterable) { - mOreDict = filterable.getFilter(); - - if ("".equals(mOreDict)) mOreDict = null; - } - - if (part instanceof IConfigurableObject configurable && configurable.getConfigManager() != null) { - NBTTagCompound settings = new NBTTagCompound(); - configurable.getConfigManager() - .writeToNBT(settings); - mSettings = settings.hasNoTags() ? null : settings; - } + mPartSettings = downloadPartSettings(part); if (part instanceof PartP2PTunnel tunnel) { mP2POutput = tunnel.isOutput(); @@ -92,20 +70,18 @@ public AEPartData(IPart part) { .toArray(PortableItemStack[]::new); } - IInventory config = segmentedInventory.getInventoryByName("config"); - if (config != null) { - mConfig = InventoryAnalysis.fromInventory(config, false); - } - IInventory patterns = segmentedInventory.getInventoryByName("patterns"); if (patterns != null) { mAEPatterns = InventoryAnalysis.fromInventory(patterns, false); } } + } - if (part instanceof IPriorityHost priorityHost) { - priority = priorityHost.getPriority(); - } + private static NBTTagCompound downloadPartSettings(IPart part) { + if (!(part instanceof AEBasePart aePart)) return null; + + NBTTagCompound settings = aePart.downloadSettings(SettingsFrom.MEMORY_CARD); + return settings == null || settings.hasNoTags() ? null : settings; } public Class getPartClass() { @@ -169,41 +145,26 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD tunnel.onTunnelConfigChange(); } - if (part instanceof ICustomNameObject customName) { - if (mCustomName != null) customName.setCustomName(mCustomName); - } - - if (part instanceof IConfigurableObject configurable && configurable.getConfigManager() != null) { - NBTTagCompound settings = mSettings == null ? new NBTTagCompound() : mSettings; - configurable.getConfigManager().readFromNBT(settings); - } - if (part instanceof ISegmentedInventory segmentedInventory) { if (segmentedInventory.getInventoryByName("upgrades") instanceof UpgradeInventory upgradeInv) { if (!MMUtils.installUpgrades(context, upgradeInv, mAEUpgrades, true, false)) { success = false; } } + } - IInventory config = segmentedInventory.getInventoryByName("config"); - if (config != null) { - if (!mConfig.apply(context, config, false, false)) success = false; - } + // Some AE settings depend on installed upgrades, such as ore filters and capacity-expanded config slots. + if (part instanceof AEBasePart aePart && mPartSettings != null) { + aePart.uploadSettings(SettingsFrom.MEMORY_CARD, mPartSettings); + } + if (part instanceof ISegmentedInventory segmentedInventory) { IInventory patterns = segmentedInventory.getInventoryByName("patterns"); if (mAEPatterns != null && patterns != null) { if (!mAEPatterns.apply(context, patterns, true, false)) success = false; } } - if (part instanceof IOreFilterable filterable) { - filterable.setFilter(mOreDict == null ? "" : mOreDict); - } - - if (part instanceof IPriorityHost priorityHost) { - priorityHost.setPriority(priority); - } - return success; } @@ -278,16 +239,11 @@ public AEPartData clone() { AEPartData dup = new AEPartData(); dup.mPart = mPart == null ? null : mPart.clone(); - dup.mSettingsName = mSettingsName; - dup.mSettings = mSettings == null ? null : (NBTTagCompound) mSettings.copy(); - dup.mCustomName = mCustomName; + dup.mPartSettings = mPartSettings == null ? null : (NBTTagCompound) mPartSettings.copy(); dup.mAEUpgrades = mAEUpgrades == null ? null : MMUtils.mapToArray(mAEUpgrades, PortableItemStack[]::new, x -> x == null ? null : x.clone()); - dup.mConfig = mConfig == null ? null : mConfig.clone(); dup.mAEPatterns = mAEPatterns == null ? null : mAEPatterns.clone(); - dup.mOreDict = mOreDict; dup.mP2POutput = mP2POutput; dup.mP2PFreq = mP2PFreq; - dup.priority = priority; return dup; } @@ -297,13 +253,9 @@ public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((mPart == null) ? 0 : mPart.hashCode()); - result = prime * result + ((mSettingsName == null) ? 0 : mSettingsName.hashCode()); - result = prime * result + ((mSettings == null) ? 0 : mSettings.hashCode()); - result = prime * result + ((mCustomName == null) ? 0 : mCustomName.hashCode()); + result = prime * result + ((mPartSettings == null) ? 0 : mPartSettings.hashCode()); result = prime * result + Arrays.hashCode(mAEUpgrades); - result = prime * result + ((mConfig == null) ? 0 : mConfig.hashCode()); result = prime * result + ((mAEPatterns == null) ? 0 : mAEPatterns.hashCode()); - result = prime * result + ((mOreDict == null) ? 0 : mOreDict.hashCode()); result = prime * result + Boolean.hashCode(mP2POutput); result = prime * result + Long.hashCode(mP2PFreq); result = prime * result + ((mPartClass == null) ? 0 : mPartClass.hashCode()); @@ -319,25 +271,13 @@ public boolean equals(Object obj) { if (mPart == null) { if (other.mPart != null) return false; } else if (!mPart.equals(other.mPart)) return false; - if (mSettingsName == null) { - if (other.mSettingsName != null) return false; - } else if (!mSettingsName.equals(other.mSettingsName)) return false; - if (mSettings == null) { - if (other.mSettings != null) return false; - } else if (!mSettings.equals(other.mSettings)) return false; - if (mCustomName == null) { - if (other.mCustomName != null) return false; - } else if (!mCustomName.equals(other.mCustomName)) return false; + if (mPartSettings == null) { + if (other.mPartSettings != null) return false; + } else if (!mPartSettings.equals(other.mPartSettings)) return false; if (!Arrays.equals(mAEUpgrades, other.mAEUpgrades)) return false; - if (mConfig == null) { - if (other.mConfig != null) return false; - } else if (!mConfig.equals(other.mConfig)) return false; if (mAEPatterns == null) { if (other.mAEPatterns != null) return false; } else if (!mAEPatterns.equals(other.mAEPatterns)) return false; - if (mOreDict == null) { - if (other.mOreDict != null) return false; - } else if (!mOreDict.equals(other.mOreDict)) return false; if (mP2POutput != other.mP2POutput) return false; if (mP2PFreq != other.mP2PFreq) return false; if (mPartClass == null) { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AbstractBuildable.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AbstractBuildable.java index d6ec45a9..482aa203 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AbstractBuildable.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/AbstractBuildable.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; -import java.util.function.Function; import net.minecraft.block.Block; import net.minecraft.block.material.Material; @@ -18,11 +17,13 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.StatCollector; import net.minecraft.world.World; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.fluids.FluidRegistry; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidBlock; @@ -34,8 +35,8 @@ import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.api.interfaces.tileentity.IRedstoneEmitter; import gregtech.api.util.GTUtility; -import gregtech.common.tileentities.machines.MTEHatchOutputBusME; -import gregtech.common.tileentities.machines.MTEHatchOutputME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputBusME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputME; import gregtech.common.tileentities.storage.MTEDigitalChestBase; import appeng.api.config.Actionable; @@ -47,7 +48,6 @@ import appeng.api.parts.PartItemStack; import appeng.api.storage.data.IAEFluidStack; import appeng.api.storage.data.IAEItemStack; -import appeng.api.storage.data.IItemList; import appeng.api.util.AEColor; import appeng.helpers.ICustomNameObject; import appeng.parts.AEBasePart; @@ -179,7 +179,7 @@ protected void removeBlock(World world, int x, int y, int z, ImmutableBlockSpec if (gt) resetGTMachine(te); if (eio) resetConduitBundle(te); - if (InteropConstants.WIRELESS_CONNECTOR.matches(block, meta)) resetTileColour(te); + if (InteropConstants.isWirelessConnector(block, meta)) resetTileColour(te); if (block instanceof IFluidBlock fluidBlock && fluidBlock.canDrain(world, x, y, z)) { givePlayerFluids(fluidBlock.drain(world, x, y, z, true)); @@ -227,23 +227,15 @@ protected void emptySuperchest(TileEntity te) { } } - private static class MEOutputCaches { - - private static final Function> GET_ITEM_STACK_LIST = MMUtils - .exposeFieldGetterLambda(MTEHatchOutputBusME.class, "itemCache"); - private static final Function> GET_FLUID_STACK_LIST = MMUtils - .exposeFieldGetterLambda(MTEHatchOutputME.class, "fluidCache"); - } - @Optional({ Names.GREG_TECH_NH, Names.APPLIED_ENERGISTICS2 }) protected void emptyMEOutput(TileEntity te) { if (te instanceof IGregTechTileEntity igte) { if (igte.getMetaTileEntity() instanceof MTEHatchOutputBusME bus) { - IItemList items = MEOutputCaches.GET_ITEM_STACK_LIST.apply(bus); + var provider = bus.getProvider(); - for (IAEItemStack item : items) { + for (IAEItemStack item : provider.getCacheList()) { if (item.getStackSize() == 0) continue; givePlayerItems(Arrays.asList(BigItemStack.create(item))); @@ -251,9 +243,9 @@ protected void emptyMEOutput(TileEntity te) { } if (igte.getMetaTileEntity() instanceof MTEHatchOutputME hatch) { - IItemList fluids = MEOutputCaches.GET_FLUID_STACK_LIST.apply(hatch); + var provider = hatch.getProvider(); - for (IAEFluidStack fluid : fluids) { + for (IAEFluidStack fluid : provider.getCacheList()) { if (fluid.getStackSize() == 0) continue; givePlayerFluids(Arrays.asList(BigFluidStack.create(fluid))); @@ -443,15 +435,26 @@ protected void playSounds() { /** * Checks if a block can be edited. */ - protected boolean isEditable(World world, int x, int y, int z) { + protected boolean isEditable(World world, int x, int y, int z, boolean isPlacement) { + boolean isBlocked; + if (isPlacement) { + isBlocked = ForgeEventFactory.onPlayerBlockPlace( + player, + new BlockSnapshot(world, x, y, z, world.getBlock(x, y, z), world.getBlockMetadata(x, y, z)), + ForgeDirection.UNKNOWN + ).isCanceled(); + } else { + isBlocked = MinecraftForge.EVENT_BUS + .post(new BlockEvent.BreakEvent(x, y, z, world, world.getBlock(x, y, z), world.getBlockMetadata(x, y, z), player)); + } // if this block is protected, ignore it completely and print a warning // spotless:off - if (!world.canMineBlock(player, x, y, z) || MinecraftServer.getServer().isBlockProtected(world, x, y, z, player)) { + if (isBlocked || !world.canMineBlock(player, x, y, z) || MinecraftServer.getServer().isBlockProtected(world, x, y, z, player)) { // spotless:on if (!printedProtectedBlockWarning) { sendWarningToPlayer( player, - StatCollector.translateToLocal("mm.info.warning.protected_area") + "mm.info.warning.protected_area" ); printedProtectedBlockWarning = true; } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ArchitectureCraftAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ArchitectureCraftAnalysisResult.java index 5fa15b09..8172b47c 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ArchitectureCraftAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ArchitectureCraftAnalysisResult.java @@ -6,8 +6,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import com.google.gson.annotations.SerializedName; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; @@ -118,7 +122,7 @@ private boolean addCladding(IBlockApplyContext context, TileShape tileShape, boo if (!context.tryConsumeItems(claddingStack)) { if (!simulate) { - context.warn("Could not find cladding: " + claddingStack.getDisplayName()); + context.warn(new ChatComponentTranslation("mm.info.warning.could_not_find_cladding", new ChatComponentItemName(claddingStack))); } return false; } @@ -132,21 +136,24 @@ private boolean addCladding(IBlockApplyContext context, TileShape tileShape, boo } @Override - public void getItemTag(NBTTagCompound tag) { + public void getItemTag(ItemStack stack) { + NBTTagCompound tag = stack.getTagCompound() != null ? stack.getTagCompound() : new NBTTagCompound(); tag.setString("BaseName", material.item.toString()); tag.setInteger("BaseData", material.getMeta()); tag.setInteger("Shape", shape); + stack.setTagCompound(tag); } @Override - public void getItemDetails(List details) { + public void getItemDetailsChat(List details) { Shape shape = Shape.forId(this.shape); - if (shape != null) details.add(shape.title); + // TODO: localize shape titles + if (shape != null) details.add(new ChatComponentText(shape.title)); ItemStack stack = material == null ? null : material.toStack(); - if (stack != null) details.add(stack.getDisplayName()); + if (stack != null) details.add(new ChatComponentItemName(stack)); } @Override diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockAnalyzer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockAnalyzer.java index d8239609..0f0de038 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockAnalyzer.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockAnalyzer.java @@ -11,7 +11,7 @@ import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.StatCollector; +import net.minecraft.util.IChatComponent; import net.minecraft.world.World; import com.recursive_pineapple.matter_manipulator.GlobalMMConfig.DebugConfig; @@ -62,6 +62,12 @@ public static RegionAnalysis analyzeRegion(World world, Location a, Location b, pending.analyze(world.getTileEntity(voxel.x, voxel.y, voxel.z), PendingBlock.ANALYZE_ALL & ~PendingBlock.ANALYZE_ARCH); } + // FMP blocks (multiparts) depend on adjacent blocks for support, + // so they must be placed after regular blocks. + if (pending.mp != null) { + pending.buildOrder = Math.max(pending.buildOrder, 100); + } + pending.x -= a.x; pending.y -= a.y; pending.z -= a.z; @@ -105,9 +111,9 @@ public static interface IBlockApplyContext extends IPseudoInventory { public boolean tryApplyAction(double complexity); - public void warn(String message); + public void warn(IChatComponent message); - public void error(String message); + public void error(IChatComponent message); } /** @@ -261,30 +267,26 @@ public void givePlayerFluids(List fluids) { } @Override - public void warn(String message) { + public void warn(IChatComponent message) { sendWarningToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.warning", - x, - y, - z, - message - ) + "mm.info.warning.only_message", + x, + y, + z, + message ); } @Override - public void error(String message) { + public void error(IChatComponent message) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error", - x, - y, - z, - message - ) + "mm.info.error.only_message", + x, + y, + z, + message ); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockSpec.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockSpec.java index 147f047b..14f4b8fb 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockSpec.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/BlockSpec.java @@ -17,6 +17,9 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition.MovingObjectType; import net.minecraft.world.World; @@ -26,6 +29,7 @@ import com.google.gson.JsonElement; import com.google.gson.annotations.SerializedName; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.recursive_pineapple.matter_manipulator.MMMod; import com.recursive_pineapple.matter_manipulator.common.compat.BlockProperty; import com.recursive_pineapple.matter_manipulator.common.compat.BlockPropertyRegistry; @@ -186,6 +190,9 @@ public ItemStack toStack(int amount) { if (isBlock) { stack = getBlock().createStackedBlock(metadata); + if (stack != null && Block.getBlockFromItem(stack.getItem()) == getBlock()) { + stack.stackSize = 1; + } } if (stack == null || stack.getItem() == null) { @@ -216,10 +223,9 @@ public ItemStack toStack(int amount) { if (this.stack.isPresent()) { ItemStack stack2 = this.stack.get(); - NBTTagCompound tag = new NBTTagCompound(); - stack2.setTagCompound(tag); + stack2.setTagCompound(new NBTTagCompound()); - if (ArchitectureCraft.isModLoaded() && arch != null) arch.getItemTag(tag); + if (ArchitectureCraft.isModLoaded() && arch != null) arch.getItemTag(stack2); if (intrinsicProperties != null) { Map props = new Object2ObjectOpenHashMap<>(); @@ -243,7 +249,7 @@ public ItemStack toStack(int amount) { } } - tag = stack2.getTagCompound(); + NBTTagCompound tag = stack2.getTagCompound(); if (tag != null && tag.hasNoTags()) stack2.setTagCompound(null); } @@ -254,7 +260,8 @@ public ItemStack toStack(int amount) { return out; } - public void getItemDetails(List details) { + @Override + public void getItemDetailsChat(List details) { Map props = new Object2ObjectOpenHashMap<>(); BlockPropertyRegistry.getIntrinsicProperties(toStack(1), props); @@ -264,7 +271,7 @@ public void getItemDetails(List details) { if (prop == null) continue; - prop.getItemDetails(details, e.getValue()); + prop.getItemDetailsChat(details, e.getValue()); } } } @@ -316,19 +323,41 @@ public BlockSpec withProperties(Map properties) { } private String getItemDetails() { - List details = new ArrayList<>(0); + return getItemDetailsChat().getUnformattedTextForChat(); + } + + private IChatComponent getItemDetailsChat() { + List details = new ArrayList<>(0); - getItemDetails(details); + getItemDetailsChat(details); - if (ArchitectureCraft.isModLoaded() && arch != null) arch.getItemDetails(details); + if (ArchitectureCraft.isModLoaded() && arch != null) arch.getItemDetailsChat(details); + + if (details.isEmpty()) { return new ChatComponentText(""); } + IChatComponent out = details.get(0); + + for (int i = 1; i < details.size(); i++) { + out.appendText(", ").appendSibling(details.get(i)); + } - return details.isEmpty() ? "" : String.format(" (%s)", String.join(", ", details)); + return new ChatComponentText(" (").appendSibling(out).appendText(")"); } public String getDisplayName() { return (toStack(1) == null ? Blocks.air.getLocalizedName() : toStack(1).getDisplayName()) + getItemDetails(); } + public IChatComponent getChatComponent() { + ItemStack stack = toStack(1); + IChatComponent chatComponent; + if (stack == null) { + chatComponent = new ChatComponentTranslation(Blocks.air.getUnlocalizedName() + ".name"); + } else { + chatComponent = new ChatComponentItemName(stack); + } + return chatComponent.appendSibling(getItemDetailsChat()); + } + @Override public final boolean equals(Object o) { if (!(o instanceof BlockSpec blockSpec)) return false; @@ -376,6 +405,10 @@ public String toString() { + "]"; } + public String toDisplayString() { + return toStack(1).getDisplayName(); + } + public static BlockSpec fromPickBlock(World world, EntityPlayer player, MovingObjectPosition hit) { if (hit == null || hit.typeOfHit != MovingObjectType.BLOCK) return new BlockSpec(); diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CarpentersBlocksAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CarpentersBlocksAnalysisResult.java new file mode 100644 index 00000000..793ae06f --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CarpentersBlocksAnalysisResult.java @@ -0,0 +1,282 @@ +package com.recursive_pineapple.matter_manipulator.common.building; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.IChatComponent; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.carpentersblocks.data.Slope; +import com.carpentersblocks.data.Stairs; +import com.carpentersblocks.tileentity.TEBase; +import com.google.gson.annotations.SerializedName; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; +import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; + +public class CarpentersBlocksAnalysisResult implements ITileAnalysisIntegration { + + private static final byte TYPE_UNKNOWN = 0; + private static final byte TYPE_STAIRS = 1; + private static final byte TYPE_SLAB = 2; + private static final byte TYPE_SLOPE = 3; + + private static final int SLOPE_COUNT = 65; + + // Slab data to ForgeDirection ordinal mapping, from Slab.DIR_MAP. + private static final int[] SLAB_DIR_MAP = { + 4, 5, 0, 1, 2, 3 + }; + + @SerializedName("d") + public int data; + + @SerializedName("t") + public byte blockType; + + @SerializedName("c") + public PortableItemStack[] covers; + + public static CarpentersBlocksAnalysisResult analyze(TileEntity te) { + if (!(te instanceof TEBase cbTE)) return null; + + var result = new CarpentersBlocksAnalysisResult(); + + result.data = cbTE.getData(); + + String blockName = te.getBlockType() + .getUnlocalizedName(); + if (blockName.contains("Stairs")) { + result.blockType = TYPE_STAIRS; + } else if (blockName.contains("Slope")) { + result.blockType = TYPE_SLOPE; + } else if (blockName.contains("blockCarpentersBlock")) { + result.blockType = TYPE_SLAB; + } + + PortableItemStack[] covers = new PortableItemStack[7]; + boolean hasCovers = false; + for (int i = 0; i < 7; i++) { + ItemStack cover = cbTE.getAttribute(TEBase.ATTR_COVER[i]); + if (cover != null) { + covers[i] = new PortableItemStack(cover); + hasCovers = true; + } + } + if (hasCovers) { + result.covers = covers; + } + + return result; + } + + @Override + public boolean apply(IBlockApplyContext ctx) { + TileEntity te = ctx.getTileEntity(); + + if (!(te instanceof TEBase cbTE)) return false; + + cbTE.setData(data); + + if (covers != null) { + for (int i = 0; i < covers.length && i < 7; i++) { + if (covers[i] == null) continue; + + ItemStack coverStack = covers[i].toStack(); + if (coverStack == null) continue; + + if (cbTE.hasAttribute(TEBase.ATTR_COVER[i])) { + ItemStack existing = cbTE.getAttribute(TEBase.ATTR_COVER[i]); + if (existing != null) { + ctx.givePlayerItems(existing.copy()); + } + cbTE.onAttrDropped(TEBase.ATTR_COVER[i]); + } + + if (ctx.tryConsumeItems(coverStack)) { + cbTE.addAttribute(TEBase.ATTR_COVER[i], coverStack); + } + } + } + + return true; + } + + @Override + public boolean getRequiredItemsForExistingBlock(IBlockApplyContext context) { + TileEntity te = context.getTileEntity(); + + if (!(te instanceof TEBase cbTE)) return false; + + if (covers != null) { + for (int i = 0; i < covers.length && i < 7; i++) { + if (covers[i] == null) continue; + + ItemStack existing = cbTE.getAttribute(TEBase.ATTR_COVER[i]); + if (existing != null) { + PortableItemStack existingPortable = new PortableItemStack(existing); + if ( + existingPortable.item != null && existingPortable.item.equals(covers[i].item) && + java.util.Objects.equals(existingPortable.metadata, covers[i].metadata) + ) { + continue; + } + context.givePlayerItems(existing.copy()); + } + ItemStack needed = covers[i].toStack(); + if (needed != null) context.tryConsumeItems(needed); + } + } + + return true; + } + + @Override + public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { + if (covers != null) { + for (int i = 0; i < covers.length && i < 7; i++) { + if (covers[i] != null) { + ItemStack needed = covers[i].toStack(); + if (needed != null) context.tryConsumeItems(needed); + } + } + } + return true; + } + + @Override + public void getItemTag(ItemStack stack) {} + + @Override + public void getItemDetailsChat(List details) { + if (covers != null && covers[6] != null) { + ItemStack stack = covers[6].toStack(); + if (stack != null) details.add(new ChatComponentItemName(stack)); + } + } + + @Override + public void transform(Transform transform) { + switch (blockType) { + case TYPE_STAIRS -> transformStairs(transform); + case TYPE_SLOPE -> transformSlope(transform); + case TYPE_SLAB -> transformSlab(transform); + default -> {} + } + } + + private List transformFacings(List facings, Transform transform) { + List result = new ArrayList<>(facings.size()); + for (ForgeDirection facing : facings) { + result.add(transform.apply(facing)); + } + return result; + } + + private boolean facingsMatch(List a, List b) { + return a.size() == b.size() && a.containsAll(b); + } + + private static boolean stairsTypesCompatible(Stairs.Type a, Stairs.Type b) { + if (a == b) return true; + return (a == Stairs.Type.NORMAL_SIDE || a == Stairs.Type.NORMAL) && (b == Stairs.Type.NORMAL_SIDE || b == Stairs.Type.NORMAL); + } + + private static boolean slopeTypesCompatible(Slope.Type a, Slope.Type b) { + if (a == b) return true; + return (a == Slope.Type.WEDGE_SIDE || a == Slope.Type.WEDGE) && (b == Slope.Type.WEDGE_SIDE || b == Slope.Type.WEDGE); + } + + private void transformStairs(Transform transform) { + if (data < 0 || data >= Stairs.stairsList.length) return; + + Stairs stairs = Stairs.stairsList[data]; + if (stairs == null) return; + + List newFacings = transformFacings(stairs.facings, transform); + + for (Stairs candidate : Stairs.stairsList) { + if (candidate != null && stairsTypesCompatible(candidate.stairsType, stairs.stairsType) && facingsMatch(candidate.facings, newFacings)) { + data = candidate.stairsID; + return; + } + } + } + + private void transformSlope(Transform transform) { + Slope slope = Slope.getSlopeById(data); + if (slope == null) return; + + List newFacings = transformFacings(slope.facings, transform); + + for (int i = 0; i < SLOPE_COUNT; i++) { + Slope candidate = Slope.getSlopeById(i); + if (candidate != null && slopeTypesCompatible(candidate.type, slope.type) && facingsMatch(candidate.facings, newFacings)) { + data = candidate.slopeID; + return; + } + } + } + + private void transformSlab(Transform transform) { + if (data < 1 || data > 6) return; + + ForgeDirection dir = ForgeDirection.getOrientation(SLAB_DIR_MAP[data - 1]); + ForgeDirection newDir = transform.apply(dir); + + for (int i = 0; i < SLAB_DIR_MAP.length; i++) { + if (SLAB_DIR_MAP[i] == newDir.ordinal()) { + data = i + 1; + return; + } + } + } + + @Override + public void migrate() {} + + @Override + public CarpentersBlocksAnalysisResult clone() { + var dup = new CarpentersBlocksAnalysisResult(); + + dup.data = data; + dup.blockType = blockType; + if (covers != null) { + dup.covers = new PortableItemStack[covers.length]; + for (int i = 0; i < covers.length; i++) { + dup.covers[i] = covers[i] == null ? null : covers[i].clone(); + } + } + + return dup; + } + + @Override + public int hashCode() { + int result = data; + result = 31 * result + blockType; + result = 31 * result + Arrays.hashCode(covers); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof CarpentersBlocksAnalysisResult other)) return false; + return data == other.data && blockType == other.blockType && Arrays.equals(covers, other.covers); + } + + @Override + public String toString() { + return "CarpentersBlocksAnalysisResult [data=" + data + + ", blockType=" + + blockType + + ", covers=" + + Arrays.toString(covers) + + "]"; + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CopyableProperty.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CopyableProperty.java index 1018b265..9985910b 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CopyableProperty.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CopyableProperty.java @@ -25,4 +25,22 @@ public enum CopyableProperty { public String toString() { return name().toLowerCase(); } + + public String getUnlocalizedName() { + return switch (this) { + case FACING -> "mm.enum.copyable.property.facing"; + case FORWARD -> "mm.enum.copyable.property.forward"; + case UP -> "mm.enum.copyable.property.up"; + case LEFT -> "mm.enum.copyable.property.left"; + case TOP -> "mm.enum.copyable.property.top"; + case ROTATION -> "mm.enum.copyable.property.rotation"; + case MODE -> "mm.enum.copyable.property.mode"; + case TEXT -> "mm.enum.copyable.property.text"; + case ORIENTATION -> "mm.enum.copyable.property.orientation"; + case DELAY -> "mm.enum.copyable.property.delay"; + case INVERTED -> "mm.enum.copyable.property.inverted"; + case COLOR -> "mm.enum.copyable.property.color"; + case ROTATION_STATE -> "mm.enum.copyable.property.rotation_state"; + }; + } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/FMPPartTransforms.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/FMPPartTransforms.java new file mode 100644 index 00000000..32220f4d --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/FMPPartTransforms.java @@ -0,0 +1,221 @@ +package com.recursive_pineapple.matter_manipulator.common.building; + +import java.util.Arrays; + +import net.minecraft.nbt.NBTTagCompound; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; + +public class FMPPartTransforms { + + private FMPPartTransforms() {} + + public static void transformSide(NBTTagCompound nbt, Transform transform) { + if (!nbt.hasKey("side")) return; + + int side = nbt.getByte("side") & 0xFF; + if (side >= 6) return; + + int newSide = rotateFace(side, transform); + nbt.setByte("side", (byte) newSide); + } + + public static void transformMicroblockShape(String typeId, NBTTagCompound nbt, Transform transform) { + if (typeId == null || !nbt.hasKey("shape")) return; + + int shape = nbt.getByte("shape") & 0xFF; + int slot = shape & 0x0F; + int sizeBits = shape & 0xF0; + + int newSlot = switch (typeId) { + case "mcr_face", "mcr_hllw" -> transformFaceSlot(slot, transform); + case "mcr_edge" -> transformEdgeSlot(slot, transform); + case "mcr_cnr" -> transformCornerSlot(slot, transform); + default -> -1; + }; + + if (newSlot >= 0) { + nbt.setByte("shape", (byte) (sizeBits | newSlot)); + } + } + + private static int rotateFace(int faceOrdinal, Transform transform) { + return transform.apply(ForgeDirection.getOrientation(faceOrdinal)).ordinal(); + } + + private static int transformFaceSlot(int slot, Transform transform) { + return slot < 6 ? rotateFace(slot, transform) : slot; + } + + // Edge slot -> two defining faces. From codechicken.multipart.PartMap. + // @formatter:off + private static final int[][] EDGE_FACES = { + { + 2, 4 + }, // 0: north-west vertical edge + { + 3, 4 + }, // 1: south-west vertical edge + { + 2, 5 + }, // 2: north-east vertical edge + { + 3, 5 + }, // 3: south-east vertical edge + { + 0, 4 + }, // 4: bottom-west horizontal edge (north-south) + { + 0, 5 + }, // 5: bottom-east horizontal edge (north-south) + { + 1, 4 + }, // 6: top-west horizontal edge (north-south) + { + 1, 5 + }, // 7: top-east horizontal edge (north-south) + { + 0, 2 + }, // 8: bottom-north horizontal edge (east-west) + { + 1, 2 + }, // 9: top-north horizontal edge (east-west) + { + 0, 3 + }, // 10: bottom-south horizontal edge (east-west) + { + 1, 3 + }, // 11: top-south horizontal edge (east-west) + }; + // @formatter:on + + private static int transformEdgeSlot(int edgeSlot, Transform transform) { + if (edgeSlot < 0 || edgeSlot >= EDGE_FACES.length) return edgeSlot; + + int f1 = rotateFace(EDGE_FACES[edgeSlot][0], transform); + int f2 = rotateFace(EDGE_FACES[edgeSlot][1], transform); + + return findEdge(Math.min(f1, f2), Math.max(f1, f2)); + } + + private static int findEdge(int face1, int face2) { + for (int i = 0; i < EDGE_FACES.length; i++) { + if (EDGE_FACES[i][0] == face1 && EDGE_FACES[i][1] == face2) return i; + } + return -1; + } + + // Corner slot -> three defining faces. From codechicken.multipart.PartMap. + // @formatter:off + private static final int[][] CORNER_FACES = { + { + 0, 2, 4 + }, // 0: bottom-north-west corner + { + 1, 2, 4 + }, // 1: top-north-west corner + { + 0, 3, 4 + }, // 2: bottom-south-west corner + { + 1, 3, 4 + }, // 3: top-south-west corner + { + 0, 2, 5 + }, // 4: bottom-north-east corner + { + 1, 2, 5 + }, // 5: top-north-east corner + { + 0, 3, 5 + }, // 6: bottom-south-east corner + { + 1, 3, 5 + }, // 7: top-south-east corner + }; + // @formatter:on + + private static int transformCornerSlot(int cornerSlot, Transform transform) { + if (cornerSlot < 0 || cornerSlot >= CORNER_FACES.length) return cornerSlot; + + int[] rotated = { + rotateFace(CORNER_FACES[cornerSlot][0], transform), + rotateFace(CORNER_FACES[cornerSlot][1], transform), + rotateFace(CORNER_FACES[cornerSlot][2], transform), + }; + Arrays.sort(rotated); + + return findCorner(rotated); + } + + private static int findCorner(int[] sortedFaces) { + for (int i = 0; i < CORNER_FACES.length; i++) { + if (CORNER_FACES[i][0] == sortedFaces[0] && CORNER_FACES[i][1] == sortedFaces[1] && CORNER_FACES[i][2] == sortedFaces[2]) return i; + } + return -1; + } + + // CodeChickenLib Rotation.sideRotMap: maps (side << 2 | rotation) to absolute direction. + // @formatter:off + private static final int[] SIDE_ROT_MAP = { + 3, + 4, + 2, + 5, // DOWN: r0=S, r1=W, r2=N, r3=E + 3, + 5, + 2, + 4, // UP: r0=S, r1=E, r2=N, r3=W + 1, + 5, + 0, + 4, // NORTH: r0=U, r1=E, r2=D, r3=W + 1, + 4, + 0, + 5, // SOUTH: r0=U, r1=W, r2=D, r3=E + 1, + 2, + 0, + 3, // WEST: r0=U, r1=N, r2=D, r3=S + 1, + 3, + 0, + 2, // EAST: r0=U, r1=S, r2=D, r3=N + }; + // @formatter:on + + /** Transforms the "orient" field: (side << 2) | rotation. Used by ProjectRed gates. */ + public static void transformOrient(NBTTagCompound nbt, Transform transform) { + if (!nbt.hasKey("orient")) return; + + int orient = nbt.getByte("orient") & 0xFF; + int oldSide = orient >> 2; + int oldRot = orient & 0x3; + + if (oldSide >= 6) return; + + int newSide = rotateFace(oldSide, transform); + + int frontDir = SIDE_ROT_MAP[oldSide << 2 | oldRot]; + int newFrontDir = rotateFace(frontDir, transform); + + int newRot = 0; + for (int r = 0; r < 4; r++) { + if (SIDE_ROT_MAP[newSide << 2 | r] == newFrontDir) { + newRot = r; + break; + } + } + + nbt.setByte("orient", (byte) ((newSide << 2) | newRot)); + } + + public static void clearConnMap(NBTTagCompound nbt) { + if (nbt.hasKey("connMap")) { + nbt.setInteger("connMap", 0); + } + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java index 06acf7fe..6cd63939 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java @@ -2,7 +2,6 @@ import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.nullIfUnknown; -import java.lang.invoke.MethodHandle; import java.util.List; import java.util.Objects; @@ -10,9 +9,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidRegistry; import gregtech.api.GregTechAPI; import gregtech.api.covers.CoverRegistry; @@ -22,7 +24,7 @@ import gregtech.api.interfaces.IDataCopyable; import gregtech.api.interfaces.IMEConnectable; import gregtech.api.interfaces.metatileentity.IConnectable; -import gregtech.api.interfaces.metatileentity.IFluidLockable; +import gregtech.api.interfaces.metatileentity.IFluidLockableMui2; import gregtech.api.interfaces.metatileentity.IItemLockable; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; @@ -37,6 +39,7 @@ import appeng.helpers.ICustomNameObject; import com.google.gson.JsonElement; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.gtnewhorizon.structurelib.alignment.IAlignment; import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider; import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; @@ -49,7 +52,6 @@ import gtnhlanth.common.beamline.MTEBeamlinePipe; import lombok.EqualsAndHashCode; -import lombok.SneakyThrows; import tectech.thing.metaTileEntity.hatch.MTEHatchDynamoTunnel; import tectech.thing.metaTileEntity.hatch.MTEHatchEnergyTunnel; import tectech.thing.metaTileEntity.multi.base.TTMultiblockBase; @@ -208,14 +210,14 @@ public GTAnalysisResult(IGregTechTileEntity igte) { } // check if the machine has a locked fluid - if (mte instanceof IFluidLockable lockable && lockable.isFluidLocked()) { - mGTFluidLock = lockable.getLockedFluidName(); + if (mte instanceof IFluidLockableMui2 lockable && lockable.isFluidLocked()) { + mGTFluidLock = lockable.getLockedFluid().getName(); } // check if the machine is a multi and store its settings if (mte instanceof MTEMultiBlockBase multi) { if (multi instanceof MTEIntegratedOreFactory iof) { - mGTMode = getIOFMode(iof); + mGTMode = iof.getMachineMode(); } else { mGTMode = multi.machineMode; } @@ -254,11 +256,11 @@ public GTAnalysisResult(IGregTechTileEntity igte) { } if (mte instanceof MTEHatchEnergyTunnel hatch) { - mAmperes = hatch.Amperes; + mAmperes = hatch.getAmperes(); } if (mte instanceof MTEHatchDynamoTunnel dynamo) { - mAmperes = dynamo.Amperes; + mAmperes = dynamo.getAmperes(); } if (mte instanceof IMEConnectable me && me.connectsToAllSides()) { @@ -266,22 +268,6 @@ public GTAnalysisResult(IGregTechTileEntity igte) { } } - private static final MethodHandle GET_IOF_MODE = MMUtils - .exposeFieldGetter(MTEIntegratedOreFactory.class, "sMode"); - - @SneakyThrows - private static int getIOFMode(MTEIntegratedOreFactory cal) { - return (int) GET_IOF_MODE.invokeExact(cal); - } - - private static final MethodHandle SET_IOF_MODE = MMUtils - .exposeFieldSetter(MTEIntegratedOreFactory.class, "sMode"); - - @SneakyThrows - private static void setIOFMode(MTEIntegratedOreFactory cal, int mode) { - SET_IOF_MODE.invokeExact(cal, mode); - } - @Override public boolean apply(IBlockApplyContext ctx) { World world = ctx.getWorld(); @@ -313,7 +299,8 @@ public boolean apply(IBlockApplyContext ctx) { basicMachine.setMainFacing(mGTMainFacing); // Stop MTEBasicMachine.doDisplayThings from overwriting the setFrontFacing call when the block is // newly placed - basicMachine.mHasBeenUpdated = true; + // This variable seems to have vanished, leaving it here as a comment in case something breaks + // basicMachine.mHasBeenUpdated = true; } basicMachine.mItemTransfer = (mGTFlags & GT_BASIC_IO_PUSH_ITEMS) != 0; @@ -358,7 +345,12 @@ public boolean apply(IBlockApplyContext ctx) { gte.setFrontFacing(facing.getDirection()); alignment.toolSetExtendedFacing(facing); } else { - ctx.error("Could not set direction to '" + facing.getLocalizedName() + "'"); + ctx.error( + new ChatComponentTranslation( + "mm.info.error.could_not_set_direction_to", + new ChatComponentTranslation(MMUtils.getFacingUnlocalizedName(facing)) + ) + ); } } } else { @@ -396,14 +388,18 @@ public boolean apply(IBlockApplyContext ctx) { // set the ghost circuit if (mte instanceof IConfigurationCircuitSupport ghostCircuit && ghostCircuit.allowSelectCircuit()) { - ItemStack circuit = null; + int circuitSlot = ghostCircuit.getCircuitSlot(); - if (mGTGhostCircuit > 0) { - circuit = ItemList.Circuit_Integrated.getWithDamage(0, mGTGhostCircuit); - } + if (circuitSlot >= 0 && circuitSlot < mte.getSizeInventory()) { + ItemStack circuit = null; + + if (mGTGhostCircuit > 0) { + circuit = ItemList.Circuit_Integrated.getWithDamage(0, mGTGhostCircuit); + } - mte.setInventorySlotContents(ghostCircuit.getCircuitSlot(), circuit); - mte.markDirty(); + mte.setInventorySlotContents(circuitSlot, circuit); + mte.markDirty(); + } } // set the various input bus options @@ -422,18 +418,19 @@ public boolean apply(IBlockApplyContext ctx) { // set the output hatch mode if (mte instanceof MTEHatchOutput outputHatch) { - outputHatch.mMode = (byte) mGTMode; + outputHatch.setMode((byte) mGTMode); } // set the locked fluid - if (mte instanceof IFluidLockable lockable && lockable.isFluidLocked()) { - lockable.setLockedFluidName(mGTFluidLock); + if (mte instanceof IFluidLockableMui2 lockable && mGTFluidLock != null) { + lockable.lockFluid(true); + lockable.setLockedFluid(FluidRegistry.getFluid(mGTFluidLock)); } // set the various multi options if (mte instanceof MTEMultiBlockBase multi) { if (mte instanceof MTEIntegratedOreFactory iof) { - setIOFMode(iof, mGTMode); + iof.setMachineMode(mGTMode); } else { multi.machineMode = mGTMode; } @@ -496,11 +493,11 @@ public boolean apply(IBlockApplyContext ctx) { } if (mAmperes > 0 && mte instanceof MTEHatchEnergyTunnel hatch) { - hatch.Amperes = MMUtils.clamp(mAmperes, 0, hatch.maxAmperes); + hatch.setAmperes(MMUtils.clamp(mAmperes, 0, hatch.maxAmperes)); } if (mAmperes > 0 && mte instanceof MTEHatchDynamoTunnel dynamo) { - dynamo.Amperes = MMUtils.clamp(mAmperes, 0, dynamo.maxAmperes); + dynamo.setAmperes(MMUtils.clamp(mAmperes, 0, dynamo.maxAmperes)); } if (mte instanceof IMEConnectable me) { @@ -529,12 +526,23 @@ private void installCover(IBlockApplyContext context, IGregTechTileEntity gte, F ItemStack stack = cover.getCoverStack(); if (!canPlace(gte, side, cover)) { - context.error("Was not allowed to put cover on " + side.name().toLowerCase() + "side: " + stack.getDisplayName()); + context.error( + new ChatComponentTranslation( + "mm.info.error.was_not_allowed_to_put_cover_on", + new ChatComponentTranslation(MMUtils.getDirectionUnlocalizedName(side, true)), + new ChatComponentItemName(stack) + ) + ); return; } if (!context.tryConsumeItems(stack)) { - context.error("Could not find cover: " + stack.getDisplayName()); + context.error( + new ChatComponentTranslation( + "mm.info.error.could_not_find_cover", + new ChatComponentItemName(stack) + ) + ); return; } @@ -623,12 +631,12 @@ private boolean canPlace(IGregTechTileEntity gte, ForgeDirection side, CoverData } @Override - public void getItemTag(NBTTagCompound tag) { + public void getItemTag(ItemStack stack) { } @Override - public void getItemDetails(List details) { + public void getItemDetailsChat(List details) { } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ITileAnalysisIntegration.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ITileAnalysisIntegration.java index e9834fab..19ff3e9f 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ITileAnalysisIntegration.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ITileAnalysisIntegration.java @@ -2,7 +2,9 @@ import java.util.List; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.block.Block; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IChatComponent; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; @@ -15,13 +17,21 @@ public interface ITileAnalysisIntegration { boolean getRequiredItemsForNewBlock(IBlockApplyContext context); - void getItemTag(NBTTagCompound tag); + void getItemTag(ItemStack stack); - void getItemDetails(List details); + void getItemDetailsChat(List details); void transform(Transform transform); ITileAnalysisIntegration clone(); void migrate(); + + default Block getPreviewBlock() { + return null; + } + + default int getPreviewMeta() { + return 0; + } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ImmutableBlockSpec.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ImmutableBlockSpec.java index 93ede8eb..04488d6f 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ImmutableBlockSpec.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ImmutableBlockSpec.java @@ -8,6 +8,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.IChatComponent; import net.minecraft.world.World; import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier; @@ -58,7 +59,7 @@ default boolean isFree() { return InteropConstants.isFree(getBlock(), getBlockMeta()); } - void getItemDetails(List details); + void getItemDetailsChat(List details); static Comparator getComparator() { return Comparator.comparing(s -> s.toStack(1), (a, b) -> { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InteropConstants.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InteropConstants.java index 3596aaca..e32ed0f1 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InteropConstants.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InteropConstants.java @@ -16,10 +16,6 @@ import com.recursive_pineapple.matter_manipulator.common.utils.Mods; import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; -import bartworks.common.loaders.FluidLoader; -import goodgenerator.loader.Loaders; -import tectech.thing.casing.TTCasingsContainer; - /** * Various constants or static methods used for interop. */ @@ -29,11 +25,17 @@ private InteropConstants() {} public static final LazyBlock BRIGHT_AIR = new LazyBlock(Mods.GalacticraftCore, "tile.brightAir", OreDictionary.WILDCARD_VALUE); public static final LazyBlock ARCANE_LAMP_LIGHT = new LazyBlock(Mods.Thaumcraft, "blockAiry", 2); - public static final LazyBlock WIRELESS_CONNECTOR = new LazyBlock(Mods.AE2Stuff, "Wireless"); + public static final LazyBlock WIRELESS_CONNECTOR = new LazyBlock(Mods.AppliedEnergistics2, "tile.BlockWirelessConnector"); + public static final LazyBlock WIRELESS_HUB = new LazyBlock(Mods.AppliedEnergistics2, "tile.BlockWirelessHub"); public static final LazyBlockSpec AE_BLOCK_CABLE = LazyBlockSpec.ofBlock(Mods.AppliedEnergistics2, "tile.BlockCableBus", 0); public static final LazyBlock FMP_BLOCK = new LazyBlock(Mods.ForgeMultipart, "block"); + public static final LazyBlock OC_CABLE = new LazyBlock(Mods.OpenComputers, "cable"); public static final LazyBlockSpec ENDER_STORAGE = LazyBlockSpec.ofBlock(Mods.EnderStorage, "enderChest", MMValues.W); + public static boolean isWirelessConnector(Block block, int meta) { + return WIRELESS_CONNECTOR.matches(block, meta) || WIRELESS_HUB.matches(block, meta); + } + public static boolean isAir(Block block, int meta) { if (block.getMaterial() instanceof MaterialTransparent) return true; @@ -44,7 +46,6 @@ public static boolean skipWhenCopying(Block block, int meta) { if (block.getMaterial() instanceof MaterialLiquid) return true; if (Mods.GregTech.isModLoaded() && isGTRenderer(block)) return true; - if (FMP_BLOCK.matches(block, meta)) return true; if (BRIGHT_AIR.matches(block, meta)) return true; if (ARCANE_LAMP_LIGHT.matches(block, meta)) return true; @@ -63,15 +64,7 @@ public static boolean shouldDropItem(Block block, int meta) { @Optional(Names.GREG_TECH_NH) private static boolean isGTRenderer(Block block) { - if (block == GregTechAPI.sDroneRender) return true; - if (block == GregTechAPI.sWormholeRender) return true; - if (block == GregTechAPI.sBlackholeRender) return true; - if (block == TTCasingsContainer.eyeOfHarmonyRenderBlock) return true; - if (block == TTCasingsContainer.forgeOfGodsRenderBlock) return true; - if (block == FluidLoader.bioFluidBlock) return true; - if (block == Loaders.antimatterRenderBlock) return true; - - return false; + return GregTechAPI.isGTRenderer(block); } public static boolean isFree(Block block, int metadata) { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InventoryAnalysis.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InventoryAnalysis.java index 304caaa9..f4961e15 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InventoryAnalysis.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InventoryAnalysis.java @@ -4,10 +4,13 @@ import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; +import net.minecraft.util.ChatComponentTranslation; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.building.providers.AECellItemProvider; import com.recursive_pineapple.matter_manipulator.common.building.providers.BatteryItemProvider; +import com.recursive_pineapple.matter_manipulator.common.building.providers.ComputerComponentItemProvider; import com.recursive_pineapple.matter_manipulator.common.building.providers.IItemProvider; import com.recursive_pineapple.matter_manipulator.common.building.providers.PatternItemProvider; import com.recursive_pineapple.matter_manipulator.common.utils.InventoryAdapter; @@ -32,10 +35,13 @@ public InventoryAnalysis() {} public static InventoryAnalysis fromInventory(IInventory inv, boolean fuzzy) { InventoryAdapter adapter = InventoryAdapter.findAdapter(inv); + int size = adapter.getSizeInventory(inv); + if (size == 0) return null; + InventoryAnalysis analysis = new InventoryAnalysis(); analysis.mFuzzy = fuzzy; - analysis.mItems = new IItemProvider[adapter.getSizeInventory(inv)]; + analysis.mItems = new IItemProvider[size]; for (int slot = 0; slot < analysis.mItems.length; slot++) { if (!adapter.isValidSlot(inv, slot)) continue; @@ -49,6 +55,11 @@ public static InventoryAnalysis fromInventory(IInventory inv, boolean fuzzy) { private static IItemProvider getProviderFor(ItemStack stack, boolean fuzzy) { if (stack == null || stack.getItem() == null) return null; + if (Mods.OpenComputers.isModLoaded()) { + IItemProvider component = ComputerComponentItemProvider.fromStack(stack); + if (component != null) return component; + } + if (Mods.AppliedEnergistics2.isModLoaded()) { if (!fuzzy) { IItemProvider cell = AECellItemProvider.fromWorkbenchItem(stack); @@ -80,7 +91,9 @@ private boolean apply(IBlockApplyContext context, IInventory inv, InventoryAdapt if (!adapter.validate(context, inv)) return false; if (adapter.getSizeInventory(inv) != mItems.length) { - context.warn("Inventory was the wrong size (expected " + mItems.length + ", was " + adapter.getSizeInventory(inv) + ")"); + if (adapter.getSizeInventory(inv) != 0) { + context.warn(new ChatComponentTranslation("mm.info.warning.inventory_was_the_wrong_size", mItems.length, adapter.getSizeInventory(inv))); + } return false; } @@ -97,7 +110,9 @@ private boolean apply(IBlockApplyContext context, IInventory inv, InventoryAdapt ItemStack stack = inv.getStackInSlot(slot); if (stack != null) { if (!adapter.canExtract(inv, slot)) { - context.warn("Could not extract item in slot " + slot + ": " + MMUtils.stripFormat(stack.getDisplayName())); + context.warn( + new ChatComponentTranslation("mm.info.warning.could_not_extract_item_in_slot", slot, new ChatComponentItemName(stack)) + ); continue; } @@ -111,14 +126,25 @@ private boolean apply(IBlockApplyContext context, IInventory inv, InventoryAdapt if (target != null) { if (!adapter.canInsert(inv, slot, target.getStack(null, false))) { - context.warn("Invalid item for slot " + slot + ": " + MMUtils.stripFormat(target.describe())); + context.warn( + new ChatComponentTranslation( + "mm.info.warning.invalid_item_for_slot", + slot, + new ChatComponentItemName(target.getStack(null, false)) + ) + ); continue; } ItemStack toInsert = target.getStack(context, consume); if (toInsert == null) { - context.warn("Could not gather item for inventory: " + MMUtils.stripFormat(target.describe())); + context.warn( + new ChatComponentTranslation( + "mm.info.warning.could_not_gather_item_for_inventory", + new ChatComponentItemName(target.getStack(null, false)) + ) + ); success = false; } else { if (!simulate) { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java index 1d53fcae..2853bd3a 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MMInventory.java @@ -1,5 +1,6 @@ package com.recursive_pineapple.matter_manipulator.common.building; +import static com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil.formatFluid; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.sendErrorToPlayer; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.sendWarningToPlayer; import static com.recursive_pineapple.matter_manipulator.common.utils.Mods.AppliedEnergistics2; @@ -10,13 +11,12 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; -import net.minecraft.util.StatCollector; +import net.minecraft.util.ChatComponentTranslation; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.IFluidContainerItem; @@ -29,6 +29,7 @@ import appeng.api.storage.data.IAEItemStack; import com.google.common.collect.ImmutableList; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentFluidName; import com.gtnewhorizon.gtnhlib.util.map.ItemStackMap; import com.recursive_pineapple.matter_manipulator.asm.Optional; import com.recursive_pineapple.matter_manipulator.common.entities.EntityItemLarge; @@ -236,8 +237,13 @@ protected void actuallyGivePlayerStuff() { if (stack.getStackSize() == 0) continue; if (stack.amount > 0 && !player.capabilities.isCreativeMode) { - sendWarningToPlayer(player, StatCollector.translateToLocal("mm.info.warning.not_find_container_for_fluid")); - sendWarningToPlayer(player, String.format(" %sL of %s", MMUtils.formatNumbers(stack.amount), stack.getFluidStack().getLocalizedName())); + sendWarningToPlayer(player, "mm.info.warning.not_find_container_for_fluid"); + sendWarningToPlayer( + player, + "mm.info.warning.of_fluid", + formatFluid(stack.amount), + new ChatComponentFluidName(stack.getFluidStack()) + ); } } @@ -251,10 +257,8 @@ private void injectItemsIntoUplink(BigItemStack stack) { printedUplinkWarning = true; sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_push_items_to_uplink", - status.toString() - ) + "mm.info.error.could_not_push_items_to_uplink", + new ChatComponentTranslation(status.toUnlocalizedString()) ); } } @@ -262,7 +266,7 @@ private void injectItemsIntoUplink(BigItemStack stack) { @Optional(Names.APPLIED_ENERGISTICS2) private void injectItemsIntoAE(BigItemStack stack) { IAEItemStack result = state.storageGrid.getItemInventory() - .injectItems(stack.getAEItemStack(), Actionable.MODULATE, new PlayerSource(player, state.securityTerminal)); + .injectItems(stack.getAEItemStack(), Actionable.MODULATE, new PlayerSource(player, state.actionHost)); stack.setStackSize(result != null ? result.getStackSize() : 0); } @@ -322,7 +326,7 @@ private void injectItemsIntoWorld(BigItemStack stack) { @Optional(Names.APPLIED_ENERGISTICS2) private void injectFluidsIntoAE(BigFluidStack stack) { IAEFluidStack result = state.storageGrid.getFluidInventory() - .injectItems(stack.getAEFluidStack(), Actionable.MODULATE, new PlayerSource(player, state.securityTerminal)); + .injectItems(stack.getAEFluidStack(), Actionable.MODULATE, new PlayerSource(player, state.actionHost)); stack.setStackSize(result != null ? result.getStackSize() : 0); } @@ -334,10 +338,8 @@ private void injectFluidsIntoUplink(BigFluidStack stack) { printedUplinkWarning = true; sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_push_fluids_to_uplink", - status.toString() - ) + "mm.info.error.could_not_push_fluids_to_uplink", + new ChatComponentTranslation(status.toUnlocalizedString()) ); } } @@ -368,25 +370,33 @@ private void injectFluidsIntoIdealCell(BigFluidStack stack) { } private void injectFluidsIntoCells(BigFluidStack stack) { - final FluidStack fluid = stack.getFluidStack(); - - // spotless:off - List validCells = MMUtils.streamInventory(player.inventory) - .filter(x -> ( - x != null && - x.getItem() instanceof IFluidContainerItem container && - x.stackSize == 1 && - container.fill(x, fluid, false) > 0 - )) - .collect(Collectors.toList()); - // spotless:on - - for (ItemStack cell : validCells) { - FluidStack fluid2 = stack.getFluidStack(); - stack.amount -= ((IFluidContainerItem) cell.getItem()).fill(cell, fluid2, true); + ItemStack[] inv = player.inventory.mainInventory; - if (stack.amount <= 0) return; + for (int i = 0; i < inv.length && stack.amount > 0; i++) { + ItemStack cell = inv[i]; + if (cell == null) continue; + if (!(cell.getItem() instanceof IFluidContainerItem container)) continue; + + // Split cells off the stack one at a time and fill them + while (stack.amount > 0 && cell.stackSize > 0) { + ItemStack single = cell.copy(); + single.stackSize = 1; + FluidStack fluid = stack.getFluidStack(); + if (container.fill(single, fluid, false) <= 0) break; + int filled = container.fill(single, fluid, true); + if (filled <= 0) break; + stack.amount -= filled; + cell.stackSize--; + if (cell.stackSize <= 0) inv[i] = null; + if (!player.inventory.addItemStackToInventory(single)) { + player.worldObj.spawnEntityInWorld( + new EntityItemLarge(player.worldObj, player.posX, player.posY, player.posZ, single) + ); + } + } } + + player.inventory.markDirty(); } private void consumeItemsFromPending( @@ -554,7 +564,7 @@ private void consumeItemsFromAE(List requestedItems, List dropList = new ArrayList<>(); + for (ItemStack drop : part.getDrops()) { + dropList.add(drop); + } + data.drops = new PortableItemStack[dropList.size()]; + for (int j = 0; j < dropList.size(); j++) { + data.drops[j] = PortableItemStack.withNBT(dropList.get(j)); + } + + return data; } @Override public boolean apply(IBlockApplyContext ctx) { + if (parts == null || parts.length == 0) return true; + World world = ctx.getWorld(); int x = ctx.getX(); int y = ctx.getY(); int z = ctx.getZ(); - if (!InteropConstants.FMP_BLOCK.matches(world.getBlock(x, y, z), 0)) return false; + removeExistingParts(ctx); + + PartData[] sorted = getSortedParts(); + + for (PartData data : sorted) { + if (!hasSupportBlock(world, data, x, y, z)) { + ctx.warn(new ChatComponentText("Could not place " + data.typeId + ": no support block")); + continue; + } + + if (!tryConsumePartItems(ctx, data)) { + continue; + } + + if (!tryPlacePart(ctx, world, new BlockCoord(x, y, z), data)) { return false; } + } + + syncTileToClient(world, x, y, z); + + return true; + } + + private static void removeExistingParts(IBlockApplyContext ctx) { + TileEntity te = ctx.getTileEntity(); + + if (!(te instanceof TileMultipart existingTile)) return; + + var existingParts = new ArrayList<>(existingTile.jPartList()); + + for (TMultiPart existingPart : existingParts) { + for (ItemStack drop : existingPart.getDrops()) { + ctx.givePlayerItems(drop.copy()); + } + existingTile.remPart(existingPart); + } + } + + private PartData[] getSortedParts() { + PartData[] sorted = parts.clone(); + Arrays.sort(sorted, (a, b) -> Integer.compare(getPartOrder(a), getPartOrder(b))); + return sorted; + } + + private static boolean hasSupportBlock(World world, PartData data, int x, int y, int z) { + if (data.typeId == null || data.typeId.startsWith("mcr_")) return true; + if (data.nbt == null) return true; + + int side = -1; + + if (data.nbt.hasKey("side")) { + side = data.nbt.getByte("side") & 0xFF; + } else if (data.nbt.hasKey("orient")) { + side = (data.nbt.getByte("orient") & 0xFF) >> 2; + } + + if (side < 0 || side >= 6) return true; + + ForgeDirection dir = ForgeDirection.getOrientation(side); + return !world.isAirBlock(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ); + } + + private static boolean tryConsumePartItems(IBlockApplyContext ctx, PartData data) { + if (data.drops == null) return true; + + List consumed = new ArrayList<>(); + + for (PortableItemStack drop : data.drops) { + ItemStack stack = drop.toStack(); + if (stack == null) continue; + + boolean ok = ctx + .tryConsumeItems( + Collections.singletonList(BigItemStack.create(stack)), + IPseudoInventory.CONSUME_FUZZY + ) + .leftBoolean(); - BlockCoord coord = new BlockCoord(x, y, z); + if (!ok) { + ctx.warn(new ChatComponentText("Could not find item: " + stack.getDisplayName())); - TileMultipart tile = TileMultipart.getOrConvertTile(ctx.getWorld(), coord); + for (ItemStack refund : consumed) { + ctx.givePlayerItems(refund); + } + return false; + } - var partList = tile.jPartList(); + consumed.add(stack); + } return true; } + private static boolean tryPlacePart(IBlockApplyContext ctx, World world, BlockCoord coord, PartData data) { + TMultiPart part = MultiPartRegistry.loadPart(data.typeId, data.nbt); + + if (part == null) { + refundPartItems(ctx, data); + ctx.error(new ChatComponentText("Could not create multipart: " + data.typeId)); + return false; + } + + part.load(data.nbt); + + try { + TileMultipart result = TileMultipart.addPart(world, coord, part); + + if (result == null) { + MMMod.LOG.warn( + "addPart returned null for {} (shape={}) at {},{},{}", + data.typeId, + data.nbt.hasKey("shape") ? (data.nbt.getByte("shape") & 0xFF) : "N/A", + coord.x, + coord.y, + coord.z + ); + refundPartItems(ctx, data); + ctx.warn(new ChatComponentText("Could not place multipart: " + data.typeId)); + } + } catch (Exception e) { + MMMod.LOG.error("Failed to add multipart " + data.typeId, e); + refundPartItems(ctx, data); + ctx.error(new ChatComponentText("Failed to add multipart: " + data.typeId)); + return false; + } + + return true; + } + + private static void refundPartItems(IBlockApplyContext ctx, PartData data) { + if (data.drops == null) return; + + for (PortableItemStack drop : data.drops) { + ItemStack refund = drop.toStack(); + if (refund != null) { + ctx.givePlayerItems(refund); + } + } + } + + // Don't use sendDescPacket here, it causes null-bounds crashes from redundant tile reconstruction. + private static void syncTileToClient(World world, int x, int y, int z) { + TileEntity te = world.getTileEntity(x, y, z); + if (te instanceof TileMultipart tile) { + tile.markDirty(); + world.markBlockForUpdate(x, y, z); + } + } + @Override public boolean getRequiredItemsForExistingBlock(IBlockApplyContext context) { + TileEntity te = context.getTileEntity(); + + if (te instanceof TileMultipart multipart) { + for (TMultiPart part : multipart.jPartList()) { + for (ItemStack drop : part.getDrops()) { + context.givePlayerItems(drop.copy()); + } + } + } + + consumeAllPartDrops(context); + return true; } @Override public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { + consumeAllPartDrops(context); return true; } - @Override - public void getItemTag(NBTTagCompound tag) { + private void consumeAllPartDrops(IBlockApplyContext context) { + if (parts == null) return; + + for (PartData data : parts) { + if (data.drops == null) continue; + + for (PortableItemStack drop : data.drops) { + ItemStack stack = drop.toStack(); + if (stack != null) { + context.tryConsumeItems( + Collections.singletonList(BigItemStack.create(stack)), + IPseudoInventory.CONSUME_FUZZY + ); + } + } + } + } + + // Material names are "modid:blockname" or "modid:blockname_meta". + private String getFirstMicroblockMaterialName() { + if (parts == null) return null; + + for (PartData data : parts) { + if (data == null || data.nbt == null || data.typeId == null) continue; + if (!data.typeId.startsWith("mcr_")) continue; + String materialName = data.nbt.getString("material"); + if (!materialName.isEmpty()) return materialName; + } + + return null; + } + + private static Block parseMaterialBlock(String materialName) { + Block block = Block.getBlockFromName(materialName); + if (block != null) return block; + + int i = materialName.lastIndexOf('_'); + if (i > 0) { + block = Block.getBlockFromName(materialName.substring(0, i)); + } + return block; + } + + private static int parseMaterialMeta(String materialName) { + int i = materialName.lastIndexOf('_'); + if (i > 0) { + Block base = Block.getBlockFromName(materialName.substring(0, i)); + if (base != null) { + try { + return Integer.parseInt(materialName.substring(i + 1)); + } catch (NumberFormatException e) { + return 0; + } + } + } + return 0; } @Override - public void getItemDetails(List details) { + public Block getPreviewBlock() { + String name = getFirstMicroblockMaterialName(); + return name != null ? parseMaterialBlock(name) : null; + } + @Override + public int getPreviewMeta() { + String name = getFirstMicroblockMaterialName(); + return name != null ? parseMaterialMeta(name) : 0; } @Override - public void transform(Transform transform) { + public void getItemTag(ItemStack stack) { + if (parts != null) { + NBTTagCompound tag = stack.getTagCompound() != null ? stack.getTagCompound() : new NBTTagCompound(); + tag.setInteger("FMPParts", parts.length); + stack.setTagCompound(tag); + } + } + + @Override + public void getItemDetailsChat(List details) { + if (parts != null && parts.length > 0) { + details.add(new ChatComponentText(parts.length + (parts.length == 1 ? " part" : " parts"))); + } + } + + private static int getPartOrder(PartData data) { + if (data == null || data.typeId == null) return 1; + return switch (data.typeId) { + case "mcr_face", "mcr_hllw", "mcr_edge", "mcr_cnr" -> 0; + default -> 1; + }; } @Override - public void migrate() { + public void transform(Transform transform) { + if (parts == null) return; + for (PartData data : parts) { + if (data == null || data.nbt == null) continue; + + FMPPartTransforms.transformSide(data.nbt, transform); + FMPPartTransforms.transformOrient(data.nbt, transform); + FMPPartTransforms.transformMicroblockShape(data.typeId, data.nbt, transform); + FMPPartTransforms.clearConnMap(data.nbt); + } } + @Override + public void migrate() {} + @Override public MultipartAnalysisResult clone() { MultipartAnalysisResult dup = new MultipartAnalysisResult(); + if (parts != null) { + dup.parts = new PartData[parts.length]; + for (int i = 0; i < parts.length; i++) { + dup.parts[i] = parts[i] == null ? null : parts[i].clone(); + } + } + return dup; } @Override public int hashCode() { - return 0; + return Arrays.hashCode(parts); } @Override public boolean equals(Object obj) { - return obj instanceof MultipartAnalysisResult; + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + MultipartAnalysisResult other = (MultipartAnalysisResult) obj; + return Arrays.equals(parts, other.parts); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBlock.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBlock.java index 3c41cdbd..eb10de82 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBlock.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBlock.java @@ -1,5 +1,6 @@ package com.recursive_pineapple.matter_manipulator.common.building; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Comparator; import java.util.EnumMap; @@ -7,19 +8,28 @@ import java.util.Map; import net.minecraft.block.Block; -import net.minecraft.init.Blocks; import net.minecraft.inventory.IInventory; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import net.minecraft.util.MathHelper; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; +import appeng.api.parts.IPart; +import appeng.api.parts.IPartHost; +import appeng.parts.automation.PartSharedItemBus; +import appeng.parts.misc.PartStorageBus; +import appeng.util.Platform; + +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.gtnewhorizon.gtnhlib.util.CoordinatePacker; import com.recursive_pineapple.matter_manipulator.MMMod; +import com.recursive_pineapple.matter_manipulator.asm.Optional; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.building.providers.IItemProvider; import com.recursive_pineapple.matter_manipulator.common.compat.BlockProperty; @@ -28,6 +38,7 @@ import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Location; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; import com.recursive_pineapple.matter_manipulator.common.utils.Mods; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; import com.recursive_pineapple.matter_manipulator.mixin.BlockCaptureDrops; import org.jetbrains.annotations.NotNull; @@ -47,6 +58,8 @@ public class PendingBlock extends Location { public ITileAnalysisIntegration ae; public ITileAnalysisIntegration arch; public ITileAnalysisIntegration mp; + public ITileAnalysisIntegration cb; + public transient SmartCopyIntegration smartCopy; public InventoryAnalysis inventory = null; @@ -67,6 +80,9 @@ public PendingBlock reset() { this.gt = null; this.ae = null; this.arch = null; + this.mp = null; + this.cb = null; + this.smartCopy = null; this.inventory = null; this.renderOrder = 0; this.buildOrder = 0; @@ -109,6 +125,21 @@ public Block getBlock() { return spec.getBlock(); } + public Block getPreviewBlock() { + for (var integration : getIntegrations()) { + Block preview = integration.getPreviewBlock(); + if (preview != null) return preview; + } + return null; + } + + public int getPreviewMeta() { + for (var integration : getIntegrations()) { + if (integration.getPreviewBlock() != null) return integration.getPreviewMeta(); + } + return 0; + } + public Item getItem() { return spec.getItem(); } @@ -119,21 +150,30 @@ private List getIntegrations() { if (gt != null) list.add(gt); if (ae != null) list.add(ae); if (arch != null) list.add(arch); - // if (mp != null) list.add(mp); + if (mp != null) list.add(mp); + if (cb != null) list.add(cb); + if (smartCopy != null) list.add(smartCopy); return list; } - private String getItemDetails() { - List details = new ArrayList<>(0); + private IChatComponent getItemDetailsChat() { + List details = new ArrayList<>(0); - spec.getItemDetails(details); + spec.getItemDetailsChat(details); for (var analysis : getIntegrations()) { - analysis.getItemDetails(details); + analysis.getItemDetailsChat(details); + } + + if (details.isEmpty()) { return new ChatComponentText(""); } + + IChatComponent out = details.get(0); + for (int i = 1; i < details.size(); i++) { + out.appendText(", ").appendSibling(details.get(i)); } - return details.isEmpty() ? "" : String.format(" (%s)", String.join(", ", details)); + return new ChatComponentText(" (").appendSibling(out).appendText(")"); } public ItemStack getStack() { @@ -141,14 +181,10 @@ public ItemStack getStack() { if (stack == null) return null; - NBTTagCompound tag = stack.getTagCompound() != null ? stack.getTagCompound() : new NBTTagCompound(); - for (var analysis : getIntegrations()) { - analysis.getItemTag(tag); + analysis.getItemTag(stack); } - stack.setTagCompound(tag.hasNoTags() ? null : tag); - return stack; } @@ -192,8 +228,8 @@ public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { return true; } - public String getDisplayName() { - return getStack().getDisplayName() + getItemDetails(); + public IChatComponent getDisplayNameChat() { + return new ChatComponentItemName(getStack()).appendSibling(getItemDetailsChat()); } public boolean isFree() { @@ -212,6 +248,8 @@ public PendingBlock clone() { if (ae != null) dup.ae = ae.clone(); if (arch != null) dup.arch = arch.clone(); if (mp != null) dup.mp = mp.clone(); + if (cb != null) dup.cb = cb.clone(); + if (smartCopy != null) dup.smartCopy = smartCopy.clone(); if (inventory != null) dup.inventory = inventory.clone(); dup.renderOrder = renderOrder; dup.buildOrder = buildOrder; @@ -323,28 +361,104 @@ class RefCell { try { prop.setValueFromText(world, x, y, z, value); } catch (Exception e) { - context.error("Could not apply property " + property + ": " + e.getMessage()); + context.error( + new ChatComponentTranslation( + "mm.info.error.could_not_apply_property", + new ChatComponentTranslation(property.getUnlocalizedName()), + e.getMessage() + ) + ); } } } BlockCaptureDrops.captureDrops(world); + boolean success = true; + try { for (var analysis : getIntegrations()) { - if (!analysis.apply(context)) return false; + if (!analysis.apply(context)) { + success = false; + break; + } } - if (context.getTileEntity() instanceof IInventory inventory && this.inventory != null) { - if (!this.inventory.apply(context, inventory, true, false)) return false; + if (success && context.getTileEntity() instanceof IInventory inventory && this.inventory != null) { + if (!this.inventory.apply(context, inventory, true, false)) { + success = false; + } } } finally { context.givePlayerItems(BlockCaptureDrops.stopCapturingDrops(world).toArray(new ItemStack[0])); + + Block block = world.getBlock(x, y, z); + world.notifyBlockOfNeighborChange(x, y, z, block); + + if (Mods.AppliedEnergistics2.isModLoaded()) { + notifyAE2Neighbors(world, x, y, z); + } + } + + return success ? ref.didSomething : false; + } + + @Optional(Names.APPLIED_ENERGISTICS2) + private static void notifyAE2Neighbors(World world, int x, int y, int z) { + initAE2ReflectionFields(); + + for (ForgeDirection dir : ForgeDirection.VALID_DIRECTIONS) { + TileEntity te = world.getTileEntity(x + dir.offsetX, y + dir.offsetY, z + dir.offsetZ); + + if (te instanceof IPartHost partHost) { + IPart part = partHost.getPart(dir.getOpposite()); + + if (part != null) { + resetAE2PartCache(part); + } + } + } + + Platform.notifyBlocksOfNeighbors(world, x, y, z); + } + + private static boolean ae2FieldsInitialized = false; + private static Field storageBusHandlerHash; + private static Field sharedBusCachedAdaptorHash; + + @Optional(Names.APPLIED_ENERGISTICS2) + private static void initAE2ReflectionFields() { + if (ae2FieldsInitialized) return; + ae2FieldsInitialized = true; + + try { + storageBusHandlerHash = PartStorageBus.class.getDeclaredField("handlerHash"); + storageBusHandlerHash.setAccessible(true); + } catch (Exception e) { + MMMod.LOG.warn("Could not find PartStorageBus.handlerHash", e); + } + + try { + sharedBusCachedAdaptorHash = PartSharedItemBus.class.getDeclaredField("cachedAdaptorHash"); + sharedBusCachedAdaptorHash.setAccessible(true); + } catch (Exception e) { + MMMod.LOG.warn("Could not find PartSharedItemBus.cachedAdaptorHash", e); } + } - world.notifyBlockOfNeighborChange(x, y, z, Blocks.air); + @Optional(Names.APPLIED_ENERGISTICS2) + private static void resetAE2PartCache(IPart part) { + try { + if (part instanceof PartStorageBus && storageBusHandlerHash != null) { + storageBusHandlerHash.setInt(part, 0); + } - return ref.didSomething; + if (part instanceof PartSharedItemBus && sharedBusCachedAdaptorHash != null) { + sharedBusCachedAdaptorHash.setInt(part, 0); + } + } catch (Exception e) { + MMMod.LOG.warn("Failed to reset AE2 part cache", e); + } } @Override @@ -356,6 +470,7 @@ public int hashCode() { result = prime * result + ((ae == null) ? 0 : ae.hashCode()); result = prime * result + ((arch == null) ? 0 : arch.hashCode()); result = prime * result + ((mp == null) ? 0 : mp.hashCode()); + result = prime * result + ((cb == null) ? 0 : cb.hashCode()); result = prime * result + ((inventory == null) ? 0 : inventory.hashCode()); result = prime * result + renderOrder; result = prime * result + buildOrder; @@ -383,6 +498,9 @@ public boolean equals(Object obj) { if (mp == null) { if (other.mp != null) return false; } else if (!mp.equals(other.mp)) return false; + if (cb == null) { + if (other.cb != null) return false; + } else if (!cb.equals(other.cb)) return false; if (inventory == null) { if (other.inventory != null) return false; } else if (!inventory.equals(other.inventory)) return false; @@ -411,6 +529,7 @@ public static PendingBlock fromBlock(World world, int x, int y, int z) { public static final int ANALYZE_AE = 0b1 << counter++; public static final int ANALYZE_ARCH = 0b1 << counter++; public static final int ANALYZE_MP = 0b1 << counter++; + public static final int ANALYZE_CB = 0b1 << counter++; public static final int ANALYZE_INV = 0b1 << counter++; public static final int ANALYZE_ALL = -1; @@ -428,9 +547,13 @@ public PendingBlock analyze(TileEntity te, int flags) { this.arch = ArchitectureCraftAnalysisResult.analyze(te); } - // if ((flags & ANALYZE_MP) != 0 && Mods.ForgeMultipart.isModLoaded()) { - // this.mp = MultipartAnalysisResult.analyze(te); - // } + if ((flags & ANALYZE_MP) != 0 && Mods.ForgeMultipart.isModLoaded()) { + this.mp = MultipartAnalysisResult.analyze(te); + } + + if ((flags & ANALYZE_CB) != 0 && Mods.CarpentersBlocks.isModLoaded()) { + this.cb = CarpentersBlocksAnalysisResult.analyze(te); + } if ((flags & ANALYZE_INV) != 0 && te instanceof IInventory inventory) { this.inventory = InventoryAnalysis.fromInventory(inventory, false); @@ -445,6 +568,7 @@ public PendingBlock migrate() { if (ae != null) ae.migrate(); if (arch != null) arch.migrate(); if (mp != null) mp.migrate(); + if (cb != null) cb.migrate(); return this; } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBuild.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBuild.java index 5d9683ce..9a5ec56c 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBuild.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBuild.java @@ -17,9 +17,12 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; +import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; import net.minecraft.util.StatCollector; import net.minecraft.world.World; @@ -60,6 +63,8 @@ public class PendingBuild extends AbstractBuildable { private final LongList errors = new LongArrayList(); private final LongList warnings = new LongArrayList(); + private final WirelessLinkFixer wirelessLinkFixer; + public PendingBuild( EntityPlayer player, MMState state, @@ -68,6 +73,7 @@ public PendingBuild( ) { super(player, state, tier); this.pendingBlocks = new ArrayDeque<>(pendingBlocks); + this.wirelessLinkFixer = new WirelessLinkFixer(state); } @Override @@ -81,6 +87,9 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { int shuffleCount = 0; World world = player.worldObj; + + wirelessLinkFixer.tryInit(world); + ProxiedWorld proxiedWorld = new ProxiedWorld(world); PendingBuildApplyContext applyContext = new PendingBuildApplyContext(stack); @@ -113,7 +122,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { } // if this block is protected, ignore it completely and print a warning - if (!isEditable(world, x, y, z)) { + if (!isEditable(world, x, y, z, true)) { pendingBlocks.removeFirst(); continue; } @@ -143,6 +152,8 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { playSound(world, x, y, z, SoundResource.MOB_ENDERMEN_PORTAL); } + wirelessLinkFixer.tryApply(world, x, y, z); + continue; } @@ -176,7 +187,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { } if (!tryConsumePower(stack, world, x, y, z, existing.spec)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.out_of_eu")); + sendErrorToPlayer(player, "mm.info.error.out_of_eu"); break; } } @@ -200,7 +211,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { } if (!tryConsumePower(stack, world, x, y, z, next.spec)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.out_of_eu")); + sendErrorToPlayer(player, "mm.info.error.out_of_eu"); break; } @@ -220,13 +231,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (!pendingBlocks.isEmpty()) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_place", - pendingBlocks.size() - ) + "mm.info.error.could_not_place", + pendingBlocks.size() ); } else { - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.finished_placing")); + sendInfoToPlayer(player, "mm.info.finished_placing"); } actuallyGivePlayerStuff(); @@ -251,18 +260,14 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (extracted == null) { sendWarningToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.warning.could_not_find", - toPlace.size() - ) + "mm.info.warning.could_not_find", + toPlace.size() ); sendWarningToPlayer( player, - String.format( - " %s x %d", - first.getDisplayName(), - total - ) + "mm.info.warning.of_item", + first.getDisplayNameChat(), + total ); for (PendingBlock pending : toPlace) { @@ -299,6 +304,8 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { pending.apply(applyContext, world); } + wirelessLinkFixer.tryApply(world, x, y, z); + world.notifyBlockOfNeighborChange(x, y, z, Blocks.air); continue; } @@ -314,7 +321,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (!pending.spec.isAir()) { Block block = pending.getBlock(); - if (pending.getItem() instanceof ItemBlock itemBlock) { + if (pending.getItem() instanceof ItemBlock itemBlock && Block.getBlockFromItem(itemBlock) == block) { itemBlock.placeBlockAt( perBlock, player, @@ -346,33 +353,29 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { applyContext.pendingBlock = pending; pending.apply(applyContext, world); + + wirelessLinkFixer.tryApply(world, x, y, z); } if (extracted != null && i < toPlace.size()) { sendWarningToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.warning.could_not_find", - toPlace.size() - i - ) + "mm.info.warning.could_not_find", + toPlace.size() - i ); sendWarningToPlayer( player, - String.format( - " %s x %d", - first.getDisplayName(), - total - (toPlace.size() - i) * perBlock.stackSize - ) + "mm.info.warning.of_item", + first.getDisplayNameChat(), + total - (toPlace.size() - i) * perBlock.stackSize ); } sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.placed_remaining", - i, - pendingBlocks.size() - ) + "mm.info.placed_remaining", + i, + pendingBlocks.size() ); if (extracted != null && extracted.stackSize >= perBlock.stackSize) { @@ -394,10 +397,179 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { visited.remove(coord); } + if (Mods.AppliedEnergistics2.isModLoaded()) { + for (PendingBlock placed : toPlace) { + if (placed.smartCopy != null && placed.smartCopy.action == SmartCopyIntegration.SmartCopyAction.INTERFACE_TO_P2P) { + placeSourceP2P(world, placed, applyContext); + } + } + } + actuallyGivePlayerStuff(); playSounds(); } + @Optional(Names.APPLIED_ENERGISTICS2) + private void placeSourceP2P(World world, PendingBlock placed, PendingBuildApplyContext applyContext) { + if (placed.smartCopy == null || placed.smartCopy.p2pActions == null || placed.smartCopy.p2pActions.isEmpty()) return; + + for (SmartCopyIntegration.P2PInfo info : placed.smartCopy.p2pActions) { + placeSourceP2PSingle(world, placed, info); + } + } + + @Optional(Names.APPLIED_ENERGISTICS2) + private void placeSourceP2PSingle(World world, PendingBlock placed, SmartCopyIntegration.P2PInfo info) { + int srcX = info.srcX; + int srcY = info.srcY; + int srcZ = info.srcZ; + ForgeDirection srcSide = info.srcSide; + ForgeDirection destSide = info.destSide != null ? info.destSide : srcSide; + long freq = info.freq; + + if (srcSide == null || srcSide == ForgeDirection.UNKNOWN) { + MMMod.LOG.warn("SmartCopy: skipping P2P for block at {},{},{} - invalid source side", placed.x, placed.y, placed.z); + return; + } + + TileEntity te = world.getTileEntity(srcX, srcY, srcZ); + if (!(te instanceof appeng.api.parts.IPartHost partHost)) { + MMMod.LOG.warn("SmartCopy: source at {},{},{} is not an IPartHost", srcX, srcY, srcZ); + return; + } + + java.util.List extractedPatterns = new java.util.ArrayList<>(); + appeng.api.parts.IPart existingPart = partHost.getPart(srcSide); + if (existingPart instanceof appeng.api.implementations.tiles.ISegmentedInventory srcSegInv) { + IInventory patternInv = srcSegInv.getInventoryByName("patterns"); + if (patternInv != null) { + for (int i = 0; i < patternInv.getSizeInventory(); i++) { + ItemStack pattern = patternInv.getStackInSlot(i); + if (pattern != null) { + extractedPatterns.add(pattern.copy()); + patternInv.setInventorySlotContents(i, null); + } + } + } + } + + if (existingPart != null) { + partHost.removePart(srcSide, false); + } + + ItemStack p2pStack = info.p2pItem != null ? + info.p2pItem.toStack() : + appeng.api.AEApi.instance() + .definitions() + .parts() + .p2PTunnelMEInterface() + .maybeStack(1) + .orNull(); + if (p2pStack == null) { + MMMod.LOG.warn("SmartCopy: no P2P item available for source at {},{},{} side {}", srcX, srcY, srcZ, srcSide); + for (ItemStack pattern : extractedPatterns) { + givePlayerItems(pattern); + } + return; + } + + if (!tryConsumeItems(p2pStack)) { + sendWarningToPlayer( + player, + "mm.info.warning.only_message", + srcX, + srcY, + srcZ, + new ChatComponentText("Could not find P2P Interface for source side " + srcSide) + ); + for (ItemStack pattern : extractedPatterns) { + givePlayerItems(pattern); + } + return; + } + + if (partHost.addPart(p2pStack, srcSide, player) == null) { + givePlayerItems(p2pStack); + sendWarningToPlayer( + player, + "mm.info.warning.only_message", + srcX, + srcY, + srcZ, + new ChatComponentText("Could not place P2P Interface on source " + srcSide + " side") + ); + for (ItemStack pattern : extractedPatterns) { + givePlayerItems(pattern); + } + return; + } + + appeng.api.parts.IPart srcPart = partHost.getPart(srcSide); + if (srcPart instanceof appeng.parts.p2p.PartP2PTunnel tunnel) { + tunnel.output = false; + try { + appeng.me.cache.P2PCache p2p = tunnel.getProxy() + .getP2P(); + p2p.updateFreq(tunnel, freq); + } catch (appeng.me.GridAccessException e) { + tunnel.setFrequency(freq); + } + tunnel.onTunnelConfigChange(); + } + + playSound(world, srcX, srcY, srcZ, SoundResource.MOB_ENDERMEN_PORTAL); + + if (!extractedPatterns.isEmpty()) { + appeng.api.parts.IPart newSrcPart = partHost.getPart(srcSide); + if (newSrcPart instanceof appeng.api.implementations.tiles.ISegmentedInventory srcSegInv) { + IInventory srcPatternInv = srcSegInv.getInventoryByName("patterns"); + if (srcPatternInv != null) { + int slot = 0; + for (ItemStack pattern : extractedPatterns) { + while (slot < srcPatternInv.getSizeInventory() && srcPatternInv.getStackInSlot(slot) != null) { + slot++; + } + if (slot >= srcPatternInv.getSizeInventory()) { + MMMod.LOG.warn( + "SmartCopy: source P2P pattern inventory full at {},{},{} side {} - returning remaining patterns to player", + srcX, + srcY, + srcZ, + srcSide + ); + givePlayerItems(pattern); + continue; + } + srcPatternInv.setInventorySlotContents(slot, pattern); + slot++; + } + } else { + MMMod.LOG.warn( + "SmartCopy: no pattern inventory on source P2P at {},{},{} side {} - returning patterns to player", + srcX, + srcY, + srcZ, + srcSide + ); + for (ItemStack pattern : extractedPatterns) { + givePlayerItems(pattern); + } + } + } else { + MMMod.LOG.warn( + "SmartCopy: source P2P at {},{},{} side {} is not a segmented inventory - returning patterns to player", + srcX, + srcY, + srcZ, + srcSide + ); + for (ItemStack pattern : extractedPatterns) { + givePlayerItems(pattern); + } + } + } + } + @Override public void onStopped() { if (pendingItems.size() > 0 || pendingFluids.size() > 0) { @@ -499,76 +671,107 @@ public void givePlayerFluids(List fluids) { } @Override - public void warn(String message) { - String blockName = null; - + public void warn(IChatComponent message) { + IChatComponent blockNameComponent = null; if (pendingBlock.isInWorld(player.worldObj)) { - if (GregTech.isModLoaded()) blockName = getGTBlockName(pendingBlock); + String gtBlockNameKey = null; + if (GregTech.isModLoaded()) gtBlockNameKey = getGTBlockUnlocalizedName(pendingBlock); - if (blockName == null) { + if (gtBlockNameKey == null) { BlockSpec spec = BlockSpec.fromBlock(null, player.worldObj, pendingBlock.x, pendingBlock.y, pendingBlock.z); - if (InteropConstants.AE_BLOCK_CABLE.matches(spec)) { - blockName = InteropConstants.AE_BLOCK_CABLE.toSpec().getDisplayName(); + blockNameComponent = InteropConstants.AE_BLOCK_CABLE.toSpec().getChatComponent(); } else { - blockName = spec.getDisplayName(); + blockNameComponent = spec.getChatComponent(); } + } else { + blockNameComponent = new ChatComponentText(gtBlockNameKey); } } - sendWarningToPlayer( - player, - StatCollector.translateToLocalFormatted( - "mm.info.warning", + if (blockNameComponent != null) { + sendWarningToPlayer( + player, + "mm.info.warning.with_block", pendingBlock.x, pendingBlock.y, pendingBlock.z, - blockName != null ? " (" + blockName + ")" : "", + blockNameComponent, message - ) - ); + ); + } else { + sendWarningToPlayer( + player, + "mm.info.warning.only_message", + pendingBlock.x, + pendingBlock.y, + pendingBlock.z, + message + ); + } PendingBuild.this.warnings.add(CoordinatePacker.pack(pendingBlock.x, pendingBlock.y, pendingBlock.z)); } @Override - public void error(String message) { - - String blockName = null; - + public void error(IChatComponent message) { + IChatComponent blockNameComponent = null; if (pendingBlock.isInWorld(player.worldObj)) { - if (GregTech.isModLoaded()) blockName = getGTBlockName(pendingBlock); + String gtBlockNameKey = null; + + if (GregTech.isModLoaded()) gtBlockNameKey = getGTBlockUnlocalizedName(pendingBlock); BlockSpec spec = BlockSpec.fromBlock(null, player.worldObj, pendingBlock.x, pendingBlock.y, pendingBlock.z); - if (InteropConstants.AE_BLOCK_CABLE.matches(spec)) { - blockName = InteropConstants.AE_BLOCK_CABLE.toSpec().getDisplayName(); + if (gtBlockNameKey == null) { + if (InteropConstants.AE_BLOCK_CABLE.matches(spec)) { + blockNameComponent = InteropConstants.AE_BLOCK_CABLE.toSpec().getChatComponent(); + } else { + blockNameComponent = spec.getChatComponent(); + } } else { - blockName = spec.getDisplayName(); + blockNameComponent = new ChatComponentText(gtBlockNameKey); } } - sendErrorToPlayer( - player, - StatCollector.translateToLocalFormatted( - "mm.info.error", + if (blockNameComponent != null) { + sendErrorToPlayer( + player, + "mm.info.error.with_block", pendingBlock.x, pendingBlock.y, pendingBlock.z, - blockName != null ? " (" + blockName + ")" : "", + blockNameComponent, message - ) - ); + ); + } else { + sendErrorToPlayer( + player, + "mm.info.error.only_message", + pendingBlock.x, + pendingBlock.y, + pendingBlock.z, + message + ); + } PendingBuild.this.errors.add(CoordinatePacker.pack(pendingBlock.x, pendingBlock.y, pendingBlock.z)); } } @Optional(Names.GREG_TECH_NH) - private String getGTBlockName(PendingBlock pendingBlock) { + private String getGTBlockUnlocalizedName(PendingBlock pendingBlock) { if (player.worldObj.getTileEntity(pendingBlock.x, pendingBlock.y, pendingBlock.z) instanceof IGregTechTileEntity igte) { IMetaTileEntity imte = igte.getMetaTileEntity(); - if (imte != null) { return imte.getLocalName(); } + if (imte != null) { + // FIXME: should use more robust method to get unlocalized name + String key = "gt.blockmachines." + imte.getMetaName() + ".name"; + if (StatCollector.canTranslate(key)) { + return key; + } else { + return imte.getLocalName(); + } + } } return null; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingMove.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingMove.java index b276eeaf..b23d15a0 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingMove.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingMove.java @@ -10,20 +10,13 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.StatCollector; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; -import gregtech.api.interfaces.metatileentity.IMetaTileEntity; -import gregtech.api.interfaces.tileentity.IGregTechTileEntity; -import gregtech.api.interfaces.tileentity.IIC2Enet; -import gregtech.api.metatileentity.BaseMetaTileEntity; - -import com.github.bsideup.jabel.Desugar; import com.recursive_pineapple.matter_manipulator.asm.Optional; +import com.recursive_pineapple.matter_manipulator.common.building.movers.BlockMover; +import com.recursive_pineapple.matter_manipulator.common.building.movers.BlockMovers; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.ItemMatterManipulator.ManipulatorTier; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Location; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.MMConfig; @@ -35,11 +28,7 @@ import com.recursive_pineapple.matter_manipulator.mixin.BlockCaptureDrops; import WayofTime.alchemicalWizardry.api.event.TeleposeEvent; -import codechicken.multipart.MultipartHelper; -import codechicken.multipart.TileMultipart; import it.unimi.dsi.fastutil.Pair; -import tectech.thing.metaTileEntity.pipe.MTEPipeData; -import tectech.thing.metaTileEntity.pipe.MTEPipeLaser; /** * Handles all moving logic. @@ -48,10 +37,49 @@ public class PendingMove extends AbstractBuildable { private List> moves = null; + private int moveOffsetX, moveOffsetY, moveOffsetZ; + private int srcMinX, srcMinY, srcMinZ, srcMaxX, srcMaxY, srcMaxZ; + public PendingMove(EntityPlayer player, MMState state, ManipulatorTier tier) { super(player, state, tier); } + public int getMoveOffsetX() { + return moveOffsetX; + } + + public int getMoveOffsetY() { + return moveOffsetY; + } + + public int getMoveOffsetZ() { + return moveOffsetZ; + } + + public int getSrcMinX() { + return srcMinX; + } + + public int getSrcMinY() { + return srcMinY; + } + + public int getSrcMinZ() { + return srcMinZ; + } + + public int getSrcMaxX() { + return srcMaxX; + } + + public int getSrcMaxY() { + return srcMaxY; + } + + public int getSrcMaxZ() { + return srcMaxZ; + } + @Override public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { resetWarnings(); @@ -79,16 +107,14 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { Location d = move.right(); // if either block is protected, ignore them completely and print a warning - if (!isEditable(world, s.x, s.y, s.z) || !isEditable(world, d.x, d.y, d.z)) { + if (!isEditable(world, s.x, s.y, s.z, false) || !isEditable(world, d.x, d.y, d.z, true)) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_protected_block", - s.x, - s.y, - s.z, - source.getDisplayName() - ) + "mm.info.error.could_not_move_protected_block.new", + s.x, + s.y, + s.z, + source.getChatComponent() ); iter.remove(); continue; @@ -104,13 +130,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (source.getBlock().getBlockHardness(world, s.x, s.y, s.z) < 0) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_invulnerable_block", - s.x, - s.y, - s.z, - source.getDisplayName() - ) + "mm.info.error.could_not_move_invulnerable_block.new", + s.x, + s.y, + s.z, + source.getChatComponent() ); iter.remove(); continue; @@ -130,13 +154,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (!canPlace) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_blocked_block", - d.x, - d.y, - d.z, - source.getDisplayName() - ) + "mm.info.error.could_not_move_blocked_block.new", + d.x, + d.y, + d.z, + source.getChatComponent() ); iter.remove(); continue; @@ -145,7 +167,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { // remove the existing block if needed if (!target.getBlock().isAir(world, d.x, d.y, d.z)) { if (!tryConsumePower(stack, world, d.x, d.y, d.z, target)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.out_of_eu")); + sendErrorToPlayer(player, "mm.info.error.out_of_eu"); break; } @@ -160,21 +182,19 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { } if (!tryConsumePower(stack, world, s.x, s.y, s.z, source)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.out_of_eu")); + sendErrorToPlayer(player, "mm.info.error.out_of_eu"); break; } // try to move the source block into the (now empty) target block - if (!moveBlock(world, s, source, d, target)) { + if (!moveBlock(this, world, s, source, d, target)) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_block", - s.x, - s.y, - s.z, - source.getDisplayName() - ) + "mm.info.error.could_not_move_block.new", + s.x, + s.y, + s.z, + source.getChatComponent() ); } @@ -193,16 +213,14 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (ops > 0) { sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.process_move", - ops, - moves.size() - ) + "mm.info.process_move", + ops, + moves.size() ); } else { sendInfoToPlayer( player, - StatCollector.translateToLocal("mm.info.finished_move") + "mm.info.finished_move" ); } } @@ -223,7 +241,7 @@ private void initMoves() { MMConfig.VoxelAABB paste = cut.clone().moveOrigin(dest.toVec()); if (cut.toBoundingBox().intersectsWith(paste.toBoundingBox())) { - MMUtils.sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.move_overlapping")); + MMUtils.sendErrorToPlayer(player, "mm.info.error.move_overlapping"); return; } @@ -243,6 +261,16 @@ private void initMoves() { int worldId = startA.worldId; + moveOffsetX = dest.x - startA.x; + moveOffsetY = dest.y - startA.y; + moveOffsetZ = dest.z - startA.z; + srcMinX = minX; + srcMinY = minY; + srcMinZ = minZ; + srcMaxX = maxX; + srcMaxY = maxY; + srcMaxZ = maxZ; + for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { for (int z = minZ; z <= maxZ; z++) { @@ -264,7 +292,14 @@ private void initMoves() { // 'borrowed' from // https://github.com/GTNewHorizons/BloodMagic/blob/master/src/main/java/WayofTime/alchemicalWizardry/common/block/BlockTeleposer.java#L158 @SuppressWarnings("unchecked") - public static boolean moveBlock(World world, Location s, BlockSpec spec1, Location d, BlockSpec spec2) { + public static boolean moveBlock( + PendingMove pendingMove, + World world, + Location s, + BlockSpec spec1, + Location d, + BlockSpec spec2 + ) { World worldS = world.provider.dimensionId == s.worldId ? world : s.getWorld(); int sx = s.x; @@ -284,18 +319,18 @@ public static boolean moveBlock(World world, Location s, BlockSpec spec1, Locati if (!allowTelepose(worldS, worldD, s, spec1, d, spec2)) return false; } - BlockMover source = (BlockMover) getBlockMover(worldS, sx, sy, sz); - BlockMover dest = (BlockMover) getBlockMover(worldD, dx, dy, dz); + BlockMover source = (BlockMover) BlockMovers.getBlockMover(worldS, sx, sy, sz); + BlockMover dest = (BlockMover) BlockMovers.getBlockMover(worldD, dx, dy, dz); try { BlockCaptureDrops.captureDrops(worldS); if (worldS != worldD) BlockCaptureDrops.captureDrops(worldD); - Object sourceState = source.remove(worldS, sx, sy, sz); - Object destState = dest.remove(worldD, dx, dy, dz); + Object sourceState = source.remove(pendingMove, worldS, sx, sy, sz); + Object destState = dest.remove(pendingMove, worldD, dx, dy, dz); - source.place(worldD, dx, dy, dz, sourceState); - dest.place(worldS, sx, sy, sz, destState); + source.place(pendingMove, worldD, dx, dy, dz, sourceState); + dest.place(pendingMove, worldS, sx, sy, sz, destState); } finally { // delete any items that were dropped BlockCaptureDrops.stopCapturingDrops(worldS); @@ -323,177 +358,4 @@ private static boolean allowTelepose(World worldI, World worldF, Location s, Blo ); return !MinecraftForge.EVENT_BUS.post(evt); } - - private static BlockMover getBlockMover(World world, int x, int y, int z) { - for (BlockMovers blockMover : BlockMovers.values()) { - if (blockMover.blockMover.canMove(world, x, y, z)) return blockMover.blockMover; - } - - throw new IllegalStateException(); - } - - private enum BlockMovers { - - @Optional(Names.FORGE_MULTIPART) - FMP(FMPBlockMover.INSTANCE), - @Optional(Names.GREG_TECH_NH) - GT(GTBlockMover.INSTANCE), - Standard(StandardBlockMover.INSTANCE); - - public final BlockMover blockMover; - - BlockMovers(BlockMover blockMover) { - this.blockMover = blockMover; - } - } - - interface BlockMover { - - boolean canMove(World world, int x, int y, int z); - - State remove(World world, int x, int y, int z); - - void place(World world, int x, int y, int z, State state); - } - - @Desugar - private record StandardBlock(Block block, int meta, NBTTagCompound tileData) { - - } - - private static class StandardBlockMover implements BlockMover { - - public static final StandardBlockMover INSTANCE = new StandardBlockMover(); - - @Override - public boolean canMove(World world, int x, int y, int z) { - return true; - } - - @Override - public StandardBlock remove(World world, int x, int y, int z) { - Block block = world.getBlock(x, y, z); - int meta = world.getBlockMetadata(x, y, z); - NBTTagCompound tag = null; - - TileEntity te = world.getTileEntity(x, y, z); - - if (te != null) { - tag = new NBTTagCompound(); - te.writeToNBT(tag); - } - - world.setTileEntity(x, y, z, null); - world.setBlockToAir(x, y, z); - - return new StandardBlock(block, meta, tag); - } - - @Override - public void place(World world, int x, int y, int z, StandardBlock standardBlock) { - world.setBlock(x, y, z, standardBlock.block, standardBlock.meta, 3); - - if (standardBlock.tileData != null) { - TileEntity te = TileEntity.createAndLoadEntity(standardBlock.tileData); - - te.xCoord = x; - te.yCoord = y; - te.zCoord = z; - - world.setTileEntity(x, y, z, te); - } - } - } - - private static class GTBlockMover extends StandardBlockMover { - - public static final GTBlockMover INSTANCE = new GTBlockMover(); - - @Override - public boolean canMove(World world, int x, int y, int z) { - return world.getTileEntity(x, y, z) instanceof IGregTechTileEntity; - } - - @Override - public StandardBlock remove(World world, int x, int y, int z) { - // Because GT uses this to call MTE.onRemoval() :doom: - world.getBlock(x, y, z).getDrops(world, x, y, z, world.getBlockMetadata(x, y, z), 0); - - return super.remove(world, x, y, z); - } - - @Override - public void place(World world, int x, int y, int z, StandardBlock standardBlock) { - super.place(world, x, y, z, standardBlock); - - TileEntity te = world.getTileEntity(x, y, z); - - if (te instanceof IGregTechTileEntity igte) { - if (igte instanceof BaseMetaTileEntity bmte) { - bmte.setCableUpdateDelay(100); - } - - IMetaTileEntity imte = igte.getMetaTileEntity(); - - if (imte instanceof MTEPipeLaser laserPipe) { - laserPipe.updateNeighboringNetworks(); - } - - if (imte instanceof MTEPipeData dataPipe) { - dataPipe.updateNeighboringNetworks(); - } - } - - if (te instanceof IIC2Enet enet) { - enet.doEnetUpdate(); - } - } - } - - @Desugar - private record FMPBlock(Block block, int meta, TileMultipart tile) { - - } - - private static class FMPBlockMover implements BlockMover { - - public static final FMPBlockMover INSTANCE = new FMPBlockMover(); - - @Override - public boolean canMove(World world, int x, int y, int z) { - return world.getTileEntity(x, y, z) instanceof TileMultipart; - } - - @Override - public FMPBlock remove(World world, int x, int y, int z) { - Block block = world.getBlock(x, y, z); - int meta = world.getBlockMetadata(x, y, z); - TileMultipart te = (TileMultipart) world.getTileEntity(x, y, z); - - world.setTileEntity(x, y, z, null); - world.setBlockToAir(x, y, z); - - return new FMPBlock(block, meta, te); - } - - @Override - public void place(World world, int x, int y, int z, FMPBlock fmpBlock) { - world.setBlock(x, y, z, fmpBlock.block, fmpBlock.meta, 3); - - fmpBlock.tile.xCoord = x; - fmpBlock.tile.yCoord = y; - fmpBlock.tile.zCoord = z; - - fmpBlock.tile.validate(); - world.setTileEntity(x, y, z, fmpBlock.tile); - - fmpBlock.tile.onMoved(); - - world.markBlockForUpdate(x, y, z); - world.func_147451_t(x, y, z); - fmpBlock.tile.markDirty(); - fmpBlock.tile.markRender(); - MultipartHelper.sendDescPacket(world, fmpBlock.tile); - } - } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/SmartCopyIntegration.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/SmartCopyIntegration.java new file mode 100644 index 00000000..b933eca0 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/SmartCopyIntegration.java @@ -0,0 +1,103 @@ +package com.recursive_pineapple.matter_manipulator.common.building; + +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.IChatComponent; + +import net.minecraftforge.common.util.ForgeDirection; + +import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; + +public class SmartCopyIntegration implements ITileAnalysisIntegration { + + public enum SmartCopyAction { + NONE, + CRIB_TO_PROXY, + INTERFACE_TO_P2P + } + + public static class P2PInfo { + + public long freq; + public int srcX, srcY, srcZ; + public ForgeDirection srcSide; + public ForgeDirection destSide; + public PortableItemStack p2pItem; + + public P2PInfo clone() { + P2PInfo dup = new P2PInfo(); + dup.freq = freq; + dup.srcX = srcX; + dup.srcY = srcY; + dup.srcZ = srcZ; + dup.srcSide = srcSide; + dup.destSide = destSide; + dup.p2pItem = p2pItem; + return dup; + } + } + + public SmartCopyAction action = SmartCopyAction.NONE; + public int sourceX, sourceY, sourceZ; + public List p2pActions; + + @Override + public boolean apply(IBlockApplyContext ctx) { + return true; + } + + @Override + public boolean getRequiredItemsForExistingBlock(IBlockApplyContext context) { + return true; + } + + @Override + public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { + return true; + } + + @Override + public void getItemTag(ItemStack stack) { + + } + + @Override + public void getItemDetailsChat(List details) { + + } + + @Override + public void transform(Transform transform) { + if (p2pActions != null) { + for (P2PInfo info : p2pActions) { + if (info.destSide != null && info.destSide != ForgeDirection.UNKNOWN) { + info.destSide = transform.apply(info.destSide); + } + } + } + } + + @Override + public SmartCopyIntegration clone() { + SmartCopyIntegration dup = new SmartCopyIntegration(); + dup.action = action; + dup.sourceX = sourceX; + dup.sourceY = sourceY; + dup.sourceZ = sourceZ; + if (p2pActions != null) { + dup.p2pActions = new ArrayList<>(p2pActions.size()); + for (P2PInfo info : p2pActions) { + dup.p2pActions.add(info.clone()); + } + } + return dup; + } + + @Override + public void migrate() { + + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/WirelessLinkFixer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/WirelessLinkFixer.java new file mode 100644 index 00000000..e95c2926 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/WirelessLinkFixer.java @@ -0,0 +1,271 @@ +package com.recursive_pineapple.matter_manipulator.common.building; + +import java.util.HashMap; +import java.util.HashSet; + +import net.minecraft.block.Block; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import net.minecraftforge.oredict.OreDictionary; + +import appeng.api.util.DimensionalCoord; +import appeng.tile.networking.TileWirelessBase; + +import com.gtnewhorizon.gtnhlib.util.CoordinatePacker; +import com.recursive_pineapple.matter_manipulator.MMMod; +import com.recursive_pineapple.matter_manipulator.asm.Optional; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Location; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.MMState; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.MMState.PlaceMode; +import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; +import com.recursive_pineapple.matter_manipulator.common.utils.MMUtils; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; + +import org.joml.Vector3i; + +public class WirelessLinkFixer { + + private final MMState state; + private HashMap> wirelessLinkMap = null; + private boolean initialized = false; + + public WirelessLinkFixer(MMState state) { + this.state = state; + } + + public boolean hasLinks() { + return wirelessLinkMap != null; + } + + public void tryInit(World world) { + if (!initialized && Mods.AppliedEnergistics2.isModLoaded()) { + try { + init(world); + } catch (Exception e) { + MMMod.LOG.error("[WirelessLink] Exception in initWirelessLinkMap", e); + initialized = true; + } + } + } + + public void tryApply(World world, int x, int y, int z) { + if (wirelessLinkMap != null && Mods.AppliedEnergistics2.isModLoaded()) { + apply(world, x, y, z); + } + } + + @Optional(Names.APPLIED_ENERGISTICS2) + private void init(World world) { + initialized = true; + + if (state.config.placeMode != PlaceMode.COPYING) { + MMMod.LOG.debug("[WirelessLink] Skipping: not in COPYING mode (mode={})", state.config.placeMode); + return; + } + + Location coordA = state.config.coordA; + Location coordB = state.config.coordB; + Location coordC = state.config.coordC; + + if (!Location.areCompatible(coordA, coordB, coordC)) { + MMMod.LOG.debug("[WirelessLink] Skipping: coordinates not compatible"); + return; + } + + boolean linkExternalHubs = state.config.linkExternalHubs; + + MMMod.LOG.debug("[WirelessLink] Initializing wireless link map with linkExternalHubs={}", linkExternalHubs); + + int srcMinX = Math.min(coordA.x, coordB.x); + int srcMinY = Math.min(coordA.y, coordB.y); + int srcMinZ = Math.min(coordA.z, coordB.z); + int srcMaxX = Math.max(coordA.x, coordB.x); + int srcMaxY = Math.max(coordA.y, coordB.y); + int srcMaxZ = Math.max(coordA.z, coordB.z); + + Transform t = state.getTransform(); + t.cacheRotation(); + + try { + HashMap allConnectorDests = new HashMap<>(); + HashMap> tempMap = new HashMap<>(); + + for (int sy = srcMinY; sy <= srcMaxY; sy++) { + for (int sx = srcMinX; sx <= srcMaxX; sx++) { + for (int sz = srcMinZ; sz <= srcMaxZ; sz++) { + Block block = world.getBlock(sx, sy, sz); + if (!InteropConstants.isWirelessConnector(block, OreDictionary.WILDCARD_VALUE)) continue; + + Vector3i destVec = t.apply(new Vector3i(sx - coordA.x, sy - coordA.y, sz - coordA.z)); + destVec.add(coordC.x, coordC.y, coordC.z); + + long srcPacked = CoordinatePacker.pack(sx, sy, sz); + long destPacked = CoordinatePacker.pack(destVec.x, destVec.y, destVec.z); + + allConnectorDests.put(srcPacked, destPacked); + + TileEntity te = world.getTileEntity(sx, sy, sz); + if (!(te instanceof TileWirelessBase tw)) continue; + + for (DimensionalCoord linkCoord : tw.getConnectedCoords()) { + if (linkCoord.getDimension() != world.provider.dimensionId) continue; + + int lx = linkCoord.x; + int ly = linkCoord.y; + int lz = linkCoord.z; + + boolean linkInRegion = lx >= srcMinX && lx <= srcMaxX && ly >= srcMinY && ly <= srcMaxY && lz >= srcMinZ && lz <= srcMaxZ; + + boolean shouldInclude = false; + + if (linkInRegion) { + shouldInclude = true; + } else if (linkExternalHubs) { + if (world.blockExists(lx, ly, lz)) { + TileEntity linkTe = world.getTileEntity(lx, ly, lz); + if (linkTe instanceof TileWirelessBase linkTw) { + if (linkTw.isHub()) { + shouldInclude = true; + } + } + } + } + + if (!shouldInclude) continue; + + MMMod.LOG.trace( + "[WirelessLink] Found connector at ({},{},{}) linked to ({},{},{}), linkInRegion={}", + sx, + sy, + sz, + lx, + ly, + lz, + linkInRegion + ); + + long linkSrcPacked = CoordinatePacker.pack(lx, ly, lz); + + tempMap.computeIfAbsent(srcPacked, ignored -> new HashSet<>()).add(linkSrcPacked); + } + } + } + } + + if (tempMap.isEmpty()) return; + + wirelessLinkMap = new HashMap<>(); + + for (var entry : tempMap.entrySet()) { + long destPacked = allConnectorDests.get(entry.getKey()); + + for (long linkSrcPacked : entry.getValue()) { + Long partnerDest = allConnectorDests.get(linkSrcPacked); + if (partnerDest != null) { + addLink(wirelessLinkMap, destPacked, partnerDest); + } else if (linkExternalHubs) { + addLink(wirelessLinkMap, destPacked, linkSrcPacked); + } + } + } + + if (state.config.arraySpan != null && !wirelessLinkMap.isEmpty()) { + Vector3i deltas = MMUtils.getRegionDeltas(coordA, coordB); + if (deltas == null) return; + + HashSet internalDests = new HashSet<>(allConnectorDests.values()); + + HashMap> baseMap = new HashMap<>(); + wirelessLinkMap.forEach((key, value) -> baseMap.put(key, new HashSet<>(value))); + wirelessLinkMap.clear(); + + MMUtils.forEachArrayOffset(state.config.arraySpan, deltas, d -> { + t.apply(d); + + for (var baseEntry : baseMap.entrySet()) { + long baseDest = baseEntry.getKey(); + + int bx = CoordinatePacker.unpackX(baseDest) + d.x; + int by = CoordinatePacker.unpackY(baseDest) + d.y; + int bz = CoordinatePacker.unpackZ(baseDest) + d.z; + + long offsetDest = CoordinatePacker.pack(bx, by, bz); + + for (long basePartner : baseEntry.getValue()) { + if (internalDests.contains(basePartner)) { + int px = CoordinatePacker.unpackX(basePartner) + d.x; + int py = CoordinatePacker.unpackY(basePartner) + d.y; + int pz = CoordinatePacker.unpackZ(basePartner) + d.z; + + addLink(wirelessLinkMap, offsetDest, CoordinatePacker.pack(px, py, pz)); + } else { + addLink(wirelessLinkMap, offsetDest, basePartner); + } + } + } + }); + } + + if (wirelessLinkMap.isEmpty()) { + MMMod.LOG.debug("[WirelessLink] Link map is empty after processing, setting to null"); + wirelessLinkMap = null; + } else { + MMMod.LOG.debug("[WirelessLink] Built link map with {} entries", wirelessLinkMap.size()); + for (var e : wirelessLinkMap.entrySet()) { + for (long partner : e.getValue()) { + MMMod.LOG.trace( + "[WirelessLink] ({},{},{}) -> ({},{},{})", + CoordinatePacker.unpackX(e.getKey()), + CoordinatePacker.unpackY(e.getKey()), + CoordinatePacker.unpackZ(e.getKey()), + CoordinatePacker.unpackX(partner), + CoordinatePacker.unpackY(partner), + CoordinatePacker.unpackZ(partner) + ); + } + } + } + } finally { + t.uncacheRotation(); + } + } + + private static void addLink(HashMap> links, long connector, long partner) { + links.computeIfAbsent(connector, ignored -> new HashSet<>()).add(partner); + } + + @Optional(Names.APPLIED_ENERGISTICS2) + private void apply(World world, int x, int y, int z) { + long packed = CoordinatePacker.pack(x, y, z); + HashSet partnerPackedSet = wirelessLinkMap.get(packed); + if (partnerPackedSet == null) return; + + Block block = world.getBlock(x, y, z); + if (!InteropConstants.isWirelessConnector(block, OreDictionary.WILDCARD_VALUE)) { + MMMod.LOG.debug("[WirelessLink] applyWirelessLink({},{},{}): block is not a wireless connector", x, y, z); + return; + } + + TileEntity te = world.getTileEntity(x, y, z); + if (!(te instanceof TileWirelessBase wireless)) { + MMMod.LOG.debug("[WirelessLink] applyWirelessLink({},{},{}): tile entity is not a TileWirelessBase", x, y, z); + return; + } + + wireless.doUnlink(); + + for (long partnerPacked : partnerPackedSet) { + int px = CoordinatePacker.unpackX(partnerPacked); + int py = CoordinatePacker.unpackY(partnerPacked); + int pz = CoordinatePacker.unpackZ(partnerPacked); + + MMMod.LOG.debug("[WirelessLink] Applying link at ({},{},{}) -> ({},{},{})", x, y, z, px, py, pz); + + wireless.injectConnection(new DimensionalCoord(world, px, py, pz)); + } + + wireless.markDirty(); + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMover.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMover.java new file mode 100644 index 00000000..dc33614e --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMover.java @@ -0,0 +1,14 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.world.World; + +import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; + +public interface BlockMover { + + boolean canMove(World world, int x, int y, int z); + + State remove(PendingMove pendingMove, World world, int x, int y, int z); + + void place(PendingMove pendingMove, World world, int x, int y, int z, State state); +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMovers.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMovers.java new file mode 100644 index 00000000..a557d277 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMovers.java @@ -0,0 +1,31 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.world.World; + +import com.recursive_pineapple.matter_manipulator.asm.Optional; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; + +public enum BlockMovers { + + @Optional(Names.FORGE_MULTIPART) + FMP(FMPBlockMover.INSTANCE), + @Optional(Names.GREG_TECH_NH) + GT(GTBlockMover.INSTANCE), + @Optional(Names.APPLIED_ENERGISTICS2) + Wireless(WirelessBlockMover.INSTANCE), + Standard(StandardBlockMover.INSTANCE); + + public final BlockMover blockMover; + + BlockMovers(BlockMover blockMover) { + this.blockMover = blockMover; + } + + public static BlockMover getBlockMover(World world, int x, int y, int z) { + for (BlockMovers blockMover : BlockMovers.values()) { + if (blockMover.blockMover.canMove(world, x, y, z)) return blockMover.blockMover; + } + + throw new IllegalStateException(); + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlock.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlock.java new file mode 100644 index 00000000..496110a9 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlock.java @@ -0,0 +1,12 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.block.Block; + +import com.github.bsideup.jabel.Desugar; + +import codechicken.multipart.TileMultipart; + +@Desugar +public record FMPBlock(Block block, int meta, TileMultipart tile) { + +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlockMover.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlockMover.java new file mode 100644 index 00000000..aa1b9ff8 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlockMover.java @@ -0,0 +1,51 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.block.Block; +import net.minecraft.world.World; + +import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; + +import codechicken.multipart.MultipartHelper; +import codechicken.multipart.TileMultipart; + +public class FMPBlockMover implements BlockMover { + + public static final FMPBlockMover INSTANCE = new FMPBlockMover(); + + @Override + public boolean canMove(World world, int x, int y, int z) { + return world.getTileEntity(x, y, z) instanceof TileMultipart; + } + + @Override + public FMPBlock remove(PendingMove pendingMove, World world, int x, int y, int z) { + Block block = world.getBlock(x, y, z); + int meta = world.getBlockMetadata(x, y, z); + TileMultipart te = (TileMultipart) world.getTileEntity(x, y, z); + + world.removeTileEntity(x, y, z); + world.setBlockToAir(x, y, z); + + return new FMPBlock(block, meta, te); + } + + @Override + public void place(PendingMove pendingMove, World world, int x, int y, int z, FMPBlock fmpBlock) { + world.setBlock(x, y, z, fmpBlock.block(), fmpBlock.meta(), 3); + + fmpBlock.tile().xCoord = x; + fmpBlock.tile().yCoord = y; + fmpBlock.tile().zCoord = z; + + fmpBlock.tile().validate(); + world.setTileEntity(x, y, z, fmpBlock.tile()); + + fmpBlock.tile().onMoved(); + + world.markBlockForUpdate(x, y, z); + world.func_147451_t(x, y, z); + fmpBlock.tile().markDirty(); + fmpBlock.tile().markRender(); + MultipartHelper.sendDescPacket(world, fmpBlock.tile()); + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/GTBlockMover.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/GTBlockMover.java new file mode 100644 index 00000000..f528cee9 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/GTBlockMover.java @@ -0,0 +1,59 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.interfaces.tileentity.IIC2Enet; +import gregtech.api.metatileentity.BaseMetaTileEntity; + +import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; + +import tectech.thing.metaTileEntity.pipe.MTEPipeData; +import tectech.thing.metaTileEntity.pipe.MTEPipeLaser; + +public class GTBlockMover extends StandardBlockMover { + + public static final GTBlockMover INSTANCE = new GTBlockMover(); + + @Override + public boolean canMove(World world, int x, int y, int z) { + return world.getTileEntity(x, y, z) instanceof IGregTechTileEntity; + } + + @Override + public StandardBlock remove(PendingMove pendingMove, World world, int x, int y, int z) { + // Because GT uses this to call MTE.onRemoval() :doom: + world.getBlock(x, y, z).getDrops(world, x, y, z, world.getBlockMetadata(x, y, z), 0); + + return super.remove(pendingMove, world, x, y, z); + } + + @Override + public void place(PendingMove pendingMove, World world, int x, int y, int z, StandardBlock standardBlock) { + super.place(pendingMove, world, x, y, z, standardBlock); + + TileEntity te = world.getTileEntity(x, y, z); + + if (te instanceof IGregTechTileEntity igte) { + if (igte instanceof BaseMetaTileEntity bmte) { + bmte.setCableUpdateDelay(100); + } + + IMetaTileEntity imte = igte.getMetaTileEntity(); + + if (imte instanceof MTEPipeLaser laserPipe) { + laserPipe.updateNeighboringNetworks(); + } + + if (imte instanceof MTEPipeData dataPipe) { + dataPipe.updateNeighboringNetworks(); + } + } + + if (te instanceof IIC2Enet enet) { + enet.doEnetUpdate(); + } + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlock.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlock.java new file mode 100644 index 00000000..24a785d2 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlock.java @@ -0,0 +1,11 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; + +import com.github.bsideup.jabel.Desugar; + +@Desugar +public record StandardBlock(Block block, int meta, NBTTagCompound tileData) { + +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlockMover.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlockMover.java new file mode 100644 index 00000000..0839cce0 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlockMover.java @@ -0,0 +1,52 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; + +import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; + +public class StandardBlockMover implements BlockMover { + + public static final StandardBlockMover INSTANCE = new StandardBlockMover(); + + @Override + public boolean canMove(World world, int x, int y, int z) { + return true; + } + + @Override + public StandardBlock remove(PendingMove pendingMove, World world, int x, int y, int z) { + Block block = world.getBlock(x, y, z); + int meta = world.getBlockMetadata(x, y, z); + NBTTagCompound tag = null; + + TileEntity te = world.getTileEntity(x, y, z); + + if (te != null) { + tag = new NBTTagCompound(); + te.writeToNBT(tag); + } + + world.removeTileEntity(x, y, z); + world.setBlockToAir(x, y, z); + + return new StandardBlock(block, meta, tag); + } + + @Override + public void place(PendingMove pendingMove, World world, int x, int y, int z, StandardBlock standardBlock) { + world.setBlock(x, y, z, standardBlock.block(), standardBlock.meta(), 3); + + if (standardBlock.tileData() != null) { + TileEntity te = TileEntity.createAndLoadEntity(standardBlock.tileData()); + + te.xCoord = x; + te.yCoord = y; + te.zCoord = z; + + world.setTileEntity(x, y, z, te); + } + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/WirelessBlockMover.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/WirelessBlockMover.java new file mode 100644 index 00000000..6fe60d61 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/WirelessBlockMover.java @@ -0,0 +1,57 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; + +import net.minecraftforge.oredict.OreDictionary; + +import com.recursive_pineapple.matter_manipulator.common.building.InteropConstants; +import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; + +public class WirelessBlockMover extends StandardBlockMover { + + public static final WirelessBlockMover INSTANCE = new WirelessBlockMover(); + + @Override + public boolean canMove(World world, int x, int y, int z) { + return InteropConstants.isWirelessConnector(world.getBlock(x, y, z), OreDictionary.WILDCARD_VALUE); + } + + @Override + public void place(PendingMove pendingMove, World world, int x, int y, int z, StandardBlock standardBlock) { + if (pendingMove != null) { + adjustWirelessLinkNbt(standardBlock, pendingMove); + } + + super.place(pendingMove, world, x, y, z, standardBlock); + } + + private void adjustWirelessLinkNbt(StandardBlock standardBlock, PendingMove pendingMove) { + NBTTagCompound tileData = standardBlock.tileData(); + if (tileData == null || !tileData.hasKey("connectedTargets")) return; + + NBTTagCompound connectedTargets = tileData.getCompoundTag("connectedTargets"); + + int i = 0; + while (connectedTargets.hasKey("pos#" + i)) { + NBTTagCompound coord = connectedTargets.getCompoundTag("pos#" + i); + int lx = coord.getInteger("x"); + int ly = coord.getInteger("y"); + int lz = coord.getInteger("z"); + + if ( + lx >= pendingMove.getSrcMinX() && lx <= pendingMove.getSrcMaxX() && + ly >= pendingMove.getSrcMinY() && + ly <= pendingMove.getSrcMaxY() && + lz >= pendingMove.getSrcMinZ() && + lz <= pendingMove.getSrcMaxZ() + ) { + coord.setInteger("x", lx + pendingMove.getMoveOffsetX()); + coord.setInteger("y", ly + pendingMove.getMoveOffsetY()); + coord.setInteger("z", lz + pendingMove.getMoveOffsetZ()); + } + + i++; + } + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/providers/ComputerComponentItemProvider.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/providers/ComputerComponentItemProvider.java new file mode 100644 index 00000000..7d37232c --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/providers/ComputerComponentItemProvider.java @@ -0,0 +1,136 @@ +package com.recursive_pineapple.matter_manipulator.common.building.providers; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import com.recursive_pineapple.matter_manipulator.common.building.IPseudoInventory; +import com.recursive_pineapple.matter_manipulator.common.utils.BigItemStack; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import it.unimi.dsi.fastutil.booleans.BooleanObjectImmutablePair; +import li.cil.oc.api.API; + +public class ComputerComponentItemProvider implements IItemProvider { + + /// Components where any matching tier/type is fine, even with an assigned address. + private static final HashSet FUZZY_COMPONENT_DAMAGE = Arrays.stream(new String[] { + // spotless:off + "cpu1", + "cpu2", + "cpu3", + "dataCard1", + "dataCard2", + "dataCard3", + "internetCard", + "lanCard", + "wlanCard1", + "wlanCard2", + "linkedCard", + "redstoneCard1", + "redstoneCard2", + "tpsCard", + "debugCard", + "graphicsCard1", + "graphicsCard2", + "graphicsCard3", + "ram1", + "ram2", + "ram3", + "ram4", + "ram5", + "ram6" + // spotless:on + }) + .map(name -> API.items.get(name).createItemStack(1).getItemDamage()) + .collect(Collectors.toCollection(HashSet::new)); + + private static final ItemStack EEPROM = API.items.get("eeprom").createItemStack(1); + private static final ItemStack HDD_1 = API.items.get("hdd1").createItemStack(1); + private static final ItemStack HDD_2 = API.items.get("hdd2").createItemStack(1); + private static final ItemStack HDD_3 = API.items.get("hdd3").createItemStack(1); + private static final ItemStack FLOPPY = API.items.get("floppy").createItemStack(1); + + private final @NotNull ItemStack component; + + public ComputerComponentItemProvider(@NotNull ItemStack component) { + this.component = component; + } + + public static ComputerComponentItemProvider fromStack(ItemStack stack) { + if (stack == null) return null; + if (API.items.get(stack) == null) return null; + return new ComputerComponentItemProvider(withoutAddress(stack)); + } + + @Override + public @Nullable ItemStack getStack(IPseudoInventory inv, boolean consume) { + if (!consume) return component; + + // Addressed components are consumed fuzzily, then recreated without copying their NBT. + if (FUZZY_COMPONENT_DAMAGE.contains(component.getItemDamage())) { + BooleanObjectImmutablePair> result = inv + .tryConsumeItems(Collections.singletonList(BigItemStack.create(component)), IPseudoInventory.CONSUME_FUZZY); + return result.leftBoolean() ? API.items.get(component).createItemStack(1) : null; + } + + if (component.getItemDamage() == EEPROM.getItemDamage()) { + // Prefer an exact matching EEPROM; otherwise program an empty one. + BooleanObjectImmutablePair> result = inv + .tryConsumeItems(Collections.singletonList(BigItemStack.create(component)), IPseudoInventory.CONSUME_REAL_ONLY); + return result.leftBoolean() || inv.tryConsumeItems(EEPROM) ? component.copy() : null; + } + + // HDDs and floppies are consumed empty; their data is not copied. + if (component.getItemDamage() == HDD_1.getItemDamage()) return inv.tryConsumeItems(HDD_1) ? HDD_1.copy() : null; + if (component.getItemDamage() == HDD_2.getItemDamage()) return inv.tryConsumeItems(HDD_2) ? HDD_2.copy() : null; + if (component.getItemDamage() == HDD_3.getItemDamage()) return inv.tryConsumeItems(HDD_3) ? HDD_3.copy() : null; + if (component.getItemDamage() == FLOPPY.getItemDamage()) return inv.tryConsumeItems(FLOPPY) ? FLOPPY.copy() : null; + + return null; + } + + /// Strips the address of EEPROMs. Might work for other components. + private static @NotNull ItemStack withoutAddress(@NotNull ItemStack source) { + ItemStack stripped = source.copy(); + + if (stripped.getItemDamage() != EEPROM.getItemDamage()) { + stripped.setTagCompound(null); + return stripped; + } + + if (!stripped.hasTagCompound()) return stripped; + NBTTagCompound tag = stripped.getTagCompound(); + + if (!tag.hasKey("oc:data")) return stripped; + NBTTagCompound data = tag.getCompoundTag("oc:data"); + + if (!data.hasKey("node")) return stripped; + data.getCompoundTag("node").removeTag("address");; + + return stripped; + } + + @Override + public IItemProvider clone() { + return new ComputerComponentItemProvider(component); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ComputerComponentItemProvider provider)) return false; + + // Most OC components are equal by descriptor; assigned addresses do not matter. + if (component.getItemDamage() != EEPROM.getItemDamage()) return API.items.get(component).equals(API.items.get(provider.component)); + + // EEPROMs must match exactly, except for their assigned OC address. + return ItemStack.areItemStacksEqual(component, provider.component); + } +} diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/BlockPropertyRegistry.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/BlockPropertyRegistry.java index ff8b065a..e7fec620 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/BlockPropertyRegistry.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/BlockPropertyRegistry.java @@ -35,11 +35,14 @@ import net.minecraft.block.BlockTorch; import net.minecraft.block.BlockTrapDoor; import net.minecraft.init.Blocks; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntitySign; import net.minecraft.tileentity.TileEntitySkull; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.IChatComponent; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; @@ -50,14 +53,16 @@ import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.common.blocks.ItemMachines; -import gregtech.common.tileentities.machines.MTEHatchOutputBusME; -import gregtech.common.tileentities.machines.MTEHatchOutputME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputBusME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputME; +import appeng.tile.networking.TileWirelessBase; import appeng.util.ReadableNumberConverter; import com.google.gson.JsonElement; import com.google.gson.JsonNull; import com.google.gson.JsonPrimitive; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; import com.recursive_pineapple.matter_manipulator.asm.Optional; import com.recursive_pineapple.matter_manipulator.common.building.InteropConstants; import com.recursive_pineapple.matter_manipulator.common.compat.BooleanProperty.FlagBooleanProperty; @@ -67,8 +72,7 @@ import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; import com.rwtema.extrautils.block.BlockSpike; -import net.bdew.ae2stuff.machines.wireless.TileWireless; - +import bartworks.API.enums.CircuitImprint; import bartworks.common.tileentities.multis.MTECircuitAssemblyLine; import codechicken.enderstorage.common.TileFrequencyOwner; import codechicken.enderstorage.storage.item.TileEnderChest; @@ -81,6 +85,7 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap; +import li.cil.oc.common.tileentity.traits.Rotatable; import lombok.SneakyThrows; public class BlockPropertyRegistry { @@ -261,9 +266,10 @@ public static void init() { if (Mods.ArchitectureCraft.isModLoaded()) initArch(); if (Mods.FloodLights.isModLoaded()) initFloodLights(); if (Mods.GregTech.isModLoaded()) initGT5u(); - if (Mods.AE2Stuff.isModLoaded()) initAE2Stuff(); + if (Mods.AppliedEnergistics2.isModLoaded()) initAE2Wireless(); if (Mods.EnderStorage.isModLoaded()) initEnderStorage(); if (Mods.ExtraUtilities.isModLoaded()) initEXU(); + if (Mods.OpenComputers.isModLoaded()) initOpenComputers(); } // #region Vanilla @@ -1072,11 +1078,12 @@ private static void initGT5u() { // #endregion - // #region AE2 Stuff + // #region AE2 Wireless Connector - @Optional(Names.AE2STUFF) - private static void initAE2Stuff() { + @Optional(Names.APPLIED_ENERGISTICS2) + private static void initAE2Wireless() { registerIntrinsicProperty(InteropConstants.WIRELESS_CONNECTOR.get().getBlock(), new WirelessHubProperty()); + registerIntrinsicProperty(InteropConstants.WIRELESS_HUB.get().getBlock(), new WirelessHubProperty()); } // #endregion @@ -1168,6 +1175,118 @@ public void setValue(World world, int x, int y, int z, ForgeDirection forgeDirec // #endregion + // #region OpenComputers + + @Optional(Names.OPEN_COMPUTERS) + private static void initOpenComputers() { + registerTileEntityInterfaceProperty( + Rotatable.class, + new OrientationBlockProperty() { + + @Override + public String getName() { + return "orientation"; + } + + @Override + public Orientation getValue(World world, int x, int y, int z) { + if (!(world.getTileEntity(x, y, z) instanceof Rotatable rotatable)) return Orientation.NONE; + + return switch (rotatable.yaw()) { + case NORTH -> switch (rotatable.pitch()) { + case UP -> Orientation.UP_SOUTH; + case DOWN -> Orientation.DOWN_NORTH; + case NORTH -> Orientation.NORTH_UP; + default -> Orientation.NONE; + }; + case SOUTH -> switch (rotatable.pitch()) { + case UP -> Orientation.UP_NORTH; + case DOWN -> Orientation.DOWN_SOUTH; + case NORTH -> Orientation.SOUTH_UP; + default -> Orientation.NONE; + }; + case WEST -> switch (rotatable.pitch()) { + case UP -> Orientation.UP_EAST; + case DOWN -> Orientation.DOWN_WEST; + case NORTH -> Orientation.WEST_UP; + default -> Orientation.NONE; + }; + case EAST -> switch (rotatable.pitch()) { + case UP -> Orientation.UP_WEST; + case DOWN -> Orientation.DOWN_EAST; + case NORTH -> Orientation.EAST_UP; + default -> Orientation.NONE; + }; + default -> Orientation.NONE; + }; + } + + @Override + public void setValue(World world, int x, int y, int z, Orientation orientation) { + if (!(world.getTileEntity(x, y, z) instanceof Rotatable rotatable)) return; + + switch (orientation) { + // NORTH yaw + case UP_SOUTH -> { + rotatable.yaw_$eq(ForgeDirection.NORTH); + rotatable.pitch_$eq(ForgeDirection.UP); + } + case DOWN_NORTH -> { + rotatable.yaw_$eq(ForgeDirection.NORTH); + rotatable.pitch_$eq(ForgeDirection.DOWN); + } + case NORTH_UP -> { + rotatable.yaw_$eq(ForgeDirection.NORTH); + rotatable.pitch_$eq(ForgeDirection.NORTH); + } + // SOUTH yaw + case UP_NORTH -> { + rotatable.yaw_$eq(ForgeDirection.SOUTH); + rotatable.pitch_$eq(ForgeDirection.UP); + } + case DOWN_SOUTH -> { + rotatable.yaw_$eq(ForgeDirection.SOUTH); + rotatable.pitch_$eq(ForgeDirection.DOWN); + } + case SOUTH_UP -> { + rotatable.yaw_$eq(ForgeDirection.SOUTH); + rotatable.pitch_$eq(ForgeDirection.NORTH); + } + // WEST yaw + case UP_EAST -> { + rotatable.yaw_$eq(ForgeDirection.WEST); + rotatable.pitch_$eq(ForgeDirection.UP); + } + case DOWN_WEST -> { + rotatable.yaw_$eq(ForgeDirection.WEST); + rotatable.pitch_$eq(ForgeDirection.DOWN); + } + case WEST_UP -> { + rotatable.yaw_$eq(ForgeDirection.WEST); + rotatable.pitch_$eq(ForgeDirection.NORTH); + } + // EAST yaw + case UP_WEST -> { + rotatable.yaw_$eq(ForgeDirection.EAST); + rotatable.pitch_$eq(ForgeDirection.UP); + } + case DOWN_EAST -> { + rotatable.yaw_$eq(ForgeDirection.EAST); + rotatable.pitch_$eq(ForgeDirection.DOWN); + } + case EAST_UP -> { + rotatable.yaw_$eq(ForgeDirection.EAST); + rotatable.pitch_$eq(ForgeDirection.NORTH); + } + default -> {} + } + } + } + ); + } + + // #endregion + @SneakyThrows public static DirectionBlockProperty methodIntDirectionTile(Class clazz, String getterName, String setterName) { Method getter = clazz.getDeclaredMethod(getterName); @@ -1356,9 +1475,9 @@ public void setValue(NBTTagCompound itemTag, JsonElement value) { } @Override - public void getItemDetails(List details, JsonElement value) { + public void getItemDetailsChat(List details, JsonElement value) { ReadableNumberConverter nc = ReadableNumberConverter.INSTANCE; - details.add(String.format("Cache Capacity: %s", nc.toWideReadableForm(value.getAsLong()))); + details.add(new ChatComponentTranslation("mm.chat.item.details.cache_capacity", nc.toWideReadableForm(value.getAsLong()))); } } @@ -1371,29 +1490,30 @@ public String getName() { @Override public boolean hasValue(ItemStack stack) { - return InteropConstants.WIRELESS_CONNECTOR.matches(MMUtils.getBlockFromItem(stack.getItem(), stack.itemDamage), WILDCARD_VALUE); + return InteropConstants.isWirelessConnector(MMUtils.getBlockFromItem(stack.getItem(), stack.itemDamage), WILDCARD_VALUE); } @Override public boolean hasValue(IBlockAccess world, int x, int y, int z) { - return world.getTileEntity(x, y, z) instanceof TileWireless; + return world.getTileEntity(x, y, z) instanceof TileWirelessBase; } @Override public JsonElement getValue(ItemStack stack) { - return new JsonPrimitive(stack.itemDamage >= 17); + return new JsonPrimitive(InteropConstants.WIRELESS_HUB.matches(MMUtils.getBlockFromItem(stack.getItem(), stack.itemDamage), WILDCARD_VALUE)); } @Override public JsonElement getValue(IBlockAccess world, int x, int y, int z) { - TileWireless te = (TileWireless) world.getTileEntity(x, y, z); + TileWirelessBase te = (TileWirelessBase) world.getTileEntity(x, y, z); return new JsonPrimitive(te.isHub()); } @Override public void setValue(ItemStack stack, JsonElement value) { - stack.itemDamage = (stack.itemDamage % 17) + (value.getAsBoolean() ? 17 : 0); + Block block = value.getAsBoolean() ? InteropConstants.WIRELESS_HUB.get().getBlock() : InteropConstants.WIRELESS_CONNECTOR.get().getBlock(); + stack.func_150996_a(Item.getItemFromBlock(block)); } @Override @@ -1402,22 +1522,15 @@ public void setValue(IBlockAccess world, int x, int y, int z, JsonElement value) } @Override - public void getItemDetails(List details, JsonElement value) { + public void getItemDetailsChat(List details, JsonElement value) { if (value.getAsBoolean()) { - details.add("Hub"); + details.add(new ChatComponentTranslation("mm.chat.item.details.hub")); } } } private static class CALImprintProperty extends IntrinsicMTEProperty { - private static final MethodHandle GET_CAL_TYPE = MMUtils.exposeFieldGetter(MTECircuitAssemblyLine.class, "type"); - - @SneakyThrows - public static NBTTagCompound getCALType(MTECircuitAssemblyLine cal) { - return (NBTTagCompound) GET_CAL_TYPE.invokeExact(cal); - } - public CALImprintProperty() { super(MTECircuitAssemblyLine.class); } @@ -1429,11 +1542,11 @@ public String getName() { @Override public JsonElement getValue(MTECircuitAssemblyLine mte) { - NBTTagCompound imprint = getCALType(mte); + CircuitImprint imprint = mte.getCircuitImprint(); if (imprint == null) return null; - return MMUtils.toJsonObjectExact(imprint); + return new JsonPrimitive(imprint.id); } @Override @@ -1443,29 +1556,36 @@ public void setValue(MTECircuitAssemblyLine mte, JsonElement value) { @Override public JsonElement getValue(NBTTagCompound itemTag) { - if (itemTag == null || !itemTag.hasKey(MTECircuitAssemblyLine.IMPRINT_KEY)) return null; + if (itemTag == null || !itemTag.hasKey(MTECircuitAssemblyLine.IMPRINT_ID_KEY)) return null; - return MMUtils.toJsonObjectExact(itemTag.getCompoundTag(MTECircuitAssemblyLine.IMPRINT_KEY)); + return new JsonPrimitive(itemTag.getInteger(MTECircuitAssemblyLine.IMPRINT_ID_KEY)); } @Override public void setValue(NBTTagCompound itemTag, JsonElement value) { if (value == null) { - itemTag.removeTag(MTECircuitAssemblyLine.IMPRINT_KEY); - } else { - itemTag.setTag(MTECircuitAssemblyLine.IMPRINT_KEY, MMUtils.toNbtExact(value)); + itemTag.removeTag(MTECircuitAssemblyLine.IMPRINT_ID_KEY); + } else if (value instanceof JsonPrimitive circuit) { + itemTag.setInteger(MTECircuitAssemblyLine.IMPRINT_ID_KEY, circuit.getAsInt()); } } @Override - public void getItemDetails(List details, JsonElement value) { - ItemStack stack = null; + public void getItemDetailsChat(List details, JsonElement value) { + int circuitID = 0; + CircuitImprint imprint = null; - if (value != null) { - stack = ItemStack.loadItemStackFromNBT((NBTTagCompound) MMUtils.toNbtExact(value)); + if (value != null && value instanceof JsonPrimitive primitive) { + circuitID = primitive.getAsInt(); + imprint = CircuitImprint.IMPRINT_LOOKUPS_BY_IDS.get(circuitID); } - details.add(String.format("Imprint: %s", stack == null ? "None" : stack.getDisplayName())); + details.add( + new ChatComponentTranslation( + "mm.chat.item.details.imprint", + imprint == null ? "mm.chat.item.details.imprint" : new ChatComponentItemName(imprint.imprint.get(1)) + ) + ); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/IntrinsicProperty.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/IntrinsicProperty.java index 4f40bcc8..d3002fb6 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/IntrinsicProperty.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/IntrinsicProperty.java @@ -3,6 +3,7 @@ import java.util.List; import net.minecraft.item.ItemStack; +import net.minecraft.util.IChatComponent; import net.minecraft.world.IBlockAccess; import com.google.gson.JsonElement; @@ -24,5 +25,5 @@ public interface IntrinsicProperty { void setValue(IBlockAccess world, int x, int y, int z, JsonElement value); - default void getItemDetails(List details, JsonElement value) {} + default void getItemDetailsChat(List details, JsonElement value) {} } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/data/WeightedSpecList.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/data/WeightedSpecList.java index bf9864c4..1df2d3c4 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/data/WeightedSpecList.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/data/WeightedSpecList.java @@ -65,7 +65,7 @@ public String toString() { for (var p : specs) { if (sb.length() > 0) sb.append(", "); - + // TODO: should we localize it? sb.append(p.left().getDisplayName()); if (p.rightInt() > 1) sb.append(" x ").append(p.rightInt()); diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/FixedLengthVertexBuffer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/FixedLengthVertexBuffer.java index 86407b66..c2eb019c 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/FixedLengthVertexBuffer.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/FixedLengthVertexBuffer.java @@ -19,7 +19,7 @@ public void reallocate() { generate(); // noinspection MagicConstant - setSize(this.length, this.bufferFlags); + setSize(this.allocatedBytes, this.bufferFlags); } @Override @@ -55,11 +55,11 @@ public void setSize( ARBBufferStorage.GL_CLIENT_STORAGE_BIT, }) int bufferFlags ) { - if (this.length > 0) throw new IllegalStateException("Cannot resize an immutable (fixed length) vertex buffer"); + if (this.allocatedBytes > 0) throw new IllegalStateException("Cannot resize an immutable (fixed length) vertex buffer"); bind(); - this.length = length; + this.allocatedBytes = length; this.bufferFlags = bufferFlags; ARBBufferStorage.glBufferStorage(GL15.GL_ARRAY_BUFFER, length, bufferFlags); @@ -74,7 +74,7 @@ public void flush() { public void flushAll() { bind(); - GL30.glFlushMappedBufferRange(GL15.GL_ARRAY_BUFFER, 0, length); + GL30.glFlushMappedBufferRange(GL15.GL_ARRAY_BUFFER, 0, allocatedBytes); unbind(); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java index bbbcd997..f96499bc 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java @@ -1,9 +1,9 @@ package com.recursive_pineapple.matter_manipulator.common.items.manipulator; +import static com.gtnewhorizon.gtnhlib.util.numberformatting.NumberFormatUtil.formatNumber; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.BLUE; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.GREEN; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.RED; -import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.formatNumbers; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.sendErrorToPlayer; import static com.recursive_pineapple.matter_manipulator.common.utils.MMUtils.sendInfoToPlayer; import static com.recursive_pineapple.matter_manipulator.common.utils.MMValues.V; @@ -35,6 +35,7 @@ import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; @@ -154,6 +155,7 @@ public ItemMatterManipulator(ManipulatorTier tier) { public static final int ALLOW_EXCHANGING = 0b1 << counter++; public static final int ALLOW_MOVING = 0b1 << counter++; public static final int ALLOW_CABLES = 0b1 << counter++; + public static final int ALLOW_SMART_COPY = 0b1 << counter++; public static final int ALL_MODES = ALLOW_GEOMETRY | ALLOW_COPYING | ALLOW_EXCHANGING | ALLOW_MOVING | ALLOW_CABLES; @@ -192,7 +194,7 @@ public enum ManipulatorTier { GlobalMMConfig.BuildingConfig.mk3BlocksPerPlace, 5, 7, 10_000_000_000L, - ALLOW_GEOMETRY | CONNECTS_TO_AE | ALLOW_REMOVING | ALLOW_EXCHANGING | ALLOW_CONFIGURING | ALLOW_CABLES | ALLOW_COPYING | ALLOW_MOVING | CONNECTS_TO_UPLINK, + ALLOW_GEOMETRY | CONNECTS_TO_AE | ALLOW_REMOVING | ALLOW_EXCHANGING | ALLOW_CONFIGURING | ALLOW_CABLES | ALLOW_COPYING | ALLOW_MOVING | CONNECTS_TO_UPLINK | ALLOW_SMART_COPY, ImmutableList.of(MMUpgrades.PowerEff, MMUpgrades.PowerP2P), MMItemList.MK3 ); @@ -407,7 +409,34 @@ public static MMState getState(ItemStack itemStack) { } public static void setState(ItemStack itemStack, MMState state) { - itemStack.setTagCompound(state.save()); + + // Reads the NBT Tags of already existing Manipulator + NBTTagCompound existingTags; + existingTags = getOrCreateNbtData(itemStack); + // Gets tags for the state change + NBTTagCompound newTags; + newTags = state.save(); + + // Manually set each tag + existingTags.setInteger("jv", newTags.getInteger("jv")); + existingTags.setInteger("dv", newTags.getInteger("dv")); + existingTags.setTag("config", newTags.getTag("config")); + existingTags.setTag("installedUpgrades", newTags.getTag("installedUpgrades")); + existingTags.setDouble("charge", newTags.getDouble("charge")); + + // needs to get rid of null if it exists otherwise gets omitted, GSON behaviour + if (newTags.hasKey("encKey")) { + existingTags.setTag("encKey", newTags.getTag("encKey")); + } else { + existingTags.removeTag("encKey"); + } + + if (newTags.hasKey("uplinkAddress")) { + existingTags.setTag("uplinkAddress", newTags.getTag("uplinkAddress")); + } else { + existingTags.removeTag("uplinkAddress"); + } + } public static NBTTagCompound getOrCreateNbtData(ItemStack itemStack) { @@ -441,7 +470,7 @@ public void addInformation( // spotless:off if (!GuiScreen.isShiftKeyDown()) { - desc.add("Hold shift for more information."); + desc.add(StatCollector.translateToLocal("mm.tooltip.hold_shift")); } else { if (state.hasCap(CONNECTS_TO_AE) || state.hasCap(CONNECTS_TO_UPLINK)) { long time = System.currentTimeMillis(); @@ -468,113 +497,124 @@ public void addInformation( if (state.hasCap(CONNECTS_TO_AE)) { if (state.encKey != null) { if (ttAEWorks) { - desc.add("Has an ME connection. (Can interact currently)"); + desc.add(StatCollector.translateToLocal("mm.tooltip.me_conn.can_interact")); } else { - desc.add("Has an ME connection. (Cannot interact currently)"); + desc.add(StatCollector.translateToLocal("mm.tooltip.me_conn.cannot_interact")); } } else { - desc.add("Does not have an ME connection."); + desc.add(StatCollector.translateToLocal("mm.tooltip.me_conn.no_conn")); } } if (state.hasCap(CONNECTS_TO_UPLINK)) { if (state.uplinkAddress != null) { if (ttUplinkWorks) { - desc.add("Has an Uplink connection. (Can interact currently)"); + desc.add(StatCollector.translateToLocal("mm.tooltip.uplink_conn.can_interact")); } else { - desc.add("Has an Uplink connection. (Cannot interact currently)"); + desc.add(StatCollector.translateToLocal("mm.tooltip.uplink_conn.cannot_interact")); } - addInfoLine(desc, "Uplink address: %s", state.uplinkAddress, Long::toHexString); + addInfoLine(desc, "mm.tooltip.uplink_conn.address", state.uplinkAddress, Long::toHexString); } else { - desc.add("Does not have an Uplink connection."); + desc.add(StatCollector.translateToLocal("mm.tooltip.uplink_conn.no_conn")); } } if (state.config.action != null) { - addInfoLine(desc, "Pending Action: %s", switch (state.config.action) { - case MOVING_COORDS -> "Moving coordinates"; - case GEOM_SELECTING_BLOCK -> "Selecting blocks to place"; - case MARK_COPY_A -> "Marking first copy corner"; - case MARK_COPY_B -> "Marking second copy corner"; - case MARK_CUT_A -> "Marking first cut corner"; - case MARK_CUT_B -> "Marking second cut corner"; - case MARK_PASTE -> "Marking paste location"; - case EXCH_ADD_REPLACE -> "Adding block to replace whitelist"; - case EXCH_SET_REPLACE -> "Setting block in replace whitelist"; - case EXCH_SET_TARGET -> "Setting block to replace with"; - case PICK_CABLE -> "Picking cable"; - case MARK_ARRAY -> "Marking array bounds"; - }); + addInfoLine(desc, "mm.tooltip.pending_action", StatCollector.translateToLocal(switch (state.config.action) { + case MOVING_COORDS -> "mm.tooltip.pending_action.moving_coords"; + case GEOM_SELECTING_BLOCK -> "mm.tooltip.pending_action.geom_selecting_block"; + case MARK_COPY_A -> "mm.tooltip.pending_action.mark_copy_a"; + case MARK_COPY_B -> "mm.tooltip.pending_action.mark_copy_b"; + case MARK_CUT_A -> "mm.tooltip.pending_action.mark_cut_a"; + case MARK_CUT_B -> "mm.tooltip.pending_action.mark_cut_b"; + case MARK_PASTE -> "mm.tooltip.pending_action.mark_paste"; + case EXCH_ADD_REPLACE -> "mm.tooltip.pending_action.exch_add_replace"; + case EXCH_SET_REPLACE -> "mm.tooltip.pending_action.exch_set_replace"; + case EXCH_SET_TARGET -> "mm.tooltip.pending_action.exch_set_target"; + case PICK_CABLE -> "mm.tooltip.pending_action.pick_cable"; + case MARK_ARRAY -> "mm.tooltip.pending_action.mark_array"; + })); } if (Integer.bitCount(tier.capabilities & ALL_MODES) > 1) { - addInfoLine(desc, "Mode: %s", switch (state.config.placeMode) { - case GEOMETRY -> "Geometry"; - case MOVING -> "Moving"; - case COPYING -> "Copying"; - case EXCHANGING -> "Exchanging"; - case CABLES -> "Cables"; - }); + addInfoLine(desc, "mm.tooltip.mode", StatCollector.translateToLocal(switch (state.config.placeMode) { + case GEOMETRY -> "mm.tooltip.mode.geometry"; + case MOVING -> "mm.tooltip.mode.moving"; + case COPYING -> "mm.tooltip.mode.copying"; + case EXCHANGING -> "mm.tooltip.mode.exchanging"; + case CABLES -> "mm.tooltip.mode.cables"; + })); } if (state.hasCap(ALLOW_REMOVING)) { - addInfoLine(desc, "Removing: %s", switch (state.config.removeMode) { - case ALL -> "All blocks"; - case REPLACEABLE -> "Replaceable blocks"; - case NONE -> "No blocks"; - }); + addInfoLine(desc, "mm.tooltip.removing", StatCollector.translateToLocal(switch (state.config.removeMode) { + case ALL -> "mm.tooltip.removing.all"; + case REPLACEABLE -> "mm.tooltip.removing.replaceable"; + case NONE -> "mm.tooltip.removing.none"; + })); } if (state.config.placeMode == PlaceMode.GEOMETRY) { - addInfoLine(desc, "Shape: %s", switch (state.config.shape) { - case LINE -> "Line"; - case CUBE -> "Cube"; - case SPHERE -> "Sphere"; - case CYLINDER -> "Cylinder"; - }); - - addInfoLine(desc, "Coordinate A: %s", state.config.coordA); - addInfoLine(desc, "Coordinate B: %s", state.config.coordB); - - addInfoLine(desc, "Corner block: %s", state.config.corners); - addInfoLine(desc, "Edge block: %s", state.config.edges); - addInfoLine(desc, "Face block: %s", state.config.faces); - addInfoLine(desc, "Volume block: %s", state.config.volumes); + addInfoLine(desc, "mm.tooltip.shape", StatCollector.translateToLocal(switch (state.config.shape) { + case LINE -> "mm.tooltip.shape.line"; + case CUBE -> "mm.tooltip.shape.cube"; + case SPHERE -> "mm.tooltip.shape.sphere"; + case CYLINDER -> "mm.tooltip.shape.cylinder"; + })); + + addInfoLine(desc, "mm.tooltip.coord_a", state.config.coordA); + addInfoLine(desc, "mm.tooltip.coord_b", state.config.coordB); + + addInfoLine(desc, "mm.tooltip.corner_block", state.config.corners); + addInfoLine(desc, "mm.tooltip.edge_block", state.config.edges); + addInfoLine(desc, "mm.tooltip.face_block", state.config.faces); + addInfoLine(desc, "mm.tooltip.volume_block", state.config.volumes); } if (state.config.placeMode == PlaceMode.COPYING) { - addInfoLine(desc, "Copy Coordinate A: %s", state.config.coordA); - addInfoLine(desc, "Copy Coordinate B: %s", state.config.coordB); + addInfoLine(desc, "mm.tooltip.copying.copy_a", state.config.coordA); + addInfoLine(desc, "mm.tooltip.copying.copy_b", state.config.coordB); - addInfoLine(desc, "Paste Coordinate: %s", state.config.coordC); + addInfoLine(desc, "mm.tooltip.paste", state.config.coordC); addInfoLine(desc, - "Stack: %s", + "mm.tooltip.copying.stack", state.config.arraySpan, span -> String.format( "X: %dx, Y: %dx, Z: %dx", span.x + (span.x < 0 ? -1 : 1), span.y + (span.y < 0 ? -1 : 1), span.z + (span.z < 0 ? -1 : 1))); + + addInfoLine(desc, "mm.tooltip.copying.wireless_link", state.config.linkExternalHubs, + on -> StatCollector.translateToLocal(on ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off")); + + if (state.hasCap(ALLOW_SMART_COPY)) { + addInfoLine(desc, "mm.tooltip.copying.auto_proxy_cribs", state.config.replaceCribsWithProxies, + on -> StatCollector.translateToLocal(on ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off")); + } + + addInfoLine(desc, "mm.tooltip.copying.auto_p2p_interfaces", state.config.replaceInterfacesWithP2P, + on -> StatCollector.translateToLocal(on ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off")); } if (state.config.placeMode == PlaceMode.MOVING) { - addInfoLine(desc, "Cut Coordinate A: %s", state.config.coordA); - addInfoLine(desc, "Cut Coordinate B: %s", state.config.coordB); + addInfoLine(desc, "mm.tooltip.moving.cut_a", state.config.coordA); + addInfoLine(desc, "mm.tooltip.moving.cut_b", state.config.coordB); - addInfoLine(desc, "Paste Coordinate: %s", state.config.coordC); + addInfoLine(desc, "mm.tooltip.paste", state.config.coordC); } if (state.config.placeMode == PlaceMode.EXCHANGING) { - addInfoLine(desc, "Removable blocks: %s", state.config.replaceWhitelist); - addInfoLine(desc, "Replacing blocks with: %s", state.config.replaceWith); + addInfoLine(desc, "mm.tooltip.exchanging.removable", state.config.replaceWhitelist); + addInfoLine(desc, "mm.tooltip.exchanging.replacing", state.config.replaceWith); } if (state.config.placeMode == PlaceMode.CABLES) { - addInfoLine(desc, "Coordinate A: %s", state.config.coordA); - addInfoLine(desc, "Coordinate B: %s", state.config.coordB); + addInfoLine(desc, "mm.tooltip.coord_a", state.config.coordA); + addInfoLine(desc, "mm.tooltip.coord_b", state.config.coordB); - addInfoLine(desc, "Cable: %s", state.config.cables); + addInfoLine(desc, "mm.tooltip.cable", state.config.cables, BlockSpec::toDisplayString); } List upgrades = new ArrayList<>(state.getInstalledUpgrades()); @@ -593,30 +633,32 @@ public void addInformation( EnumChatFormatting.AQUA + I18n.format( "mm.tooltip.voltage", - formatNumbers(MMUtils.clamp(Math.round(state.charge), 0, tier.maxCharge)), - formatNumbers(tier.maxCharge), - formatNumbers(V[tier.voltageTier])) + formatNumber(MMUtils.clamp(Math.round(state.charge), 0, tier.maxCharge)), + formatNumber(tier.maxCharge), + formatNumber(V[tier.voltageTier])) + EnumChatFormatting.GRAY); // spotless:on } - private void addInfoLine(List desc, String format, T value) { - addInfoLine(desc, format, value, T::toString); + private void addInfoLine(List desc, String formatKey, T value) { + addInfoLine(desc, formatKey, value, T::toString); } - private void addInfoLine(List desc, String format, T value, Function toString) { + private void addInfoLine(List desc, String formatKey, T value, Function toString) { if (value != null) { desc.add( - String.format( - format, - EnumChatFormatting.BLUE.toString() + toString.apply(value) + EnumChatFormatting.RESET.toString() + StatCollector.translateToLocalFormatted( + formatKey, + EnumChatFormatting.BLUE + toString.apply(value) + EnumChatFormatting.RESET ) ); } else { desc.add( - String - .format(format, EnumChatFormatting.GRAY.toString() + "None" + EnumChatFormatting.RESET.toString()) + StatCollector.translateToLocalFormatted( + formatKey, + EnumChatFormatting.GRAY + StatCollector.translateToLocal("mm.tooltip.none") + EnumChatFormatting.RESET + ) ); } } @@ -796,7 +838,7 @@ private void onPickBlock( BlockSpec block = BlockSpec.fromPickBlock(world, player, hit); - String what = null; + String whatKey = null; boolean add = player.isSneaking(); @@ -804,25 +846,25 @@ private void onPickBlock( case CORNERS: { if (state.config.corners == null || !add) state.config.corners = new WeightedSpecList(); state.config.corners.add(block); - what = "corners"; + whatKey = "mm.enum.what.corners"; break; } case EDGES: { if (state.config.edges == null || !add) state.config.edges = new WeightedSpecList(); state.config.edges.add(block); - what = "edges"; + whatKey = "mm.enum.what.edges"; break; } case FACES: { if (state.config.faces == null || !add) state.config.faces = new WeightedSpecList(); state.config.faces.add(block); - what = "faces"; + whatKey = "mm.enum.what.faces"; break; } case VOLUMES: { if (state.config.volumes == null || !add) state.config.volumes = new WeightedSpecList(); state.config.volumes.add(block); - what = "volumes"; + whatKey = "mm.enum.what.volumes"; break; } case ALL: { @@ -834,7 +876,7 @@ private void onPickBlock( state.config.edges.add(block); state.config.faces.add(block); state.config.volumes.add(block); - what = "all blocks"; + whatKey = "mm.enum.what.all"; break; } } @@ -842,12 +884,16 @@ private void onPickBlock( if (add) { sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted("mm.info.added", block.getDisplayName(), what) + "mm.info.added", + block.getChatComponent(), + new ChatComponentTranslation(whatKey) ); } else { sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted("mm.info.set", what, block.getDisplayName()) + "mm.info.set", + new ChatComponentTranslation(whatKey), + block.getChatComponent() ); } } @@ -869,10 +915,8 @@ private void onExchangeSetTarget( sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.set_block_to_replace_with", - block.getDisplayName() - ) + "mm.info.set_block_to_replace_with", + block.getChatComponent() ); } @@ -896,10 +940,8 @@ private void onExchangeAddWhitelist( sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.added_block_to_exchange_whitelist", - block.getDisplayName() - ) + "mm.info.added_block_to_exchange_whitelist", + block.getChatComponent() ); } @@ -913,10 +955,8 @@ private void onExchangeSetWhitelist(World world, EntityPlayer player, ItemStack sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.set_exchange_whitelist_to_only_contain", - block.getDisplayName() - ) + "mm.info.set_exchange_whitelist_to_only_contain", + block.getChatComponent() ); } @@ -931,13 +971,18 @@ private void onPickCable(World world, EntityPlayer player, ItemStack stack, MMSt if (cable.isAir() && Mods.AppliedEnergistics2.isModLoaded()) { MMUtils.getAECable(cable, world, hit.blockX, hit.blockY, hit.blockZ); } + + if (cable.isAir() && Mods.OpenComputers.isModLoaded()) { + MMUtils.getOCCable(cable, world, hit.blockX, hit.blockY, hit.blockZ); + } } state.config.cables = cable.isAir() ? null : cable; sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted("mm.info.set_cable", cable.getDisplayName()) + "mm.info.set_cable", + cable.getChatComponent() ); } @@ -953,13 +998,13 @@ private void onMarkArray(World world, EntityPlayer player, ItemStack stack, MMSt Vector3i lookingAt = MMUtils.getLookingAtLocation(player); if (!Location.areCompatible(state.config.coordA, state.config.coordB)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.cannot_mark_copy")); + sendErrorToPlayer(player, "mm.info.error.cannot_mark_copy"); state.config.arraySpan = null; return; } if (state.config.coordC == null || !state.config.coordC.isInWorld(world)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.cannot_mark_paste")); + sendErrorToPlayer(player, "mm.info.error.cannot_mark_paste"); state.config.arraySpan = null; return; } @@ -1037,7 +1082,7 @@ public void onUsingTick(ItemStack stack, EntityPlayer player, int count) { MMMod.LOG.error("Could not place blocks", t); sendErrorToPlayer( player, - "Could not place blocks due to a crash. Check the logs for more info." + "mm.info.error.could_not_place_blocks" ); } } @@ -1407,6 +1452,34 @@ private void addCopyingOptions(RadialMenuBuilder builder, UIBuildContext buildCo .onClicked(() -> { Messages.MarkPaste.sendToServer(); }) + .done() + .branch() + .label(StatCollector.translateToLocal("mm.gui.advanced_options")) + .option() + .label(() -> StatCollector.translateToLocalFormatted( + "mm.gui.wireless_link_hub", + StatCollector.translateToLocal(initialState.config.linkExternalHubs ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off"))) + .onClicked(() -> { + Messages.SetLinkExternalHubs.sendToServer(); + }) + .done() + .option() + .hidden(!initialState.hasCap(ALLOW_SMART_COPY)) + .label(() -> StatCollector.translateToLocalFormatted( + "mm.gui.smart_copy.cribs_to_proxies", + StatCollector.translateToLocal(initialState.config.replaceCribsWithProxies ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off"))) + .onClicked(() -> { + Messages.SetReplaceCribs.sendToServer(); + }) + .done() + .option() + .label(() -> StatCollector.translateToLocalFormatted( + "mm.gui.smart_copy.interfaces_to_p2p", + StatCollector.translateToLocal(initialState.config.replaceInterfacesWithP2P ? "mm.gui.smart_copy.on" : "mm.gui.smart_copy.off"))) + .onClicked(() -> { + Messages.SetReplaceInterfaces.sendToServer(); + }) + .done() .done(); } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMConfig.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMConfig.java index 0433a482..f2002061 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMConfig.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMConfig.java @@ -46,6 +46,19 @@ public class MMConfig { /** The array size in repetitions */ public Vector3i arraySpan; + public boolean linkExternalHubs = false; + + /** + * When true, CRIBs (Crafting Input Buses) in the copy region are replaced with Crafting Input Proxies linked to the + * source CRIB. + */ + public boolean replaceCribsWithProxies = false; + /** + * When true, ME Interface parts with patterns in the copy region are replaced with P2P ME tunnels, with matching + * tunnels auto-placed at the source. + */ + public boolean replaceInterfacesWithP2P = false; + public Location getCoordA(World world, Vector3i lookingAt) { if (coordAOffset == null) { return coordA; @@ -262,6 +275,9 @@ public int hashCode() { result = prime * result + ((replaceWith == null) ? 0 : replaceWith.hashCode()); result = prime * result + ((transform == null) ? 0 : transform.hashCode()); result = prime * result + ((arraySpan == null) ? 0 : arraySpan.hashCode()); + result = prime * result + Boolean.hashCode(linkExternalHubs); + result = prime * result + Boolean.hashCode(replaceCribsWithProxies); + result = prime * result + Boolean.hashCode(replaceInterfacesWithP2P); return result; } @@ -322,6 +338,9 @@ public boolean equals(Object obj) { if (arraySpan == null) { if (other.arraySpan != null) return false; } else if (!arraySpan.equals(other.arraySpan)) return false; + if (linkExternalHubs != other.linkExternalHubs) return false; + if (replaceCribsWithProxies != other.replaceCribsWithProxies) return false; + if (replaceInterfacesWithP2P != other.replaceInterfacesWithP2P) return false; return true; } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMRenderer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMRenderer.java index baf4f215..a8fc07c6 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMRenderer.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMRenderer.java @@ -9,6 +9,7 @@ import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; @@ -22,7 +23,6 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; import cpw.mods.fml.common.gameevent.TickEvent.PlayerTickEvent; -import cpw.mods.fml.common.gameevent.TickEvent.Type; import cpw.mods.fml.relauncher.Side; import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber; @@ -160,7 +160,7 @@ private static void clear(EntityPlayer player) { needsHintDraw = false; needsAnalysis = false; - RenderHints.reset(); + RenderHints.INSTANCE.reset(); AboveHotbarHUD.renderTextAboveHotbar("", 0, false, false); } @@ -169,7 +169,6 @@ private static void clear(EntityPlayer player) { public static void checkPlayerStoppedBuilding(PlayerTickEvent event) { if (event.side != Side.CLIENT) return; if (event.phase != Phase.END) return; - if (event.type != Type.PLAYER) return; ItemStack inUse = event.player.getItemInUse(); @@ -511,8 +510,8 @@ private static void drawHints( World world = player.worldObj; - RenderHints.reset(); - RenderHints.setDrawOnTop(RenderingConfig.hintsOnTop || state.config.placeMode == PlaceMode.EXCHANGING); + RenderHints.INSTANCE.start(); + RenderHints.INSTANCE.setDepthTest(!RenderingConfig.hintsOnTop && state.config.placeMode != PlaceMode.EXCHANGING); for (PendingBlock pendingBlock : analysisCache) { if (!pendingBlock.isInWorld(world)) continue; @@ -526,11 +525,26 @@ private static void drawHints( if (pendingBlock.spec.isAir() && world.isAirBlock(pendingBlock.x, pendingBlock.y, pendingBlock.z)) continue; Block block = pendingBlock.getBlock(); + int meta = pendingBlock.spec.getBlockMeta(); + + Block previewBlock = pendingBlock.getPreviewBlock(); + if (previewBlock != null) { + block = previewBlock; + meta = pendingBlock.getPreviewMeta(); + } else if (pendingBlock.mp != null) { + block = Blocks.redstone_wire; + meta = 0; + } + if (block == null) continue; BlockSpec.fromBlock(pooled, world, pendingBlock.x, pendingBlock.y, pendingBlock.z); - if (pooled.isEquivalent(pendingBlock.spec)) continue; + if (pendingBlock.mp != null) { + if (pooled.getBlock() == pendingBlock.getBlock()) continue; + } else { + if (pooled.isEquivalent(pendingBlock.spec)) continue; + } if (++i > RenderingConfig.maxHints) break; @@ -547,7 +561,7 @@ private static void drawHints( } if (pendingBlock.spec.isAir()) { - RenderHints.addHint( + RenderHints.INSTANCE.addHint( pendingBlock.x, pendingBlock.y, pendingBlock.z, @@ -556,12 +570,12 @@ private static void drawHints( tint ); } else { - RenderHints.addHint( + RenderHints.INSTANCE.addHint( pendingBlock.x, pendingBlock.y, pendingBlock.z, block, - pendingBlock.spec.getBlockMeta(), + meta, tint ); } @@ -573,7 +587,7 @@ private static void drawHints( int y = CoordinatePacker.unpackY(packed); int z = CoordinatePacker.unpackZ(packed); - RenderHints.addHint(x, y, z, StructureLibAPI.getBlockHint(), StructureLibAPI.HINT_BLOCK_META_AIR, WARNING); + RenderHints.INSTANCE.addHint(x, y, z, StructureLibAPI.getBlockHint(), StructureLibAPI.HINT_BLOCK_META_AIR, WARNING); } } @@ -583,9 +597,11 @@ private static void drawHints( int y = CoordinatePacker.unpackY(packed); int z = CoordinatePacker.unpackZ(packed); - RenderHints.addHint(x, y, z, StructureLibAPI.getBlockHint(), StructureLibAPI.HINT_BLOCK_META_AIR, ERROR); + RenderHints.INSTANCE.addHint(x, y, z, StructureLibAPI.getBlockHint(), StructureLibAPI.HINT_BLOCK_META_AIR, ERROR); } } + + RenderHints.INSTANCE.finish(); } private static final short[] WHITE = { diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java index 257fba2d..e7077754 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java @@ -12,14 +12,20 @@ import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import cpw.mods.fml.common.registry.GameRegistry.UniqueIdentifier; +import gregtech.api.enums.ItemList; +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; import gregtech.common.blocks.BlockMachines; +import gregtech.common.tileentities.machines.MTEHatchCraftingInputME; import appeng.api.AEApi; import appeng.api.config.SecurityPermissions; @@ -29,6 +35,7 @@ import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; import appeng.api.networking.energy.IEnergyGrid; +import appeng.api.networking.security.IActionHost; import appeng.api.networking.security.ISecurityGrid; import appeng.api.networking.storage.IStorageGrid; import appeng.api.parts.IPartItem; @@ -54,6 +61,9 @@ import com.recursive_pineapple.matter_manipulator.common.building.ImmutableBlockSpec; import com.recursive_pineapple.matter_manipulator.common.building.InteropConstants; import com.recursive_pineapple.matter_manipulator.common.building.PendingBlock; +import com.recursive_pineapple.matter_manipulator.common.building.PortableItemStack; +import com.recursive_pineapple.matter_manipulator.common.building.SmartCopyIntegration; +import com.recursive_pineapple.matter_manipulator.common.building.SmartCopyIntegration.SmartCopyAction; import com.recursive_pineapple.matter_manipulator.common.data.WeightedSpecList; import com.recursive_pineapple.matter_manipulator.common.items.MMUpgrades; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.ItemMatterManipulator.ManipulatorTier; @@ -64,6 +74,7 @@ import com.recursive_pineapple.matter_manipulator.common.persist.WeightedListJsonAdapter; import com.recursive_pineapple.matter_manipulator.common.uplink.IUplinkMulti; import com.recursive_pineapple.matter_manipulator.common.utils.MMUtils; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; import org.joml.Vector3i; @@ -99,7 +110,7 @@ public class MMState { public transient ItemMatterManipulator manipulator; @Optional(Names.APPLIED_ENERGISTICS2) - public transient TileSecurity securityTerminal; + public transient IActionHost actionHost; @Optional(Names.APPLIED_ENERGISTICS2) public transient IGridNode gridNode; @Optional(Names.APPLIED_ENERGISTICS2) @@ -193,7 +204,7 @@ private void onLoad() { public boolean hasMEConnection() { if (!AppliedEnergistics2.isModLoaded()) return false; - return encKey != null && securityTerminal != null && gridNode != null && grid != null && storageGrid != null && itemStorage != null; + return encKey != null && actionHost != null && gridNode != null && grid != null && storageGrid != null && itemStorage != null; } /** @@ -213,7 +224,7 @@ public boolean connectToMESystem() { .getLocatableBy(encKey); if (grid instanceof TileSecurity security) { - this.securityTerminal = security; + this.actionHost = security; this.gridNode = security.getGridNode(ForgeDirection.UNKNOWN); if (this.gridNode != null) { this.grid = this.gridNode.getGrid(); @@ -222,6 +233,16 @@ public boolean connectToMESystem() { this.itemStorage = this.storageGrid.getItemInventory(); } } + } else if (grid instanceof TileWireless wireless) { + this.actionHost = wireless; + this.gridNode = wireless.getGridNode(ForgeDirection.UNKNOWN); + if (this.gridNode != null) { + this.grid = this.gridNode.getGrid(); + this.storageGrid = this.grid.getCache(IStorageGrid.class); + if (this.storageGrid != null) { + this.itemStorage = this.storageGrid.getItemInventory(); + } + } } return hasMEConnection(); @@ -333,6 +354,10 @@ private List getAnalysis(World world) { RegionAnalysis analysis = BlockAnalyzer .analyzeRegion(world, coordA, coordB, config.placeMode == PlaceMode.COPYING ? true : false); + if (config.placeMode == PlaceMode.COPYING && (config.replaceCribsWithProxies || config.replaceInterfacesWithP2P)) { + applySmartCopySubstitutions(world, analysis.blocks, coordA); + } + if (config.placeMode == PlaceMode.COPYING) { Transform t = getTransform(); @@ -358,34 +383,20 @@ private List getAnalysis(World world) { // copy the blocks (arraying) if (config.arraySpan != null) { - int sx = config.arraySpan.x; - int sy = config.arraySpan.y; - int sz = config.arraySpan.z; - List base = new ArrayList<>(analysis.blocks); analysis.blocks.clear(); - for (int y = Math.min(sy, 0); y <= Math.max(sy, 0); y++) { - for (int z = Math.min(sz, 0); z <= Math.max(sz, 0); z++) { - for (int x = Math.min(sx, 0); x <= Math.max(sx, 0); x++) { - int dx = x * (analysis.deltas.x + (analysis.deltas.x < 0 ? -1 : 1)); - int dy = y * (analysis.deltas.y + (analysis.deltas.y < 0 ? -1 : 1)); - int dz = z * (analysis.deltas.z + (analysis.deltas.z < 0 ? -1 : 1)); - - Vector3i d = new Vector3i(dx, dy, dz); - - t.apply(d); - - for (PendingBlock original : base) { - PendingBlock dup = original.clone(); - dup.x += d.x; - dup.y += d.y; - dup.z += d.z; - analysis.blocks.add(dup); - } - } + MMUtils.forEachArrayOffset(config.arraySpan, analysis.deltas, d -> { + t.apply(d); + + for (PendingBlock original : base) { + PendingBlock dup = original.clone(); + dup.x += d.x; + dup.y += d.y; + dup.z += d.z; + analysis.blocks.add(dup); } - } + }); } analysis.deltas = t.apply(analysis.deltas); @@ -402,6 +413,142 @@ private List getAnalysis(World world) { return analysis.blocks; } + private void applySmartCopySubstitutions(World world, List blocks, Location coordA) { + if (GregTech.isModLoaded() && config.replaceCribsWithProxies && hasCap(ItemMatterManipulator.ALLOW_SMART_COPY)) { + applySmartCopyCribs(world, blocks, coordA); + } + + if (AppliedEnergistics2.isModLoaded() && config.replaceInterfacesWithP2P) { + applySmartCopyP2P(world, blocks, coordA); + } + } + + @Optional(Names.GREG_TECH_NH) + private void applySmartCopyCribs(World world, List blocks, Location coordA) { + ItemStack proxyStack = ItemList.Hatch_CraftingInput_Bus_Slave.get(1L); + if (proxyStack == null) return; + + ImmutableBlockSpec proxySpec = new BlockSpec().setObject(proxyStack); + + for (PendingBlock block : blocks) { + int srcX = coordA.x + block.x; + int srcY = coordA.y + block.y; + int srcZ = coordA.z + block.z; + + TileEntity te = world.getTileEntity(srcX, srcY, srcZ); + if (!(te instanceof IGregTechTileEntity igte)) continue; + + IMetaTileEntity mte = igte.getMetaTileEntity(); + if (!(mte instanceof MTEHatchCraftingInputME)) continue; + + block.spec = proxySpec; + SmartCopyIntegration sc = new SmartCopyIntegration(); + sc.action = SmartCopyAction.CRIB_TO_PROXY; + sc.sourceX = srcX; + sc.sourceY = srcY; + sc.sourceZ = srcZ; + block.smartCopy = sc; + + NBTTagCompound proxyData = new NBTTagCompound(); + proxyData.setString("type", "craftingInputProxy"); + NBTTagCompound masterNBT = new NBTTagCompound(); + masterNBT.setInteger("x", srcX); + masterNBT.setInteger("y", srcY); + masterNBT.setInteger("z", srcZ); + proxyData.setTag("master", masterNBT); + + GTAnalysisResult gtResult = block.gt instanceof GTAnalysisResult ? (GTAnalysisResult) block.gt : new GTAnalysisResult(); + gtResult.mGTData = MMUtils.toJsonObject(proxyData); + gtResult.mGTGhostCircuit = 0; + block.gt = gtResult; + + block.ae = null; + block.inventory = null; + } + } + + /** + * Replaces patterned ME Interface parts in the analyzed blocks with P2P ME Interface tunnels. + * For each interface part that has patterns, this: + * 1. Swaps the part in the analysis to a P2P tunnel (output side) with a unique frequency + * 2. Records a P2PInfo so that PendingBuild can later place a matching P2P tunnel + * (input side) at the original source location, preserving the patterns there + * The result is that all pasted copies route patterns through P2P back to the single source interface. + */ + @Optional(Names.APPLIED_ENERGISTICS2) + private void applySmartCopyP2P(World world, List blocks, Location coordA) { + long freqBase = System.currentTimeMillis(); + int freqOffset = 0; + + ItemStack p2pIfaceStack = AEApi.instance().definitions().parts().p2PTunnelMEInterface().maybeStack(1).orNull(); + if (p2pIfaceStack == null) return; + + ItemStack p2pDualIfaceStack = null; + if (Mods.AE2FluidCraft.isModLoaded()) { + p2pDualIfaceStack = getFluidP2PInterfaceStack(); + } + + for (PendingBlock block : blocks) { + if (!(block.ae instanceof AEAnalysisResult aeResult)) continue; + if (aeResult.mAEParts == null) continue; + + int srcX = coordA.x + block.x; + int srcY = coordA.y + block.y; + int srcZ = coordA.z + block.z; + + for (int i = 0; i < aeResult.mAEParts.length; i++) { + AEPartData partData = aeResult.mAEParts[i]; + if (partData == null) continue; + if (partData.mAEPatterns == null) continue; + if (partData.isP2P()) continue; + + boolean isDual = p2pDualIfaceStack != null && isFluidInterfaceStack(partData.mPart); + ItemStack replacementStack = isDual ? p2pDualIfaceStack : p2pIfaceStack; + + ForgeDirection side = AEAnalysisResult.ALL_DIRECTIONS[i]; + long freq = freqBase + freqOffset++; + + // Replace the interface part with a P2P tunnel in the analysis + partData.mPart = new PortableItemStack(replacementStack); + partData.mP2POutput = true; + partData.mP2PFreq = freq; + partData.mAEPatterns = null; + + if (block.smartCopy == null) { + block.smartCopy = new SmartCopyIntegration(); + } + block.smartCopy.action = SmartCopyAction.INTERFACE_TO_P2P; + + if (block.smartCopy.p2pActions == null) { + block.smartCopy.p2pActions = new ArrayList<>(); + } + + // Record info for the source-side P2P placement (done later by PendingBuild) + SmartCopyIntegration.P2PInfo info = new SmartCopyIntegration.P2PInfo(); + info.freq = freq; + info.srcX = srcX; + info.srcY = srcY; + info.srcZ = srcZ; + info.srcSide = side; + info.destSide = side; + info.p2pItem = new PortableItemStack(replacementStack); + block.smartCopy.p2pActions.add(info); + } + } + } + + @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) + private static ItemStack getFluidP2PInterfaceStack() { + var item = com.glodblock.github.loader.ItemAndBlockHolder.FLUID_INTERFACE_P2P; + return item != null ? new ItemStack(item) : null; + } + + @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) + private static boolean isFluidInterfaceStack(PortableItemStack stack) { + var item = com.glodblock.github.loader.ItemAndBlockHolder.FLUID_INTERFACE; + return item != null && stack != null && stack.item != null && stack.getItem() == item; + } + private List getExchangeBlocks(ManipulatorTier tier, World world) { Location coordA = config.coordA; Location coordB = config.coordB; @@ -520,11 +667,30 @@ private List getCableBlocks(World world) { if (AppliedEnergistics2.isModLoaded()) { getAECables(a, b, out, block, world, config.cables); } + + if (Mods.OpenComputers.isModLoaded()) { + getOCCables(a, b, out, block, world, config.cables); + } } return out; } + private void getOCCables( + Vector3i a, + Vector3i b, + List out, + Block block, + World world, + ImmutableBlockSpec cable + ) { + if (InteropConstants.OC_CABLE.matches(block, 0)) { + for (Vector3i voxel : getLineVoxels(a.x, a.y, a.z, b.x, b.y, b.z)) { + out.add(cable.instantiate(world, voxel.x, voxel.y, voxel.z)); + } + } + } + @Optional(Names.GREG_TECH_NH) private void getGTCables(Vector3i a, Vector3i b, List out, Block block, World world, ImmutableBlockSpec cable) { if (block instanceof BlockMachines) { @@ -581,13 +747,16 @@ private void getAECables(Vector3i a, Vector3i b, List out, Block b if (InteropConstants.AE_BLOCK_CABLE.matches(world.getBlock(x, y, z), 0)) { ae = AEAnalysisResult.analyze(world.getTileEntity(voxel.x, voxel.y, voxel.z)); - ae.mAEParts[ForgeDirection.UNKNOWN.ordinal()] = new AEPartData(cable); } else { ae = new AEAnalysisResult(); + } + + if (ae.mAEParts == null) { ae.mAEParts = new AEPartData[7]; - ae.mAEParts[ForgeDirection.UNKNOWN.ordinal()] = new AEPartData(cable); } + ae.mAEParts[ForgeDirection.UNKNOWN.ordinal()] = new AEPartData(cable); + PendingBlock pendingBlock = cableSpec.instantiate(world, x, y, z); pendingBlock.ae = ae; @@ -1100,4 +1269,5 @@ public static enum PlaceMode { EXCHANGING, CABLES, } + } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/RenderHints.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/RenderHints.java index 6a5dc938..b0882f29 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/RenderHints.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/RenderHints.java @@ -1,8 +1,12 @@ package com.recursive_pineapple.matter_manipulator.common.items.manipulator; +import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*; + import java.nio.ByteBuffer; +import java.time.Duration; import java.util.ArrayList; import java.util.Comparator; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -15,73 +19,118 @@ import net.minecraft.entity.Entity; import net.minecraft.profiler.Profiler; import net.minecraft.util.IIcon; -import net.minecraft.world.World; import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.WorldEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; -import cpw.mods.fml.relauncher.Side; +import com.gtnewhorizon.gtnhlib.client.renderer.LocalTessellator; import com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.DefaultVertexFormat; import com.gtnewhorizon.gtnhlib.client.renderer.vertex.VertexFormat; -import com.gtnewhorizon.gtnhlib.eventbus.EventBusSubscriber; import com.recursive_pineapple.matter_manipulator.MMMod; import org.joml.Vector3d; import org.joml.Vector3i; -import org.lwjgl.LWJGLException; -import org.lwjgl.opengl.Display; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL15; import org.lwjgl.opengl.GL30; -import org.lwjgl.opengl.SharedDrawable; - -import lombok.Setter; -@EventBusSubscriber(side = Side.CLIENT) public class RenderHints { - /// The latest list of hints. This is not sorted in any way and can only be accessed by the main thread. - private static final ArrayList HINTS = new ArrayList<>(10000); + public static final RenderHints INSTANCE = new RenderHints(); - /// The list of hints that was sent to the worker thread. This field is modified by the main thread but the list - /// itself is modified by the worker thread. This is an optimization because the list from the prior position is - /// almost certainly nearly-sorted, which should reduce the number of comparisons significantly. - private static ArrayList drawnHints = null; + private static final VertexFormat VBO_FORMAT = DefaultVertexFormat.POSITION_TEXTURE_COLOR; - private static final Vector3d LAST_PLAYER_POSITION = new Vector3d(); - private static final Vector3i LAST_RENDERED_PLAYER_POSITION = new Vector3i(); + private static class RenderState { - private static boolean vboNeedsRebuild = false; - /// The VBO that's being used for rendering - private static StreamingVertexBuffer activeVBO; - /// The VBO that's being written to by the worker thread (or is idle) - private static StreamingVertexBuffer pendingVBO; + public final ArrayList hints; + public long expiration; + public boolean depthTest = false; - /// An opengl context that's active on the background thread and is used for writing to the pending VBO. - private static SharedDrawable backgroundContext; + public RenderState(ArrayList hints) { + this.hints = hints; + } + } - private static final ExecutorService WORKER_THREAD = Executors.newFixedThreadPool(1); - private static Future renderTask; + private static class TessellationResult { - @Setter - private static boolean drawOnTop = false; + public Vector3i vboOffset; + public long dataPtr; + public int dataSize; - public static void reset() { - if (renderTask != null) { - renderTask.cancel(true); - renderTask = null; + public TessellationResult(Vector3i vboOffset, long dataPtr, int dataSize) { + this.vboOffset = vboOffset; + this.dataPtr = dataPtr; + this.dataSize = dataSize; } + } + + /// The most recent batch of hints. This is not used by the renderer, and it must be flushed by calling [#finish()]. + /// This must only be accessed by the client thread. + private RenderState pending = null; - HINTS.clear(); - drawnHints = null; + /// The latest batch of hints. This is not sorted in any way and can only be accessed by the client thread. + /// The contents of this object's hint list can only be accessed by the worker thread, but the list reference itself + /// and the RenderState object can only be accessed by the client. The worker thread receives a reference to the + /// hint list, but this field can be replaced arbitrarily. + private RenderState hints = null; + + /// The player position for the most recent buffer. If the player moves too far, it will cause the quads to be + /// re-sorted. + private final Vector3i lastPlayerPosition = new Vector3i(); + + /// True when the hints have changed and the VBO needs to be rebuilt from scratch + private boolean vboNeedsRebuild = false; + + private final ExecutorService workerThread = Executors.newFixedThreadPool(1); + private Future buildTask; + + private TessellationResult lastResult; + private StreamingVertexBuffer vbo; + + public RenderHints() { + MinecraftForge.EVENT_BUS.register(this); + } + + public void start() { + pending = new RenderState(new ArrayList<>()); + } + + public void setDepthTest(boolean depthTest) { + pending.depthTest = depthTest; + } + + public void setExpiry(Duration duration) { + pending.expiration = System.currentTimeMillis() + duration.toMillis(); + } + + public void finish() { + hints = pending; + pending = null; vboNeedsRebuild = true; } - public static void addHint(int x, int y, int z, Block block, int meta, short[] tint) { + public void reset() { + if (buildTask != null) { + buildTask.cancel(true); + buildTask = null; + } + + pending = null; + hints = null; + + vboNeedsRebuild = false; + + if (vbo != null) { + vbo.orphan(); + } + } + + public void addHint(int x, int y, int z, Block block, int meta, short[] tint) { Hint hint = new Hint(); hint.x = x; @@ -94,94 +143,66 @@ public static void addHint(int x, int y, int z, Block block, int meta, short[] t hint.icons[i] = block.getIcon(i, meta); } - HINTS.add(hint); - - // Invalidate the cached sort results - drawnHints = null; + pending.hints.add(hint); } @SubscribeEvent - public static void onWorldLoad(WorldEvent.Load e) { + public void onWorldLoad(WorldEvent.Load e) { if (e.world.isRemote) { reset(); } } - private static VBOResult buildVBO(StreamingVertexBuffer vbo, ArrayList hints, double xd, double yd, double zd, int xi, int yi, int zi) { - try { - Vector3d eyes = new Vector3d(xd, yd, zd); - - try { - if (!backgroundContext.isCurrent()) { - backgroundContext.makeCurrent(); - } - } catch (LWJGLException e) { - throw new RuntimeException("Could not activate background GL context", e); - } - - hints.sort(Comparator.comparingDouble(info -> -eyes.distanceSquared(info.x + 0.5, info.y + 0.5, info.z + 0.5))); - - Tessellator tes = TessellatorManager.startCapturingAndGet(); - - tes.startDrawing(GL11.GL_QUADS); + private TessellationResult buildVBO(ArrayList hints, Vector3d eyes, Vector3i worldPos) { + hints.sort(Comparator.comparingDouble(info -> -eyes.distanceSquared(info.x, info.y, info.z))); - int hintCount = hints.size(); + final VertexFormat format = VBO_FORMAT; + final int vertexSize = format.getVertexSize(); - for (int i = 0; i < hintCount; i++) { - hints.get(i).draw(tes, xd, yd, zd, xi, yi, zi); - } + final LocalTessellator tes = TessellatorManager.enterLocalMode(); + tes.startDrawing(GL11.GL_QUADS); - final var quads = TessellatorManager.stopCapturingToPooledQuads(); + final int hintCount = hints.size(); - final VertexFormat format = DefaultVertexFormat.POSITION_TEXTURE_COLOR; + int capacity = hintCount * 6 * 4 * format.getVertexSize(); + final int quadSize = tes.getDataSize(vertexSize); - // noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (vbo) { - vbo.allocate(quads.size() * 4, GL15.GL_STREAM_DRAW); + long basePtr = nmemAllocChecked(capacity); + long writePtr = basePtr; + long endPtr = writePtr + capacity; - ByteBuffer buffer = vbo.map(GL30.GL_MAP_WRITE_BIT); + for (int i = 0; i < hintCount; i++) { + hints.get(i).draw(tes, eyes.x, eyes.y, eyes.z, worldPos.x, worldPos.y, worldPos.z); - buffer.rewind(); + if (writePtr + quadSize > endPtr) { + capacity = Math.max(capacity + quadSize, (int) (capacity * 1.5)); - long expectedSize = (long) format.getVertexSize() * quads.size() * 4; + long offset = writePtr - basePtr; - if (expectedSize > buffer.capacity()) { - MMMod.LOG.error( - "Could not upload hint VBO: Could not insert hint quads into GL buffer (expectedSize={}, buffer.capacity={})", - expectedSize, - buffer.capacity() - ); + basePtr = nmemReallocChecked(basePtr, capacity); - return new VBOResult(new Vector3i(xi, yi, zi), 0); - } + writePtr = basePtr + offset; + endPtr = writePtr + capacity; + } - for (int i = 0, quadsSize = quads.size(); i < quadsSize; i++) { - format.writeQuad(quads.get(i), buffer); - } + writePtr = tes.writeToBuffer0(writePtr, format); + } - buffer.rewind(); + tes.exitLocalMode(); - vbo.unmap(); - } + final int dataSize = (int) (writePtr - basePtr); - return new VBOResult(new Vector3i(xi, yi, zi), quads.size() * 4); - } finally { - TessellatorManager.cleanup(); - } + return new TessellationResult(new Vector3i(worldPos), basePtr, dataSize); } @SubscribeEvent - public static void onRenderWorldLast(RenderWorldLastEvent e) { - if (HINTS.isEmpty()) return; - - if (backgroundContext == null) { - try { - backgroundContext = new SharedDrawable(Display.getDrawable()); - } catch (LWJGLException ex) { - throw new RuntimeException("Could not initialized background SharedDrawable", ex); - } + public void onRenderWorldLast(RenderWorldLastEvent e) { + if (hints != null && hints.expiration > 0 && System.currentTimeMillis() >= hints.expiration) { + hints = null; } + if (hints == null || hints.hints.isEmpty()) return; + Profiler p = Minecraft.getMinecraft().mcProfiler; p.startSection("Render MM Hints"); @@ -190,52 +211,17 @@ public static void onRenderWorldLast(RenderWorldLastEvent e) { double xd = player.lastTickPosX + (player.posX - player.lastTickPosX) * e.partialTicks; double yd = player.lastTickPosY + (player.posY - player.lastTickPosY) * e.partialTicks; double zd = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * e.partialTicks; - int xi = (int) xd, yi = (int) yd, zi = (int) zd; - Vector3d currentPos = new Vector3d(xd, yd, zd); + Vector3i worldPos = new Vector3i((int) xd, (int) yd, (int) zd); - if (activeVBO == null) { - activeVBO = new StreamingVertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_COLOR, GL11.GL_QUADS); - } + // Subtract by 0.5 because Hint stores the corner coordinates + final Vector3d eyes = new Vector3d(xd - 0.5f, yd - 0.5f, zd - 0.5f); - if (pendingVBO == null) { - pendingVBO = new StreamingVertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_COLOR, GL11.GL_QUADS); + if (vbo == null) { + vbo = new StreamingVertexBuffer(VBO_FORMAT, GL11.GL_QUADS); } - if (renderTask != null && renderTask.isDone()) { - VBOResult result = null; - - try { - result = renderTask.get(); - } catch (InterruptedException | ExecutionException ex) { - MMMod.LOG.error("Could not assemble render hint quads", ex); - } - - renderTask = null; - - if (result != null) { - LAST_RENDERED_PLAYER_POSITION.set(result.playerPosition); - - StreamingVertexBuffer temp = activeVBO; - activeVBO = pendingVBO; - pendingVBO = temp; - } - } - - if (renderTask == null && (vboNeedsRebuild || currentPos.distance(LAST_PLAYER_POSITION) > 1.0)) { - LAST_PLAYER_POSITION.set(currentPos); - vboNeedsRebuild = false; - - // If the hint list has changed, re-copy them into the drawnHints list so that the worker thread can sort - // them. - if (drawnHints == null) { - drawnHints = new ArrayList<>(HINTS); - } - - renderTask = WORKER_THREAD.submit(() -> buildVBO(pendingVBO, drawnHints, xd, yd, zd, xi, yi, zi)); - } - - if (activeVBO.getVertexCount() > 0) { + if (vbo.getVertexCount() > 0) { p.startSection("Draw MM Hints"); GL11.glPushMatrix(); @@ -243,7 +229,8 @@ public static void onRenderWorldLast(RenderWorldLastEvent e) { Minecraft.getMinecraft().renderEngine.bindTexture(TextureMap.locationBlocksTexture); - GL11.glTranslated(-xd + LAST_RENDERED_PLAYER_POSITION.x, -yd + LAST_RENDERED_PLAYER_POSITION.y, -zd + LAST_RENDERED_PLAYER_POSITION.z); + // vboOffset is the integer player pos at build time; translate by how much the player has drifted since + GL11.glTranslated(lastResult.vboOffset.x - xd, lastResult.vboOffset.y - yd, lastResult.vboOffset.z - zd); // we need the back facing rendered because the thing is transparent GL11.glDisable(GL11.GL_CULL_FACE); @@ -251,17 +238,13 @@ public static void onRenderWorldLast(RenderWorldLastEvent e) { GL11.glEnable(GL11.GL_BLEND); // enable blend so it is transparent GL11.glBlendFunc(GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_SRC_ALPHA); - if (drawOnTop) { + if (!hints.depthTest) { GL11.glDisable(GL11.GL_DEPTH_TEST); } else { GL11.glEnable(GL11.GL_DEPTH_TEST); } - // noinspection SynchronizeOnNonFinalField - synchronized (activeVBO) { - // There aren't any frames in flight, so we can re-use this buffer on the next frame without issue - activeVBO.render(); - } + vbo.render(); GL11.glPopAttrib(); GL11.glPopMatrix(); @@ -269,6 +252,43 @@ public static void onRenderWorldLast(RenderWorldLastEvent e) { p.endSection(); } + if (buildTask != null && buildTask.isDone()) { + lastResult = null; + + try { + lastResult = buildTask.get(); + } catch (InterruptedException | ExecutionException | CancellationException ex) { + MMMod.LOG.error("Could not assemble render hint quads", ex); + } + + buildTask = null; + + if (lastResult != null) { + final int vertexCount = lastResult.dataSize / VBO_FORMAT.getVertexSize(); + vbo.allocate(vertexCount, GL15.GL_DYNAMIC_DRAW); + final ByteBuffer buffer = vbo.map(GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_BUFFER_BIT); + + if (buffer == null) { + MMMod.LOG.error( + "Could not upload hint VBO: glMapBufferRange returned null (vertexCount={})", + vertexCount + ); + } else { + memCopy(lastResult.dataPtr, memAddress0(buffer), lastResult.dataSize); + vbo.unmap(); + } + + nmemFree(lastResult.dataPtr); + } + } + + if (buildTask == null && (vboNeedsRebuild || worldPos.distance(lastPlayerPosition) > 1.0)) { + lastPlayerPosition.set(worldPos); + vboNeedsRebuild = false; + + buildTask = workerThread.submit(() -> buildVBO(this.hints.hints, eyes, worldPos)); + } + p.endSection(); } @@ -289,11 +309,6 @@ public void draw( ) { double size = 0.5; - World w = Minecraft.getMinecraft().theWorld; - - int brightness = w.blockExists(x, 0, z) ? w.getLightBrightnessForSkyBlocks(x, y, z, 0) : 0; - tes.setBrightness(brightness); - tes.setColorRGBA(tint[0], tint[1], tint[2], 150); double X = (x - eyeXint) + 0.25; @@ -324,7 +339,6 @@ public void draw( case 0 -> { // all these ifs is in form if ((is face unobstructed) != (is in unobstructred pass)) if ((worldY >= eyeY) != unobstructedPass) continue; - tes.setNormal(0, -1, 0); tes.addVertexWithUV(X, Y, Z, u, v); tes.addVertexWithUV(X + size, Y, Z, U, v); tes.addVertexWithUV(X + size, Y, Z + size, U, V); @@ -332,7 +346,6 @@ public void draw( } case 1 -> { if ((worldY + size <= eyeY) != unobstructedPass) continue; - tes.setNormal(0, 1, 0); tes.addVertexWithUV(X, Y + size, Z, u, v); tes.addVertexWithUV(X, Y + size, Z + size, u, V); tes.addVertexWithUV(X + size, Y + size, Z + size, U, V); @@ -340,7 +353,6 @@ public void draw( } case 2 -> { if ((worldZ >= eyeZ) != unobstructedPass) continue; - tes.setNormal(0, 0, -1); tes.addVertexWithUV(X, Y, Z, U, V); tes.addVertexWithUV(X, Y + size, Z, U, v); tes.addVertexWithUV(X + size, Y + size, Z, u, v); @@ -348,7 +360,6 @@ public void draw( } case 3 -> { if ((worldZ + size <= eyeZ) != unobstructedPass) continue; - tes.setNormal(0, 0, 1); tes.addVertexWithUV(X + size, Y, Z + size, U, V); tes.addVertexWithUV(X + size, Y + size, Z + size, U, v); tes.addVertexWithUV(X, Y + size, Z + size, u, v); @@ -356,7 +367,6 @@ public void draw( } case 4 -> { if ((worldX >= eyeX) != unobstructedPass) continue; - tes.setNormal(-1, 0, 0); tes.addVertexWithUV(X, Y, Z + size, U, V); tes.addVertexWithUV(X, Y + size, Z + size, U, v); tes.addVertexWithUV(X, Y + size, Z, u, v); @@ -364,7 +374,6 @@ public void draw( } case 5 -> { if ((worldX + size <= eyeX) != unobstructedPass) continue; - tes.setNormal(1, 0, 0); tes.addVertexWithUV(X + size, Y, Z, U, V); tes.addVertexWithUV(X + size, Y + size, Z, U, v); tes.addVertexWithUV(X + size, Y + size, Z + size, u, v); @@ -375,15 +384,4 @@ public void draw( } } } - - private static class VBOResult { - - public Vector3i playerPosition; - public int vertexCount; - - public VBOResult(Vector3i playerPosition, int vertexCount) { - this.playerPosition = playerPosition; - this.vertexCount = vertexCount; - } - } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/StreamingVertexBuffer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/StreamingVertexBuffer.java index 60172ec5..0d19d5b2 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/StreamingVertexBuffer.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/StreamingVertexBuffer.java @@ -23,11 +23,14 @@ public class StreamingVertexBuffer implements AutoCloseable { @Getter protected int id; + + // The amount of vertices to render @Getter protected int vertexCount; + // The amount of bytes allocated in the buffer @Getter - protected long length; + protected long allocatedBytes; @Getter protected int bufferFlags; @@ -52,6 +55,20 @@ public void generate() { this.id = GL15.glGenBuffers(); } + public boolean isAllocated() { + return allocatedBytes != 0; + } + + public void orphan() { + if (this.isAllocated()) { + vertexCount = 0; + allocatedBytes = 0; + bind(); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, 0, bufferFlags != 0 ? bufferFlags : GL15.GL_STREAM_DRAW); + unbind(); + } + } + @Override public void close() { if (this.id > 0) { @@ -61,7 +78,7 @@ public void close() { this.id = 0; this.vertexCount = 0; - this.length = 0; + this.allocatedBytes = 0; } } @@ -124,12 +141,16 @@ public void render() { /// Reference: [Buffer Object Streaming](https://wikis.khronos.org/opengl/Buffer_Object_Streaming) public void reallocate() { bind(); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, length, bufferFlags); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, allocatedBytes, bufferFlags); unbind(); } + public void clear() { + this.vertexCount = 0; + } + public void allocate( - int vertexCount, + int newVertexCount, @MagicConstant(intValues = { GL15.GL_STREAM_DRAW, GL15.GL_STREAM_READ, @@ -142,21 +163,22 @@ public void allocate( GL15.GL_DYNAMIC_COPY, }) int usage ) { - bind(); - - this.vertexCount = vertexCount; - this.length = vertexCount * (long) format.getVertexSize(); - this.bufferFlags = usage; - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.length, this.bufferFlags); + if (newVertexCount != vertexCount) { + this.vertexCount = newVertexCount; + this.allocatedBytes = newVertexCount * (long) format.getVertexSize(); + this.bufferFlags = usage; - unbind(); + bind(); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.allocatedBytes, this.bufferFlags); + unbind(); + } } /// Maps the buffer into the client memory space (CPU) and returns a [ByteBuffer] wrapper for it. /// @param access See [glMapBufferRange](https://docs.gl/es3/glMapBufferRange) for more info. public ByteBuffer map( - @MagicConstant(intValues = { + @MagicConstant(flags = { GL30.GL_MAP_READ_BIT, GL30.GL_MAP_WRITE_BIT, GL30.GL_MAP_INVALIDATE_RANGE_BIT, @@ -173,7 +195,7 @@ public ByteBuffer map( mappedBuffer.clear(); } - mappedBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, length, access, mappedBuffer); + mappedBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, allocatedBytes, access, mappedBuffer); if (mappedBuffer == null) { MMMod.LOG.error("Error mapping buffer: {}", GLU.gluErrorString(GL11.glGetError())); diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/networking/Messages.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/networking/Messages.java index 905b6a44..371239cf 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/networking/Messages.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/networking/Messages.java @@ -87,6 +87,9 @@ public enum Messages { state.config.action = null; }))), SetShape(server(enumPacket(Shape.values(), (state, value) -> state.config.shape = value))), + SetLinkExternalHubs(server(simple((player, stack, manipulator, state) -> { + state.config.linkExternalHubs = !state.config.linkExternalHubs; + }))), SetA(server(locationPacket((player, stack, manipulator, state, location) -> { state.config.coordA = new Location(player.worldObj, location); }))), @@ -307,6 +310,12 @@ public IntPacket getNewPacket(Messages message, @Nullable Object value) { return packet; } })), + SetReplaceCribs(server(simple((player, stack, manipulator, state) -> { + state.config.replaceCribsWithProxies = !state.config.replaceCribsWithProxies; + }))), + SetReplaceInterfaces(server(simple((player, stack, manipulator, state) -> { + state.config.replaceInterfacesWithP2P = !state.config.replaceInterfacesWithP2P; + }))), SetArray(server(locationPacket((player, stack, manipulator, state, span) -> { state.config.arraySpan = span; }))), diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplink.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplink.java index 60381358..c04a6adc 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplink.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplink.java @@ -3,15 +3,16 @@ import static com.recursive_pineapple.matter_manipulator.common.structure.MMCasings.AdvancedIridiumPlatedMachineCasing; import static com.recursive_pineapple.matter_manipulator.common.structure.MMCasings.MatterGenerationCoil; import static com.recursive_pineapple.matter_manipulator.common.structure.MMCasings.RadiantNaquadahAlloyCasing; -import static gregtech.api.enums.GTValues.AuthorPineapple; +import static gregtech.api.enums.GTAuthors.AuthorPineapple; import static gregtech.api.enums.HatchElement.Energy; import static gregtech.api.enums.HatchElement.ExoticEnergy; import static gregtech.api.enums.HatchElement.InputHatch; import static gregtech.api.enums.HatchElement.Maintenance; +import static gregtech.api.enums.Textures.BlockIcons.custom; +import static gregtech.api.enums.Textures.BlockIcons.customOptional; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import javax.annotation.Nonnull; @@ -22,7 +23,6 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.StatCollector; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -33,10 +33,9 @@ import cpw.mods.fml.relauncher.SideOnly; import gregtech.api.enums.Materials; -import gregtech.api.enums.StructureError; -import gregtech.api.enums.Textures.BlockIcons.CustomIcon; import gregtech.api.enums.TierEU; import gregtech.api.interfaces.IHatchElement; +import gregtech.api.interfaces.IIconContainer; import gregtech.api.interfaces.ITexture; import gregtech.api.interfaces.metatileentity.IMetaTileEntity; import gregtech.api.interfaces.tileentity.IGregTechTileEntity; @@ -53,6 +52,7 @@ import gregtech.api.structure.StructureWrapper; import gregtech.api.structure.StructureWrapperInstanceInfo; import gregtech.api.structure.StructureWrapperTooltipBuilder; +import gregtech.api.structure.error.StructureError; import gregtech.api.util.GTRecipe; import gregtech.api.util.GTUtility; import gregtech.api.util.IGTHatchAdder; @@ -164,26 +164,9 @@ public int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBu } @Override - public boolean checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack) { - return structure.checkStructure(this); - } - - @Override - protected void validateStructure(Collection errors, NBTTagCompound context) { - super.validateStructure(errors, context); - - structureInstanceInfo.validate(errors, context); - } - - @Override - protected void localizeStructureErrors( - Collection errors, - NBTTagCompound context, - List lines - ) { - super.localizeStructureErrors(errors, context, lines); - - structureInstanceInfo.localizeStructureErrors(errors, context, lines); + public void checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack, List errors) { + if (!structure.checkStructure(this, errors)) return; + structureInstanceInfo.validate(errors); } private enum UplinkHatchAdder implements IHatchElement { @@ -259,17 +242,20 @@ protected MultiblockTooltipBuilder createTooltip() { return tt; } - private static final CustomIcon ACTIVE_GLOW = new CustomIcon( + private static final IIconContainer ACTIVE_GLOW = customOptional( Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_ACTIVE_GLOW") ); - private static final CustomIcon IDLE_GLOW = new CustomIcon( + + private static final IIconContainer IDLE_GLOW = customOptional( Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_IDLE_GLOW") ); - private static final CustomIcon OFF = new CustomIcon( + + private static final IIconContainer OFF = custom( Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_OFF") ); @Override + @SideOnly(Side.CLIENT) public ITexture[] getTexture( IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, @@ -387,11 +373,6 @@ public int getDamageToComponent(ItemStack aStack) { return 0; } - @Override - public boolean explodesOnComponentBreak(ItemStack aStack) { - return false; - } - // #endregion @Override @@ -706,10 +687,9 @@ public void submitPlan( MMUtils.sendInfoToPlayer( submitter, - StatCollector.translateToLocalFormatted( - "mm.info.new_virtual_me_pattern", - patternName - ) + "mm.info.new_virtual_me_pattern", + // FIXME: localize it + patternName ); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplinkMEHatch.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplinkMEHatch.java index 8a2fe285..872e171f 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplinkMEHatch.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/MTEMMUplinkMEHatch.java @@ -22,7 +22,6 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.util.ChatComponentTranslation; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.StatCollector; import net.minecraftforge.common.util.ForgeDirection; @@ -158,10 +157,9 @@ private void pollRequests() { if (player != null) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.craft_failed", - request.requestName - ) + "mm.info.error.craft_failed", + // FIXME: localize it + request.requestName ); } iter.remove(); @@ -449,10 +447,9 @@ public boolean pushPattern(ICraftingPatternDetails patternDetails, InventoryCraf if (player != null) { sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.craft_finished", - request.requestName - ) + "mm.info.craft_finished", + // FIXME: localize it + request.requestName ); } } @@ -551,7 +548,7 @@ public void onScrewdriverRightClick( onRequestsChanged(); - sendInfoToPlayer(aPlayer, StatCollector.translateToLocal("mm.info.cleared_all_plans_and_pending_jobs")); + sendInfoToPlayer(aPlayer, "mm.info.cleared_all_plans_and_pending_jobs"); } public void clearManualPlans(EntityPlayer player) { @@ -564,7 +561,7 @@ public void clearManualPlans(EntityPlayer player) { onRequestsChanged(); - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.cleared_your_manual_plans")); + sendInfoToPlayer(player, "mm.info.cleared_your_manual_plans"); } public void cancelAutoPlans(EntityPlayer player) { @@ -590,7 +587,7 @@ public void cancelAutoPlans(EntityPlayer player) { onRequestsChanged(); - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.cleared_your_plans_and_craft")); + sendInfoToPlayer(player, "mm.info.cleared_your_plans_and_craft"); } private void onRequestsChanged() { @@ -749,10 +746,9 @@ boolean poll() { if (player != null) { sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.submitted_job", - requestName - ) + "mm.info.submitted_job", + // FIXME: localize it + requestName ); } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/UplinkStatus.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/UplinkStatus.java index 359c9d98..d08ae15f 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/UplinkStatus.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/uplink/UplinkStatus.java @@ -16,4 +16,13 @@ public String toString() { case NO_HATCH -> "Missing ME hatch"; }; } + + public String toUnlocalizedString() { + return switch (this) { + case OK -> "mm.enum.uplink_status.ok"; + case NO_PLASMA -> "mm.enum.uplink_status.no_plasma"; + case AE_OFFLINE -> "mm.enum.uplink_status.ae_offline"; + case NO_HATCH -> "mm.enum.uplink_status.no_hatch"; + }; + } } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/InventoryAdapter.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/InventoryAdapter.java index 32e7c3fc..655c8f58 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/InventoryAdapter.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/InventoryAdapter.java @@ -3,6 +3,7 @@ import net.minecraft.inventory.IInventory; import net.minecraft.inventory.ISidedInventory; import net.minecraft.item.ItemStack; +import net.minecraft.util.ChatComponentTranslation; import net.minecraftforge.common.util.ForgeDirection; @@ -13,8 +14,10 @@ import gregtech.api.metatileentity.implementations.MTEHatchInputBus; import gregtech.api.metatileentity.implementations.MTEHatchOutputBus; import gregtech.api.metatileentity.implementations.MTEMultiBlockBase; -import gregtech.common.tileentities.machines.MTEHatchOutputBusME; -import gregtech.common.tileentities.machines.MTEHatchOutputME; +import gregtech.common.tileentities.machines.MTEHatchCraftingInputME; +import gregtech.common.tileentities.machines.MTEHatchCraftingInputSlave; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputBusME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputME; import com.google.common.collect.ImmutableList; import com.recursive_pineapple.matter_manipulator.asm.Optional; @@ -56,13 +59,13 @@ public boolean validate(BlockAnalyzer.IBlockApplyContext context, IInventory inv IGregTechTileEntity igte = (IGregTechTileEntity) inv; MTEHatchRack rack = (MTEHatchRack) igte.getMetaTileEntity(); - if (rack.heat > 2000) { - context.error("QC Rack is too hot to extract or insert items"); + if (rack.getHeat() > 2000) { + context.error(new ChatComponentTranslation("mm.info.error.qc_rack_is_too_hot")); return false; } if (igte.isActive()) { - context.error("Cannot extract or insert items from/into QC Rack while the QC is on"); + context.error(new ChatComponentTranslation("mm.info.error.cannot_extract_while_qc_on")); return false; } @@ -83,6 +86,8 @@ public boolean canHandle(IInventory inv) { if (imte instanceof MTEMultiBlockBase) return true; if (imte instanceof MTEBasicBatteryBuffer) return true; if (imte instanceof MTEHatchTurbine) return true; + if (imte instanceof MTEHatchCraftingInputME) return true; + if (imte instanceof MTEHatchCraftingInputSlave) return true; } return false; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java index be74904b..01498af4 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java @@ -13,7 +13,6 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; -import java.math.BigInteger; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -21,6 +20,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -30,6 +30,7 @@ import java.util.Objects; import java.util.Random; import java.util.UUID; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.IntFunction; import java.util.regex.Pattern; @@ -61,12 +62,13 @@ import net.minecraft.nbt.NBTTagString; import net.minecraft.server.MinecraftServer; import net.minecraft.util.AxisAlignedBB; -import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChatComponentTranslation; +import net.minecraft.util.ChatStyle; import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; import net.minecraft.util.MovingObjectPosition.MovingObjectType; -import net.minecraft.util.StatCollector; import net.minecraft.util.Vec3; import net.minecraft.world.World; @@ -103,6 +105,8 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonPrimitive; +import com.gtnewhorizon.gtnhlib.chat.customcomponents.ChatComponentItemName; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; import com.gtnewhorizon.structurelib.util.XSTR; import com.recursive_pineapple.matter_manipulator.MMMod; import com.recursive_pineapple.matter_manipulator.asm.Optional; @@ -413,28 +417,64 @@ public static List getBlocksInBB(Location l, Vector3i deltas) { return blocks; } - public static void sendErrorToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(RED + aChatMessage)); + /** + * Iterates over all array copy offsets for the given span and deltas, + * calling the consumer with each offset vector (dx, dy, dz) in world-space + * (before any transform is applied). + * + * @param arraySpan the array repetition counts in each axis + * @param deltas the region deltas from {@link #getRegionDeltas} + * @param consumer called with each offset vector; the vector is reused, so callers must not retain references + */ + public static void forEachArrayOffset(Vector3i arraySpan, Vector3i deltas, Consumer consumer) { + int sx = arraySpan.x; + int sy = arraySpan.y; + int sz = arraySpan.z; + + for (int ay = Math.min(sy, 0); ay <= Math.max(sy, 0); ay++) { + for (int az = Math.min(sz, 0); az <= Math.max(sz, 0); az++) { + for (int ax = Math.min(sx, 0); ax <= Math.max(sx, 0); ax++) { + int dx = ax * (deltas.x + (deltas.x < 0 ? -1 : 1)); + int dy = ay * (deltas.y + (deltas.y < 0 ? -1 : 1)); + int dz = az * (deltas.z + (deltas.z < 0 ? -1 : 1)); + + consumer.accept(new Vector3i(dx, dy, dz)); + } + } } } - public static void sendWarningToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(GOLD + aChatMessage)); + public static void sendChatToPlayerWithColor(EntityPlayer aPlayer, IChatComponent aChat, EnumChatFormatting color) { + if (aPlayer instanceof EntityPlayerMP && aChat != null) { + if (color != null) { + aChat.setChatStyle(new ChatStyle().setColor(color)); + } + aPlayer.addChatComponentMessage(aChat); } } - public static void sendInfoToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(GRAY + aChatMessage)); - } + public static void sendErrorToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendChatToPlayerWithColor(aPlayer, new ChatComponentTranslation(aChatMessageKey, args), EnumChatFormatting.RED); } - public static void sendChatToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(aChatMessage)); - } + public static void sendWarningToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendChatToPlayerWithColor(aPlayer, new ChatComponentTranslation(aChatMessageKey, args), EnumChatFormatting.GOLD); + } + + public static void sendInfoToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendInfoToPlayer(aPlayer, new ChatComponentTranslation(aChatMessageKey, args)); + } + + public static void sendInfoToPlayer(EntityPlayer aPlayer, IChatComponent aChat) { + sendChatToPlayerWithColor(aPlayer, aChat, EnumChatFormatting.GRAY); + } + + public static void sendChatToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendChatToPlayer(aPlayer, new ChatComponentTranslation(aChatMessageKey, args)); + } + + public static void sendChatToPlayer(EntityPlayer aPlayer, IChatComponent aChat) { + sendChatToPlayerWithColor(aPlayer, aChat, null); } public static String stripFormat(String text) { @@ -454,18 +494,6 @@ private static DecimalFormat getDecimalFormat() { }); } - public static String formatNumbers(BigInteger aNumber) { - return getDecimalFormat().format(aNumber); - } - - public static String formatNumbers(long aNumber) { - return getDecimalFormat().format(aNumber); - } - - public static String formatNumbers(double aNumber) { - return getDecimalFormat().format(aNumber); - } - public static EntityPlayer getPlayerById(UUID playerId) { for (EntityPlayer player : MinecraftServer.getServer().getConfigurationManager().playerEntityList) { if (player.getGameProfile().getId().equals(playerId)) { return player; } @@ -524,7 +552,7 @@ public static List getStacksOfSize(Object2LongOpenHashMap map map.forEach((item, amount) -> { while (amount > 0) { int toRemove = Math - .min(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : amount.intValue(), maxStackSize); + .min(amount > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) amount, maxStackSize); list.add(item.getItemStack(toRemove)); @@ -870,7 +898,13 @@ public static boolean installUpgrades( if (src instanceof IBlockApplyContext ctx) { for (BigItemStack wanted : toInstallBig) { if (wanted.stackSize > 0) { - ctx.warn("Could not find upgrade: " + wanted.getItemStack().getDisplayName() + " x " + wanted.stackSize); + ctx.warn( + new ChatComponentTranslation( + "mm.info.warning.could_not_find_upgrade", + new ChatComponentItemName(wanted.getItemStack()), + wanted.stackSize + ) + ); success = false; } } @@ -914,7 +948,7 @@ public static boolean installUpgrades( ); if (src instanceof IBlockApplyContext ctx) { - ctx.error("Tried to install too many upgrades: voiding the rest (this is a bug, please report it)"); + ctx.error(new ChatComponentTranslation("mm.info.error.too_many_upgrades")); } break outer; } @@ -1310,7 +1344,7 @@ public static void createPlanImpl( state = state.clone(); if (!Location.areCompatible(state.config.coordA, state.config.coordB)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.must_have_copy_region")); + sendErrorToPlayer(player, "mm.info.error.must_have_copy_region"); return; } @@ -1320,7 +1354,7 @@ public static void createPlanImpl( } } else { if (!Location.areCompatible(state.config.coordA, state.config.coordC)) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.must_have_paste_region")); + sendErrorToPlayer(player, "mm.info.error.must_have_paste_region"); return; } } @@ -1343,7 +1377,7 @@ public static void createPlanImpl( List availableItems = extractResult.right() == null ? new ArrayList<>() : extractResult.right(); - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.required_items")); + sendInfoToPlayer(player, "mm.info.required_items"); if (!requiredItems.isEmpty()) { requiredItems.stream() @@ -1354,10 +1388,9 @@ public static void createPlanImpl( .sum(); if (stack.getStackSize() - available > 0) { - return String.format( - "%s%s: %s%d%s (%s%d%s missing)", - stack.getItemStack() - .getDisplayName(), + return new ChatComponentTranslation( + "mm.info.missing", + new ChatComponentItemName(stack.getItemStack()), GRAY, GOLD, stack.getStackSize(), @@ -1367,10 +1400,9 @@ public static void createPlanImpl( GRAY ); } else { - return String.format( - "%s%s: %s%d%s", - stack.getItemStack() - .getDisplayName(), + return new ChatComponentTranslation( + "%s%s: %s%s%s", + new ChatComponentItemName(stack.getItemStack()), GRAY, GOLD, stack.getStackSize(), @@ -1378,10 +1410,10 @@ public static void createPlanImpl( ); } }) - .sorted() + .sorted(Comparator.comparing(IChatComponent::getUnformattedText)) .forEach(message -> { sendInfoToPlayer(player, message); }); } else { - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.none")); + sendInfoToPlayer(player, "mm.info.none"); } if (!requiredItems.isEmpty()) { @@ -1413,10 +1445,10 @@ public static void createPlanImpl( (flags & PLAN_AUTO_SUBMIT) != 0 ); } else { - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.not_need_creating_pattern")); + sendInfoToPlayer(player, "mm.info.not_need_creating_pattern"); } } else { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.not_connected")); + sendErrorToPlayer(player, "mm.info.error.not_connected"); } } } @@ -1540,6 +1572,19 @@ public static boolean getAECable(BlockSpec spec, World world, int x, int y, int return false; } + public static boolean isOCCable(ImmutableBlockSpec spec) { + if (spec == null) return false; + return InteropConstants.OC_CABLE.matches(spec.getBlock(), 0); + } + + public static boolean getOCCable(BlockSpec spec, World world, int x, int y, int z) { + if (InteropConstants.OC_CABLE.matches(world.getBlock(x, y, z), 0)) { + spec.setObject(Item.getItemFromBlock(world.getBlock(x, y, z)), 0); + return true; + } + return false; + } + @Optional(Names.GREG_TECH_NH) public static boolean getGTCable(BlockSpec spec, World world, int x, int y, int z) { if (world.getTileEntity(x, y, z) instanceof IGregTechTileEntity igte && igte.getMetaTileEntity() instanceof IConnectable) { @@ -1567,6 +1612,22 @@ public static String getDirectionDisplayName(ForgeDirection dir, boolean unknown }; } + public static String getDirectionUnlocalizedName(ForgeDirection dir, boolean unknownIsCentre) { + return switch (dir) { + case DOWN -> "mm.direction.down"; + case EAST -> "mm.direction.east"; + case NORTH -> "mm.direction.north"; + case SOUTH -> "mm.direction.south"; + case UNKNOWN -> unknownIsCentre ? "mm.direction.center" : "mm.direction.unknown"; + case UP -> "mm.direction.up"; + case WEST -> "mm.direction.west"; + }; + } + + public static String getFacingUnlocalizedName(ExtendedFacing facing) { + return "structurelib.facing." + facing.getIndex(); + } + public static boolean areMapsEqual(Map left, Map right) { if (left == null || right == null) return left == right; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/Mods.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/Mods.java index dc76fb7e..62a79291 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/Mods.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/Mods.java @@ -14,7 +14,7 @@ public enum Mods implements IMod, ITargetMod { - AE2Stuff(Names.AE2STUFF), + AE2FluidCraft(Names.AE2_FLUID_CRAFT), Angelica(Names.ANGELICA), AppliedEnergistics2(Names.APPLIED_ENERGISTICS2), ArchitectureCraft(Names.ARCHITECTURE_CRAFT), @@ -45,6 +45,7 @@ protected String getEffectiveModID() { IndustrialCraft2(Names.INDUSTRIAL_CRAFT2), LogisticsPipes(Names.LOGISTICS_PIPES), MatterManipulator(Names.MATTER_MANIPULATOR), + OpenComputers(Names.OPEN_COMPUTERS), Minecraft(Names.MINECRAFT) { @Override @@ -61,7 +62,7 @@ public boolean isModLoaded() { public static class Names { - public static final String AE2STUFF = "ae2stuff"; + public static final String AE2_FLUID_CRAFT = "ae2fc"; public static final String ANGELICA = "angelica"; public static final String APPLIED_ENERGISTICS2 = "appliedenergistics2"; public static final String ARCHITECTURE_CRAFT = "ArchitectureCraft"; @@ -86,6 +87,7 @@ public static class Names { public static final String LOGISTICS_PIPES = "LogisticsPipes"; public static final String MATTER_MANIPULATOR = "matter-manipulator"; public static final String MINECRAFT = "minecraft"; + public static final String OPEN_COMPUTERS = "OpenComputers"; public static final String NEW_HORIZONS_CORE_MOD = "dreamcraft"; public static final String NOT_ENOUGH_ITEMS = "NotEnoughItems"; public static final String STORAGE_DRAWERS = "StorageDrawers"; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/server/BlockStateCommand.java b/src/main/java/com/recursive_pineapple/matter_manipulator/server/BlockStateCommand.java index 5af6227d..e8e9ebcc 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/server/BlockStateCommand.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/server/BlockStateCommand.java @@ -11,7 +11,6 @@ import net.minecraft.command.ICommandSender; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.MovingObjectPosition.MovingObjectType; -import net.minecraft.util.StatCollector; import com.google.gson.Gson; import com.google.gson.JsonElement; @@ -55,7 +54,7 @@ public void processCommand(ICommandSender sender, String[] args) { var hit = MMUtils.getHitResult(player, true); if (hit == null || hit.typeOfHit != MovingObjectType.BLOCK) { - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.must_look_at_block")); + sendErrorToPlayer(player, "mm.info.error.must_look_at_block"); return; } @@ -81,16 +80,16 @@ public void processCommand(ICommandSender sender, String[] args) { return; } - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.property_not_found")); + sendErrorToPlayer(player, "mm.info.error.property_not_found"); } else { if (properties.isEmpty() && intrinsicProps.isEmpty()) { - sendChatToPlayer(player, StatCollector.translateToLocal("mm.info.properties")); - sendChatToPlayer(player, StatCollector.translateToLocal("mm.info.none")); + sendChatToPlayer(player, "mm.info.properties"); + sendChatToPlayer(player, "mm.info.none"); return; } if (!properties.isEmpty()) { - sendChatToPlayer(player, StatCollector.translateToLocal("mm.info.properties")); + sendChatToPlayer(player, "mm.info.properties"); for (var e : properties.entrySet()) { sendChatToPlayer( @@ -101,7 +100,7 @@ public void processCommand(ICommandSender sender, String[] args) { } if (!intrinsicProps.isEmpty()) { - sendChatToPlayer(player, StatCollector.translateToLocal("mm.info.intrinsic_properties")); + sendChatToPlayer(player, "mm.info.intrinsic_properties"); intrinsicProps.forEach((s, intrinsicProperty) -> { sendChatToPlayer( @@ -120,10 +119,8 @@ public void processCommand(ICommandSender sender, String[] args) { } catch (Throwable t) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.error_setting_property", - t.getMessage() - ) + "mm.info.error.error_setting_property", + t.getMessage() ); } @@ -138,17 +135,15 @@ public void processCommand(ICommandSender sender, String[] args) { } catch (Throwable t) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.error_setting_property", - t.getMessage() - ) + "mm.info.error.error_setting_property", + t.getMessage() ); } return; } - sendErrorToPlayer(player, StatCollector.translateToLocal("mm.info.error.property_not_found")); + sendErrorToPlayer(player, "mm.info.error.property_not_found"); } } } diff --git a/src/main/resources/assets/matter-manipulator/lang/en_US.lang b/src/main/resources/assets/matter-manipulator/lang/en_US.lang index f6c6952b..1d93cda6 100644 --- a/src/main/resources/assets/matter-manipulator/lang/en_US.lang +++ b/src/main/resources/assets/matter-manipulator/lang/en_US.lang @@ -95,27 +95,38 @@ mm.gui.add_block=Add Block mm.gui.set_block=Set Block mm.gui.set_block_to_replace_with=Set Block To Replace With mm.gui.set_cable=Set Cable +mm.gui.advanced_options=Advanced Options +mm.gui.wireless_link_hub=Link to External Hubs: %s +mm.gui.smart_copy=Smart Copy +mm.gui.smart_copy.cribs_to_proxies=Auto-Proxy CRIBs: %s +mm.gui.smart_copy.interfaces_to_p2p=Auto-P2P Interfaces: %s +mm.gui.smart_copy.on=ON +mm.gui.smart_copy.off=OFF -# InfoMessage +# InfoMessage, only accept %s in this section mm.info.error.out_of_eu=Matter Manipulator ran out of EU. mm.info.error.could_not_place=Could not place %s remaining blocks. mm.info.finished_placing=Finished placing blocks. mm.info.warning.could_not_find=Could not find item, %d block(s) will be skipped temporarily: +mm.info.warning.of_item= %s x %s mm.info.placed_remaining=Placed %s blocks (%s remaining) -mm.info.warning=Warning at block %d, %d, %d%s: %s -mm.info.error=Error at block %d, %d, %d%s: %s -mm.info.error.could_not_move_block=Could not move block X=%d, Y=%d, Z=%d: %s -mm.info.error.could_not_move_protected_block=Could not move protected block X=%d, Y=%d, Z=%d: %s -mm.info.error.could_not_move_invulnerable_block=Could not move invulnerable source block X=%d, Y=%d, Z=%d: %s -mm.info.error.could_not_move_blocked_block=Destination was blocked for source block X=%d, Y=%d, Z=%d: %s +mm.info.warning.only_message=Warning at block %s, %s, %s: %s +mm.info.warning.with_block=Warning at block %s, %s, %s (%s): %s +mm.info.error.only_message=Error at block %s, %s, %s: %s +mm.info.error.with_block=Error at block %s, %s, %s (%s): %s +mm.info.error.could_not_move_block.new=Could not move block X=%s, Y=%s, Z=%s: %s +mm.info.error.could_not_move_protected_block.new=Could not move protected block X=%s, Y=%s, Z=%s: %s +mm.info.error.could_not_move_invulnerable_block.new=Could not move invulnerable source block X=%s, Y=%s, Z=%s: %s +mm.info.error.could_not_move_blocked_block.new=Destination was blocked for source block X=%s, Y=%s, Z=%s: %s mm.info.process_move=Moved %s blocks (%s remaining) mm.info.finished_move=Finished moving blocks. mm.info.warning.protected_area=Tried to break/place a block in a protected area! mm.info.warning.not_find_container_for_fluid=Could not find a container for fluid (it was voided): +mm.info.warning.of_fluid= %sL of %s mm.info.error.could_not_push_items_to_uplink=Could not push items to uplink: -mm.info.error.could_not_push_fluids_to_uplink=Could not push fluids to uplink: -mm.info.error.could_not_request_items_from_uplink=Could not request items from uplink: +mm.info.error.could_not_push_fluids_to_uplink=Could not push fluids to uplink: %s +mm.info.error.could_not_request_items_from_uplink=Could not request items from uplink: %s mm.info.added=Added %s to %s mm.info.set=Set %s to: %s mm.info.set_block_to_replace_with=Set block to replace with to: %s @@ -143,6 +154,63 @@ mm.info.intrinsic_properties=Intrinsic Properties: mm.info.none=None mm.info.error.error_setting_property=Error setting property: %s mm.info.error.move_overlapping=Cannot move when the cut and paste regions overlap. +mm.info.warning.could_not_extract_item=Could not extract item: %s +mm.info.warning.could_not_find_item=Could not find %s +mm.info.warning.could_not_find_cladding=Could not find cladding: %s +mm.info.warning.inventory_was_the_wrong_size=Inventory was the wrong size (expected %s, was %s) +mm.info.warning.could_not_extract_item_in_slot=Could not extract item in slot %s: %s +mm.info.warning.invalid_item_for_slot=Invalid item for slot %s: %s +mm.info.warning.could_not_gather_item_for_inventory=Could not gather item for inventory: %s +mm.info.warning.could_not_find_upgrade=Could not find upgrade: %s x %s +mm.info.error.invalid_location=Invalid location (%s) for part (%s) +mm.info.error.could_not_set_direction_to=Could not set direction to '%s' +mm.info.error.was_not_allowed_to_put_cover_on=Was not allowed to put cover on %s side: %s +mm.info.error.could_not_find_cover=Could not find cover: %s +mm.info.error.could_not_apply_property=Could not apply property %s: %s +mm.info.error.qc_rack_is_too_hot=QC Rack is too hot to extract or insert items +mm.info.error.cannot_extract_while_qc_on=Cannot extract or insert items from/into QC Rack while the QC is on +mm.info.error.too_many_upgrades=Tried to install too many upgrades: voiding the rest (this is a bug, please report it) +mm.info.error.could_not_place_blocks=Could not place blocks due to a crash. Check the logs for more info. +mm.info.missing=%s%s: %s%s%s (%s%s%s missing) + +mm.direction.down=Down +mm.direction.east=East +mm.direction.north=North +mm.direction.south=South +mm.direction.center=Center +mm.direction.unknown=Unknow +mm.direction.up=Up +mm.direction.west=West + +mm.enum.copyable.property.facing=facing +mm.enum.copyable.property.forward=forward +mm.enum.copyable.property.up=up +mm.enum.copyable.property.left=left +mm.enum.copyable.property.top=top +mm.enum.copyable.property.rotation=rotation +mm.enum.copyable.property.mode=mode +mm.enum.copyable.property.text=text +mm.enum.copyable.property.orientation=orientation +mm.enum.copyable.property.delay=delay +mm.enum.copyable.property.inverted=inverted +mm.enum.copyable.property.color=color +mm.enum.copyable.property.rotation_state=rotation_state + +mm.enum.uplink_status.ok=Ok +mm.enum.uplink_status.no_plasma=Insufficient plasma +mm.enum.uplink_status.ae_offline=Could not connect to the ME system +mm.enum.uplink_status.no_hatch=Missing ME hatch + +mm.enum.what.corners=corners +mm.enum.what.edges=edges +mm.enum.what.faces=faces +mm.enum.what.volumes=volumes +mm.enum.what.all=all blocks + +mm.chat.item.details.imprint=Imprint: %s +mm.chat.item.details.imprint.none=None +mm.chat.item.details.cache_capacity=Cache Capacity: %s +mm.chat.item.details.hub=Hub # Transform mm.transform.info=Flip: %s\nUp: %s\nForward: %s @@ -178,3 +246,60 @@ building=Building # NEI mm.recipe.upgrades=Manipulator Upgrades + +# tooltip +mm.tooltip.none=None +mm.tooltip.hold_shift=Hold shift for more information. +mm.tooltip.me_conn.can_interact=Has an ME connection. (Can interact currently) +mm.tooltip.me_conn.cannot_interact=Has an ME connection. (Cannot interact currently) +mm.tooltip.me_conn.no_conn=Does not have an ME connection. +mm.tooltip.uplink_conn.can_interact=Has an Uplink connection. (Can interact currently) +mm.tooltip.uplink_conn.cannot_interact=Has an Uplink connection. (Cannot interact currently) +mm.tooltip.uplink_conn.address=Uplink address: %s +mm.tooltip.uplink_conn.no_conn=Does not have an Uplink connection. +mm.tooltip.pending_action=Pending Action: %s +mm.tooltip.pending_action.moving_coords=Moving coordinates +mm.tooltip.pending_action.geom_selecting_block=Selecting blocks to place +mm.tooltip.pending_action.mark_copy_a=Marking first copy corner +mm.tooltip.pending_action.mark_copy_b=Marking second copy corner +mm.tooltip.pending_action.mark_cut_a=Marking first cut corner +mm.tooltip.pending_action.mark_cut_b=Marking second cut corner +mm.tooltip.pending_action.mark_paste=Marking paste location +mm.tooltip.pending_action.exch_add_replace=Adding block to replace whitelist +mm.tooltip.pending_action.exch_set_replace=Setting block in replace whitelist +mm.tooltip.pending_action.exch_set_target=Setting block to replace with +mm.tooltip.pending_action.pick_cable=Picking cable +mm.tooltip.pending_action.mark_array=Marking array bounds +mm.tooltip.mode=Mode: %s +mm.tooltip.mode.geometry=Geometry +mm.tooltip.mode.moving=Moving +mm.tooltip.mode.copying=Copying +mm.tooltip.mode.exchanging=Exchanging +mm.tooltip.mode.cables=Cables +mm.tooltip.removing=Removing: %s +mm.tooltip.removing.all=All blocks +mm.tooltip.removing.replaceable=Replaceable blocks +mm.tooltip.removing.none=No blocks +mm.tooltip.shape=Shape: %s +mm.tooltip.shape.line=Line +mm.tooltip.shape.cube=Cube +mm.tooltip.shape.sphere=Sphere +mm.tooltip.shape.cylinder=Cylinder +mm.tooltip.coord_a=Coordinate A: %s +mm.tooltip.coord_b=Coordinate B: %s +mm.tooltip.corner_block=Corner block: %s +mm.tooltip.edge_block=Edge block: %s +mm.tooltip.face_block=Face block: %s +mm.tooltip.volume_block=Volume block: %s +mm.tooltip.copying.copy_a=Copy Coordinate A: %s +mm.tooltip.copying.copy_b=Copy Coordinate B: %s +mm.tooltip.copying.stack=Stack: %s +mm.tooltip.copying.wireless_link=Wireless Links: %s +mm.tooltip.copying.auto_proxy_cribs=Auto-Proxy CRIBs: %s +mm.tooltip.copying.auto_p2p_interfaces=Auto-P2P Interfaces: %s +mm.tooltip.paste=Paste Coordinate: %s +mm.tooltip.moving.cut_a=Cut Coordinate A: %s +mm.tooltip.moving.cut_b=Cut Coordinate B: %s +mm.tooltip.exchanging.removable=Removable blocks: %s +mm.tooltip.exchanging.replacing=Replacing blocks with: %s +mm.tooltip.cable=Cable: %s diff --git a/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_ACTIVE_GLOW.png b/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_ACTIVE_GLOW.png index cbdd7af8..999a896a 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_ACTIVE_GLOW.png and b/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_ACTIVE_GLOW.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_OFF.png b/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_OFF.png index 5e135d51..bae192a3 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_OFF.png and b/src/main/resources/assets/matter-manipulator/textures/blocks/machines/uplink/OVERLAY_FRONT_OFF.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator0.png b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator0.png index f68c80ab..f48b0cf0 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator0.png and b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator0.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator1.png b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator1.png index 24ff07b9..c5c09240 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator1.png and b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator1.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator2.png b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator2.png index c48ec1d3..1c01b673 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator2.png and b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator2.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator3.png b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator3.png index 70db49f9..2641eac7 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator3.png and b/src/main/resources/assets/matter-manipulator/textures/items/itemMatterManipulator3.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/0.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/0.png index ab0c190b..f14dc8ac 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/0.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/0.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/1.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/1.png index b3a48900..8160fde9 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/1.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/1.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/10.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/10.png index 20888e57..40e5ff80 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/10.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/10.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/11.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/11.png index 1c83e4a5..8f01a818 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/11.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/11.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/12.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/12.png index 0fcf2447..79ab8467 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/12.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/12.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/13.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/13.png index 5ae6573e..159987bc 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/13.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/13.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/14.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/14.png index 3b5d0483..ebc586e9 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/14.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/14.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/15.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/15.png index 1d538886..0377986e 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/15.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/15.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/16.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/16.png index 9139da9c..4ab70b29 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/16.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/16.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/17.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/17.png index fccdf9ab..65bacce3 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/17.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/17.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/18.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/18.png index 73d31f1f..723cdc3d 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/18.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/18.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/19.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/19.png index 8afe60c7..986c5a6c 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/19.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/19.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/2.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/2.png index 2199c31a..726e678f 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/2.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/2.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/20.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/20.png index ad619d93..f5c465b5 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/20.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/20.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/21.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/21.png index d19bdd4b..378c4b9c 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/21.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/21.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/22.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/22.png index 60b78f45..882398eb 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/22.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/22.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/23.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/23.png index a35f0ffd..69d44906 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/23.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/23.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/24.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/24.png index e7d34cfd..95e58b89 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/24.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/24.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/3.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/3.png index 78d8f58e..5728a9df 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/3.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/3.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/4.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/4.png index 5522a07f..66129755 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/4.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/4.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/5.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/5.png index 95d703a8..31f18a04 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/5.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/5.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/6.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/6.png index 68fbde00..f2ab6282 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/6.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/6.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/7.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/7.png index abad19aa..0466f0b8 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/7.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/7.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/8.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/8.png index 7c58ad6d..0babe00b 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/8.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/8.png differ diff --git a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/9.png b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/9.png index b0e864c7..e0c2ee9b 100644 Binary files a/src/main/resources/assets/matter-manipulator/textures/items/metaitem/9.png and b/src/main/resources/assets/matter-manipulator/textures/items/metaitem/9.png differ