From 0036c1631a1a33a1326f6f70150f22c42ba5dd95 Mon Sep 17 00:00:00 2001 From: Connor-Colenso <52056774+Connor-Colenso@users.noreply.github.com> Date: Sat, 24 Jan 2026 02:50:37 +0000 Subject: [PATCH 01/43] Format numbers migration (#20) Co-authored-by: Connor Colenso --- dependencies.gradle | 44 +++++++++---------- .../common/building/MMInventory.java | 3 +- .../manipulator/ItemMatterManipulator.java | 8 ++-- .../common/utils/MMUtils.java | 13 ------ 4 files changed, 28 insertions(+), 40 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index fa483a52..6721ba3f 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -37,52 +37,52 @@ def asmVersion = "9.7" dependencies { - implementation("com.github.GTNewHorizons:StructureLib:1.4.24:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.8.32:dev") + implementation("com.github.GTNewHorizons:StructureLib:1.4.28:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.1:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.23-1.7.10:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.25-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.9.6: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:Applied-Energistics-2-Unofficial:rv3-beta-793-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.5") { 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("com.github.GTNewHorizons:BloodMagic:1.8.9: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("com.github.GTNewHorizons:EnderIO:2.10.15:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.2: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("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.223:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.48: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("com.github.GTNewHorizons:NotEnoughItems:2.8.55-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.12-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("com.github.GTNewHorizons:waila:1.19.20: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:ArchitectureCraft:1.12.5") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.0.0-alpha9:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-793-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('com.github.GTNewHorizons:EnderIO:2.10.15:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.2: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:GT5-Unofficial:5.09.52.223: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("com.github.GTNewHorizons:Hodgepodge:2.7.48:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.40-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.55-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.0:dev") @@ -105,7 +105,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.6-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); 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..cb334e47 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; @@ -237,7 +238,7 @@ protected void actuallyGivePlayerStuff() { 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, String.format(" %s of %s", formatFluid(stack.amount), stack.getFluidStack().getLocalizedName())); } } 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..215ffea9 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; @@ -593,9 +593,9 @@ 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 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..f759f5df 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; @@ -454,18 +453,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; } From 393b47801cc3f17d0114b244d3468e9e89f6946b Mon Sep 17 00:00:00 2001 From: Discreater Date: Thu, 5 Feb 2026 04:32:01 +0800 Subject: [PATCH 02/43] Localize tooltips (#18) Co-authored-by: Martin Robertz --- .../common/building/BlockSpec.java | 4 + .../manipulator/ItemMatterManipulator.java | 138 +++++++++--------- .../assets/matter-manipulator/lang/en_US.lang | 54 +++++++ 3 files changed, 128 insertions(+), 68 deletions(-) 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..dd7b3e7f 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 @@ -376,6 +376,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/items/manipulator/ItemMatterManipulator.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java index 215ffea9..83d87640 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 @@ -441,7 +441,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,88 +468,88 @@ 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", @@ -559,22 +559,22 @@ public void addInformation( } 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()); @@ -601,22 +601,24 @@ public void addInformation( // 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 + ) ); } } 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..1ddb87c3 100644 --- a/src/main/resources/assets/matter-manipulator/lang/en_US.lang +++ b/src/main/resources/assets/matter-manipulator/lang/en_US.lang @@ -178,3 +178,57 @@ 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.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 From 45e39cdd3be5fe25d8b4a09b2bf9972df247a5f6 Mon Sep 17 00:00:00 2001 From: hinyb <40139991+hinyb@users.noreply.github.com> Date: Mon, 9 Feb 2026 14:44:42 -0500 Subject: [PATCH 03/43] feat: support MEOutput cacheMode and checkMode. (#22) Co-authored-by: Martin Robertz --- dependencies.gradle | 44 +++++++++--------- gradle.properties | 33 +++++++++++-- gradle/gradle-daemon-jvm.properties | 12 +++++ gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45633 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 5 +- gradlew.bat | 3 +- settings.gradle | 2 +- .../common/building/AbstractBuildable.java | 22 +++------ .../common/compat/BlockPropertyRegistry.java | 4 +- .../common/utils/InventoryAdapter.java | 4 +- 11 files changed, 78 insertions(+), 53 deletions(-) create mode 100644 gradle/gradle-daemon-jvm.properties diff --git a/dependencies.gradle b/dependencies.gradle index 6721ba3f..06c6c493 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -39,50 +39,50 @@ def asmVersion = "9.7" dependencies { implementation("com.github.GTNewHorizons:StructureLib:1.4.28:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.1:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.25-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.9.6:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.39-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.9.13:dev") - compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.2-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-793-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.5") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.78:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.11-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-819-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.7") { transitive = false } + compileOnly("com.github.GTNewHorizons:Avaritia:1.85: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.9:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.12:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.15:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.18:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.2: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.223:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.48:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.272:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.58: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.55-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.12-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.63-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.16-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.19.20:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:waila:1.19.22: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.5") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.0.0-alpha9:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-793-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.78:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.11-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.7") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.0.0-alpha20:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-819-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.85: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.15:dev') +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.18:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.2:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.223:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.272: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.48:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.58:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.55-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.63-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.0:dev") 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..b4265c7a --- /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/4945f00643ec68e7c7a6b66f90124f89/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/46949723aaa20c7b64d7ecfed7207034/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d6690dfd71c4c91e08577437b5b2beb0/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3cd7045fca9a72cd9bc7d14a385e594c/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/redirect +toolchainVersion=25 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..f8e1ee3125fe0768e9a76ee977ac089eb657005e 100644 GIT binary patch delta 37442 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

WNp5~7YD4``2J+2^JLXSDAn1#GQa=Wm~d2R({ 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 delta 35599 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 ORzXA2f%7X@nSTIfr%7r6 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da4..23449a2b 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.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a93..adff685a 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. @@ -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..335f37c4 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.19' } 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..bb27fa08 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; @@ -34,8 +33,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 +46,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; @@ -227,23 +225,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 +241,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))); 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..805f963d 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 @@ -50,8 +50,8 @@ 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.util.ReadableNumberConverter; 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..5a6d5ff2 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 @@ -13,8 +13,8 @@ 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.outputme.MTEHatchOutputBusME; +import gregtech.common.tileentities.machines.outputme.MTEHatchOutputME; import com.google.common.collect.ImmutableList; import com.recursive_pineapple.matter_manipulator.asm.Optional; From 76dfb76922c3145870e922222bd6f405c43ca17b Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Mon, 9 Feb 2026 20:59:37 +0100 Subject: [PATCH 04/43] update deps --- dependencies.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dependencies.gradle b/dependencies.gradle index 06c6c493..313a8c7e 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -97,6 +97,8 @@ 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') } // deps may transitively add Baubles, so we replace it From fe514200da59b5d46c33e044f710ac3038194efc Mon Sep 17 00:00:00 2001 From: boubou19 Date: Tue, 10 Feb 2026 16:53:53 +0100 Subject: [PATCH 05/43] Fix compat with CAL (#21) Co-authored-by: Martin Robertz --- dependencies.gradle | 2 +- .../common/compat/BlockPropertyRegistry.java | 32 ++++++++----------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 313a8c7e..0fc02b35 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -55,7 +55,7 @@ dependencies { 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.272:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.279:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.58:dev") { transitive = false } compileOnly("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") { transitive = false } compileOnly("curse.maven:mekanism-268560:2475797") { transitive = false } 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 805f963d..34abce09 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 @@ -69,6 +69,7 @@ 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; @@ -1411,13 +1412,6 @@ public void getItemDetails(List details, JsonElement value) { 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 +1423,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 +1437,31 @@ 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; + 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(String.format("Imprint: %s", imprint == null ? "None" : imprint.imprint.get(1).getDisplayName())); } } From 182be96bf4f62576149ad53583085805fbdac7b3 Mon Sep 17 00:00:00 2001 From: danyadev Date: Sat, 7 Mar 2026 18:36:15 +0300 Subject: [PATCH 06/43] optimize DeMemberator (#24) --- .../matter_manipulator/asm/DeMemberator.java | 2 ++ 1 file changed, 2 insertions(+) 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); From 87655a4c2e2283ee3bb9e4cb197a6bbed43c534a Mon Sep 17 00:00:00 2001 From: boubou19 Date: Sat, 14 Mar 2026 18:23:41 +0100 Subject: [PATCH 07/43] bump GT to account for https://github.com/GTNewHorizons/GT5-Unofficial/pull/5781 (#25) --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 0fc02b35..ab6e903b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -55,7 +55,7 @@ dependencies { 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.279:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.360:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.58:dev") { transitive = false } compileOnly("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") { transitive = false } compileOnly("curse.maven:mekanism-268560:2475797") { transitive = false } From a042f2f2573de8d4a1202c8f1b322d983c2aea45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9a=20Gris?= Date: Sat, 14 Mar 2026 19:42:29 +0100 Subject: [PATCH 08/43] Impr(Textures): Adapt GT5U OptionalResource (#26) --- .../matter_manipulator/client/Textures.java | 25 +++++++++++++++++++ .../common/uplink/MTEMMUplink.java | 20 ++++----------- 2 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java b/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java new file mode 100644 index 00000000..5216da39 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java @@ -0,0 +1,25 @@ +package com.recursive_pineapple.matter_manipulator.client; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +import gregtech.api.interfaces.IIconContainer; + +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; + +@SideOnly(Side.CLIENT) +public class Textures { + + public static class MTEMMUplink { + + public static final IIconContainer ACTIVE_GLOW = gregtech.api.enums.Textures.BlockIcons.customOptional( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_ACTIVE_GLOW") + ); + public static final IIconContainer IDLE_GLOW = gregtech.api.enums.Textures.BlockIcons.customOptional( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_IDLE_GLOW") + ); + public static final IIconContainer OFF = gregtech.api.enums.Textures.BlockIcons.custom( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_OFF") + ); + } +} 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..1d472fde 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 @@ -34,7 +34,6 @@ 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.ITexture; @@ -70,6 +69,7 @@ import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; import com.gtnewhorizon.structurelib.structure.IStructureDefinition; import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +import com.recursive_pineapple.matter_manipulator.client.Textures; import com.recursive_pineapple.matter_manipulator.common.building.IPseudoInventory; import com.recursive_pineapple.matter_manipulator.common.items.MMItemList; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.ItemMatterManipulator; @@ -79,7 +79,6 @@ import com.recursive_pineapple.matter_manipulator.common.utils.BigFluidStack; import com.recursive_pineapple.matter_manipulator.common.utils.BigItemStack; import com.recursive_pineapple.matter_manipulator.common.utils.MMUtils; -import com.recursive_pineapple.matter_manipulator.common.utils.Mods; import it.unimi.dsi.fastutil.Pair; import mcp.mobius.waila.api.IWailaConfigHandler; @@ -259,17 +258,8 @@ protected MultiblockTooltipBuilder createTooltip() { return tt; } - private static final CustomIcon ACTIVE_GLOW = new CustomIcon( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_ACTIVE_GLOW") - ); - private static final CustomIcon IDLE_GLOW = new CustomIcon( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_IDLE_GLOW") - ); - private static final CustomIcon OFF = new CustomIcon( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_OFF") - ); - @Override + @SideOnly(Side.CLIENT) public ITexture[] getTexture( IGregTechTileEntity baseMetaTileEntity, ForgeDirection side, @@ -285,7 +275,7 @@ public ITexture[] getTexture( if (side == facing) { textures.add( TextureFactory.builder() - .addIcon(OFF) + .addIcon(Textures.MTEMMUplink.OFF) .extFacing() .build() ); @@ -297,7 +287,7 @@ public ITexture[] getTexture( case IDLE: { textures.add( TextureFactory.builder() - .addIcon(IDLE_GLOW) + .addIcon(Textures.MTEMMUplink.IDLE_GLOW) .extFacing() .glow() .build() @@ -307,7 +297,7 @@ public ITexture[] getTexture( case ACTIVE: { textures.add( TextureFactory.builder() - .addIcon(ACTIVE_GLOW) + .addIcon(Textures.MTEMMUplink.ACTIVE_GLOW) .extFacing() .glow() .build() From 6662248da781bce97f638823844e1e26c9bc820c Mon Sep 17 00:00:00 2001 From: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:58:02 -0400 Subject: [PATCH 09/43] Fix Texture Render Crash (#27) --- dependencies.gradle | 2 +- .../matter_manipulator/client/Textures.java | 25 ------------------- .../common/uplink/MTEMMUplink.java | 23 ++++++++++++++--- 3 files changed, 20 insertions(+), 30 deletions(-) delete mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java diff --git a/dependencies.gradle b/dependencies.gradle index ab6e903b..840a6b4c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -77,7 +77,7 @@ dependencies { // runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.18:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.2:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.272:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.360: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.58:dev") diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java b/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java deleted file mode 100644 index 5216da39..00000000 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/client/Textures.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.recursive_pineapple.matter_manipulator.client; - -import cpw.mods.fml.relauncher.Side; -import cpw.mods.fml.relauncher.SideOnly; - -import gregtech.api.interfaces.IIconContainer; - -import com.recursive_pineapple.matter_manipulator.common.utils.Mods; - -@SideOnly(Side.CLIENT) -public class Textures { - - public static class MTEMMUplink { - - public static final IIconContainer ACTIVE_GLOW = gregtech.api.enums.Textures.BlockIcons.customOptional( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_ACTIVE_GLOW") - ); - public static final IIconContainer IDLE_GLOW = gregtech.api.enums.Textures.BlockIcons.customOptional( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_IDLE_GLOW") - ); - public static final IIconContainer OFF = gregtech.api.enums.Textures.BlockIcons.custom( - Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_OFF") - ); - } -} 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 1d472fde..137c1ca2 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 @@ -8,6 +8,8 @@ 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; @@ -36,6 +38,7 @@ import gregtech.api.enums.StructureError; 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; @@ -69,7 +72,6 @@ import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; import com.gtnewhorizon.structurelib.structure.IStructureDefinition; import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; -import com.recursive_pineapple.matter_manipulator.client.Textures; import com.recursive_pineapple.matter_manipulator.common.building.IPseudoInventory; import com.recursive_pineapple.matter_manipulator.common.items.MMItemList; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.ItemMatterManipulator; @@ -79,6 +81,7 @@ import com.recursive_pineapple.matter_manipulator.common.utils.BigFluidStack; import com.recursive_pineapple.matter_manipulator.common.utils.BigItemStack; import com.recursive_pineapple.matter_manipulator.common.utils.MMUtils; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; import it.unimi.dsi.fastutil.Pair; import mcp.mobius.waila.api.IWailaConfigHandler; @@ -258,6 +261,18 @@ protected MultiblockTooltipBuilder createTooltip() { return tt; } + private static final IIconContainer ACTIVE_GLOW = customOptional( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_ACTIVE_GLOW") + ); + + private static final IIconContainer IDLE_GLOW = customOptional( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_IDLE_GLOW") + ); + + private static final IIconContainer OFF = custom( + Mods.MatterManipulator.getResourcePath("machines", "uplink", "OVERLAY_FRONT_OFF") + ); + @Override @SideOnly(Side.CLIENT) public ITexture[] getTexture( @@ -275,7 +290,7 @@ public ITexture[] getTexture( if (side == facing) { textures.add( TextureFactory.builder() - .addIcon(Textures.MTEMMUplink.OFF) + .addIcon(OFF) .extFacing() .build() ); @@ -287,7 +302,7 @@ public ITexture[] getTexture( case IDLE: { textures.add( TextureFactory.builder() - .addIcon(Textures.MTEMMUplink.IDLE_GLOW) + .addIcon(IDLE_GLOW) .extFacing() .glow() .build() @@ -297,7 +312,7 @@ public ITexture[] getTexture( case ACTIVE: { textures.add( TextureFactory.builder() - .addIcon(Textures.MTEMMUplink.ACTIVE_GLOW) + .addIcon(ACTIVE_GLOW) .extFacing() .glow() .build() From b902349ba18c01bc3e3702bd0a45cf9aa3dd6505 Mon Sep 17 00:00:00 2001 From: LazyFleshWasTaken <40587985+LazyFleshWasTaken@users.noreply.github.com> Date: Sun, 22 Mar 2026 11:18:49 -0700 Subject: [PATCH 10/43] fix crash post-IOF rework GT5u (#28) --- .../matter_manipulator/common/building/GTAnalysisResult.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..7f7285d6 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 @@ -267,7 +267,7 @@ public GTAnalysisResult(IGregTechTileEntity igte) { } private static final MethodHandle GET_IOF_MODE = MMUtils - .exposeFieldGetter(MTEIntegratedOreFactory.class, "sMode"); + .exposeFieldGetter(MTEIntegratedOreFactory.class, "mode"); @SneakyThrows private static int getIOFMode(MTEIntegratedOreFactory cal) { @@ -275,7 +275,7 @@ private static int getIOFMode(MTEIntegratedOreFactory cal) { } private static final MethodHandle SET_IOF_MODE = MMUtils - .exposeFieldSetter(MTEIntegratedOreFactory.class, "sMode"); + .exposeFieldSetter(MTEIntegratedOreFactory.class, "mode"); @SneakyThrows private static void setIOFMode(MTEIntegratedOreFactory cal, int mode) { From b81f63489cc25a90eb39ffe86ba0bb1dd3c6c60f Mon Sep 17 00:00:00 2001 From: Alexdoru <57050655+Alexdoru@users.noreply.github.com> Date: Tue, 24 Mar 2026 09:56:02 +0100 Subject: [PATCH 11/43] use public getter/setter for integrated ore factory (#29) --- .../common/building/GTAnalysisResult.java | 22 ++----------------- 1 file changed, 2 insertions(+), 20 deletions(-) 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 7f7285d6..58f774e7 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; @@ -49,7 +48,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; @@ -215,7 +213,7 @@ public GTAnalysisResult(IGregTechTileEntity igte) { // 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; } @@ -266,22 +264,6 @@ public GTAnalysisResult(IGregTechTileEntity igte) { } } - private static final MethodHandle GET_IOF_MODE = MMUtils - .exposeFieldGetter(MTEIntegratedOreFactory.class, "mode"); - - @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, "mode"); - - @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(); @@ -433,7 +415,7 @@ public boolean apply(IBlockApplyContext ctx) { // 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; } From 77a4ae0efd490e3494085bf09725233d3cfab827 Mon Sep 17 00:00:00 2001 From: iouter <62897714+iouter@users.noreply.github.com> Date: Sat, 28 Mar 2026 11:02:05 +0800 Subject: [PATCH 12/43] [Sub-PR] Reduce the size of the GregTech.lang, and standardize the Line Break, etc. (#30) --- dependencies.gradle | 2 +- .../matter_manipulator/common/uplink/MTEMMUplink.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 840a6b4c..231859f6 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -55,7 +55,7 @@ dependencies { 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.360:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.391:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.58:dev") { transitive = false } compileOnly("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") { transitive = false } compileOnly("curse.maven:mekanism-268560:2475797") { transitive = false } 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 137c1ca2..5202cb7b 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,7 +3,7 @@ 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; From afd338f562dfc18fd2e87762e19ef1b1232aea18 Mon Sep 17 00:00:00 2001 From: Martin Robertz Date: Fri, 3 Apr 2026 22:59:43 +0200 Subject: [PATCH 13/43] update+gradle+bs+deps --- dependencies.gradle | 62 +++++++++++------------ gradle/gradle-daemon-jvm.properties | 20 ++++---- gradle/wrapper/gradle-wrapper.jar | Bin 45633 -> 48966 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- settings.gradle | 2 +- 6 files changed, 44 insertions(+), 44 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 231859f6..9ba94b49 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -37,52 +37,52 @@ def asmVersion = "9.7" dependencies { - implementation("com.github.GTNewHorizons:StructureLib:1.4.28:dev") - implementation("com.github.GTNewHorizons:ModularUI:1.3.1:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.39-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.9.13:dev") + implementation("com.github.GTNewHorizons:StructureLib:1.4.34:dev") + implementation("com.github.GTNewHorizons:ModularUI:1.3.2:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.51-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.9.47:dev") - compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.11-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-819-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.7") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.85:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.3-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.17-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") { transitive = false } + compileOnly("com.github.GTNewHorizons:Avaritia:1.93:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.12:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.14:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.18:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.2:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.22:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3: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.391:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.58:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.3:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.399:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.116: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.63-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.16-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.22-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:waila:1.19.22: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.11-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.7") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.0.0-alpha20:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-819-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.85: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.18:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.2:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.17-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.15:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.93:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.22:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.360:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.399: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.58:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.3:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.116:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.63-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.0:dev") @@ -107,7 +107,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.6-GTNH")) + .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.11-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index b4265c7a..991a69c7 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,12 +1,12 @@ #This file is generated by updateDaemonJvm -toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/4945f00643ec68e7c7a6b66f90124f89/redirect -toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/b41931cf1e70bc8e08d7dd19c343ef00/redirect -toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect -toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect -toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/46949723aaa20c7b64d7ecfed7207034/redirect -toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/d6690dfd71c4c91e08577437b5b2beb0/redirect -toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/df211d3c3eefdc408b462041881bc575/redirect -toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/93aeea858331bd6bb00ba94759830234/redirect -toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/3cd7045fca9a72cd9bc7d14a385e594c/redirect -toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/552c7bffe0370c66410a51c55985b511/redirect +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect toolchainVersion=25 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f8e1ee3125fe0768e9a76ee977ac089eb657005e..d997cfc60f4cff0e7451d19d49a82fa986695d07 100644 GIT binary patch delta 39855 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 N2q(zEpY4qk{{xkq;s5{u diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23449a2b..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-9.2.1-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 adff685a..0262dcbd 100755 --- a/gradlew +++ b/gradlew @@ -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/. diff --git a/settings.gradle b/settings.gradle index 335f37c4..ea29dace 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,5 @@ pluginManagement { } plugins { - id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.19' + id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24' } From 31e632ad268386775f029e80ee13c968d8f2e9f6 Mon Sep 17 00:00:00 2001 From: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com> Date: Fri, 3 Apr 2026 17:00:08 -0400 Subject: [PATCH 14/43] Optimize Images & Workflow (#32) --- .github/workflows/optimize-images.yml | 17 +++++++++++++++++ .../uplink/OVERLAY_FRONT_ACTIVE_GLOW.png | Bin 755 -> 722 bytes .../machines/uplink/OVERLAY_FRONT_OFF.png | Bin 719 -> 714 bytes .../textures/items/itemMatterManipulator0.png | Bin 927 -> 774 bytes .../textures/items/itemMatterManipulator1.png | Bin 877 -> 715 bytes .../textures/items/itemMatterManipulator2.png | Bin 881 -> 714 bytes .../textures/items/itemMatterManipulator3.png | Bin 834 -> 705 bytes .../textures/items/metaitem/0.png | Bin 845 -> 704 bytes .../textures/items/metaitem/1.png | Bin 820 -> 709 bytes .../textures/items/metaitem/10.png | Bin 813 -> 688 bytes .../textures/items/metaitem/11.png | Bin 819 -> 708 bytes .../textures/items/metaitem/12.png | Bin 834 -> 695 bytes .../textures/items/metaitem/13.png | Bin 908 -> 737 bytes .../textures/items/metaitem/14.png | Bin 726 -> 652 bytes .../textures/items/metaitem/15.png | Bin 820 -> 688 bytes .../textures/items/metaitem/16.png | Bin 824 -> 709 bytes .../textures/items/metaitem/17.png | Bin 846 -> 704 bytes .../textures/items/metaitem/18.png | Bin 882 -> 739 bytes .../textures/items/metaitem/19.png | Bin 710 -> 652 bytes .../textures/items/metaitem/2.png | Bin 789 -> 726 bytes .../textures/items/metaitem/20.png | Bin 818 -> 688 bytes .../textures/items/metaitem/21.png | Bin 859 -> 725 bytes .../textures/items/metaitem/22.png | Bin 1117 -> 922 bytes .../textures/items/metaitem/23.png | Bin 374 -> 268 bytes .../textures/items/metaitem/24.png | Bin 245 -> 238 bytes .../textures/items/metaitem/3.png | Bin 937 -> 737 bytes .../textures/items/metaitem/4.png | Bin 714 -> 653 bytes .../textures/items/metaitem/5.png | Bin 818 -> 688 bytes .../textures/items/metaitem/6.png | Bin 828 -> 709 bytes .../textures/items/metaitem/7.png | Bin 858 -> 701 bytes .../textures/items/metaitem/8.png | Bin 919 -> 737 bytes .../textures/items/metaitem/9.png | Bin 734 -> 652 bytes 32 files changed, 17 insertions(+) create mode 100644 .github/workflows/optimize-images.yml 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/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 cbdd7af8114e2b09720af15e4ca0544f06aad517..999a896a6eed1ca78d666285b019e8eac6473710 100644 GIT binary patch delta 208 zcmV;>05AXZ1=0nuv;mXG0Zo&%0TO?zNklc71G$i(iCiD33Hk%M;6GXFz&Bio;=5)c6u+Jy(`E zF>>E8z=B}`3(l%_`3wmSGe3X@0SnDF(r+=TX2H&#k%xX^v6JYqXOd>1`5fQ_%nx8e zz(VskmWe$P4O1V$f(K8uo1rj6pgw?wrT)pHU+AD0rezku09VKD_z6h_f~@4UZMAPb{{Fq){K zg9=6lIo|!Yb4YN8wfL(=d3b(Ngiu65f)NdgoCI_5dblb{_)pVQRRDI!lZ`6rl${Qw$3C^3Q> z51jCzxKTZzjDj|Wl!^PKxTxxk4vWO&A$GwGMj2c;dS;kNe00*J0&H-H6R;=Q=^20#D8p>VPM*{Y5zeOry(E%uxYkb)BX>_$gaU^2*?1C zB)aAeAO8Q3_DuWF&%pklBm+R2XP$lgzqxV&h9Niu71)n5#;Qt7BPy8_gG6ZG-%n-2V8w_BBM0>P}GXxg*FhfAj z2WbWwqITbdxO4%-FvCEGfHZ?#NYDU$27xe4jGzIy3?o@HJ^%o{;p#D8v;hwWVoOIv0R8~{0R3^5QC5?&0Z)GccS%G+R5;76ld(&~Kp4f} zl|sjYqNE)hEHp!AhbUbnr4(uhV{s9bPPT_oUBpSU2tg54a#Vyu2klf>m*^mf;93J# zv_l68h5ilabfpbRDCnE+-tql-ACCk6DB?9VP2=Nt+qOvx@R zz*D0^#)l8z-m#ckK^(Eb_z^!-O)Gq-^^A_$M5%R+LVg=@#JFWy#M@K^09@B4?RFcK z($Rx+d4>6nHFV4-PU=a2FnGov;mXG0Zfy#0TX|tNkl`r2GKAvWP|WJ8=GbrXsGMJZwN>No94p&ivJ)CG63cptcHLDL6Yd2{p5H3 zfAr!l*btHoK-TP`S53MhAjQai~v;hwWVoOIv0R8~{0R3^5QC5?&0Z)GcMM*?KR5;6HV4x5%q6=th zI{ruIU%$Sekt74=&p-PA&70Q@OP9=GU|?Wi`1$K6UW1shX%-O?Ww>y02jhi{I~dv7 z*csT_*fCs&Vt}Tm<9}q$iHX*5+b&$(!5HB0fYT5b7z3N;!u*Q=$Oakcd;Hhbbo`I( z9k>^kE}4IUq519xCx*hxQihKoKj4oFO-;xD2KpZVkv03t@B07f#oPZLde#3Qy?FZ{ z<{FqQ@ETH>UxA_-*${LCn9vPssO$L8&o98x-`>qIYgZ9Nnp+>ktX)M6IT2+Hj-IZh zd*#uKxBvYdlK*eovG;%N)c^lC?bwUr3KsNKhiqL)|HKYP1_p-zDHTx+r|&v3m@4mM znA2y9KP4h3XLdGrxL8wXDZ`vTQ-((`!{90O`t|*cjD)-dV`r2GKAvWP|WJ8=Gbr2zB+xZwN>Nn`R#yr~e=fG63cptcHLDL6Yd2x44J> z|M>L_*btHoK-PR~(JImn0Vzh-jBE%&13>--VRS>D??3XtGd_=SV4`~kS@SqLt03YH zLG})^W)MwOxHXClqHgTmS$o=-ru|8d>Q80000`BO8FM`PQOUq#NR6Na_u$-@nA28Z>KIW*edJbPX`8KivPd;`@?YS&0|KG zOP223#PIFZ1%^cnRxzmFe!_5DRfb{i*7XcrOe|px7hc_GWI<1L$X?lUWFKR+yWf9X zWgWO?)!R?-r$prB{O#W#xLC4xAcN}dCkz$0PQz2^g;)0(@uqX+z(O_*Ie9Y@o9jr; bWJCi1R~f}-;F);500000NkvXXu0mjf!BV)O 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 70db49f90e8beb386fc1a97baaa0f2e34efe16ab..2641eac7fabc0ba84a341ee019453837e8913d65 100644 GIT binary patch delta 191 zcmV;w06_o32Ehfev;mXG0Zfy#0TX|jNklEy0*1 zgJ7CLG)xT5Al%NzrWpp};^GJzf>m=aac>(>zuOmwdxYX*6XXhV>_gRCzZL=%-R tkPQN1WX;4FfRQhd4I?+7P?pJX0RV}4?iy^x4*&oF002ovPDHLkV1jajPICYN delta 334 zcmV-U0kQtU1;Pffv;hwWVoOIv0NDWF0OVX0@;j5U0Z)Gc8c9S!R5;6}ld*2XKn#Ze ztC*Xo$b>Qg5~^Q-fsqHuTeMTu0g%ettt)dSn9BDc1 zJz19jPrrRW;Gdx!HSy@FJ)D}1rdRW3{borY;`IHfB?WRu(hy(zb+3ChO z5SO?TsxW^HZ8weRn8YQXCW!z*mgit*MMT2P;&LL!sx6)@cTyY=Vn9`oa~}5C(96 z-1i6pAk!ovXyQBB0FN$r$qa;GssI2007*qoM6N<$f;bpgk^lez delta 345 zcmV-f0jB=I1UFcd|v zY)ooO0)bA^6tbjv3ls`o`U(Ald|baEpWr1BD0nM~f=L&F2ZQ1mr06;1Q0NA%}FNpcX0Pr%MG5}cRxgn4b!pQdX zWP+}kW>bF@rmSl(B002ovPDHLkV1h6LPTBwf delta 320 zcmX@gx`l1S7Dj%yByV>YhJOry82%Pc4Gf#Sk@ZFovT~^Ckxxx zz1C9RC82C~R`n|@4+yB4Ed$f(eRI+hfZ#>uoQ6aW;a$(1!}*f0o;h&fz=ovnL2Pww zWoy^8f$$_2n8r9vwcFlA4 zFxH288y;$y-}v|co~6MOAfPvYZfd_k{`CMz( zzgAwOcigTe~DWM4fbzFza 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 20888e5793c31806fe9d66b744991a327480120c..40e5ff80b8dfbca6d89ddc7457f01e1037cadc08 100644 GIT binary patch delta 174 zcmV;f08#&~2CxONv;mXG0Zfy#0TX|SNkl!xIssI20 delta 313 zcmV-90mlBY1+50Kv;hwWVoOIv0R8~{0R3^5QC5?&0Z)Gc1xZ9fR5;7clCev}KorJ* z4bFj-a!0#6i+_h}ZwTJdxpc_dzrfX91c!oyxY(iK;Nobd)U|s{LLoYpOy^?arI*~Q z-}dg__x<3#2Y;=O?^BPa#;#{~S5E(l&o~+D>MhN3isE1%?yo!E06m%-HM~HIh}C+- zpx*;PiU@ywIRvPM1U|E^)p~;z5neMCs<4``#8GkJe|<#{o4hgcK`(k$ov?6gK7^m}Y~dzQ-&hwdDb ziWBp*s!C7u7XXBm4!!XbRh*bG>{fMY1RQhgDXSegG4`sOjQ>a9R-tWbp`grk00000 LNkvXXu0mjfTh5to 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 1c83e4a5a7528b970fd82115e9ab04063fe8b939..8f01a8186add091ccead521ee705bc698ec42d49 100644 GIT binary patch delta 194 zcmV;z06qV+2E+xhv;mXG0Zfy#0TX|mNklYhJOry82%Pc4Gf#Sk@awIa9X$PZqvk z{$5LYmjt)j*}lYsJRlIY>lT>4t{30xu`cOHe;$YMu4lJi3UAC#OGr48w=VfXdV+hQ ztnx0#Ah#->lPgbZtgHI|OqUr5obMmuJA5Jg#mkkE;R3(y!F7H}em zpmbo37e4{?;0h&q1qttk1Eqdi2ZK!BLnzq1CnOOPCSwT5*e}*LBS~JJBM`v86=7x2 zixdIKqt*l>^DVN0{LvuSkxMQDF~$Lb{L13M+X!_;Gy=X0kCopBax2h`NaQ6)1cx)B jE2jv=8i|}D4BxL42khYRW(s^q00000NkvXXu0mjf$I(c( delta 334 zcmV-U0kQtK1;Pffv;hwWVoOIv0NDWF0OVX0@;j5U0Z)Gc8c9S!R5;6}lRav~KoCZs zQ{xll1UW_;kuwA}fn36c&S27pG$xH9kV33cA#7JNxG|eXyU|)3 zW6)RakKW9;Gb8A!;Gc7S?f1R_`fWaU4mNcSz{_gYVFDlgx?ThDG@Y`kYjULkxE&1m zs49mGeei!IikL)^y)gUj=|m!>^Tfah13ZjI);hq~eh!8Nb~|qoox⫬g{gfcLTl_;ZkD*3e&9d6b)B g>ByJ4_s`T@zmmy?)~5bZZvX%Q07*qoM6N<$g3x-Q)c^nh 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 5ae6573e1a103360f1995aec1aeed5bf8b692f0c..159987bc6723e351b9d63776c0a4a285f5921697 100644 GIT binary patch delta 223 zcmV<503iR22jK;D z&IZv-tgXSAC__L7fG|w6rLrcnOUD13<0jLi^|HKRu_S(%Dh Z007KS@TeytQ~&?~002ovPDHLkV1g!XTTTD~ delta 408 zcmV;J0cZZ<1&jx0TjnS zC!%zS%5j23oKX%INfJSjU_x6B4M|ba&>#|y4*d~rK@>C;L`5M@{Q)A?QiPz8gElot znkdAD5?q@<@-Cs?_8#y1;qiU%!9PRLD~2WlT4$clWdtLDNl%=J#z;hC0EGPkBtg`S z0Orml5)6N9n=7l-%kM3*>~JEvY*0q4YN}v3!B6r1(<8FO$=>xj0Fuk5JBi6iJgy9L zbd&6A)`RqTP$(9dotj3^Vw_~-@$yCG@_Zsc7x4o1t9Kr<53ju^7$5I zeSU#dHbXj>5xUl51X;4%84^WaRTb4Y37`+?2N7rH07%}ZTG!dLh3D#v@TIm*`)D)( z7+hTL5$gi5Y(8rd+CHRA&SbGS|BvMgZ)00000NkvXXu0mjf-)u8r delta 224 zcmV<603ZL11=akO zzBkCGwpK&}vy_y1Fql%JTi>JgUe1HfY>1MzLFB$hQOQ+(}1iQ*?^Rwp#KClqZv1!I>JGvpr7^IOX&B%tpG$CswN;4j^AuvrO cX&ev$0GSIv6EFS$Y5)KL07*qoM6N<$f?|?QAOHXW delta 320 zcmV-G0l)sR1+)gRv;hwWVoOIv0R8~{0R3^5QC5?&0Z)Gc3`s;mR5;7cQoV}8KoI^6 zzCd6>7xodZ^+~S2#w`T*4HC<6rJdbbU129!T`6c8mTQE(Kyc+QU?=J3DnoY1q&=sb zo$qIu{eb_jLEkVlhT8pNwPN^TA90qZ&0FOhZgoxX>vgXwGGnN)mVVD?+V6H$Sljf4 zwUlV>Z54lo<)CXh1*WK6VX xWJ8cKNt#E?1GMsh0a_m17%dM*4H(wt0RYu1K+RZ?7{dSn002ovPDHLkV1n*(P7(kB delta 324 zcmX@gx`S=Q7Dj%yByV>YhMNrc8J@(5e6pUrk@awl9G~t$-?`K zo^mPgl2A4~TYB~^4+uO{Qv=iL=jS9Q071s8wMPwFIAcHWEPlQrD-9@eV^^&;vputQ z*!6?FEAsMw{eOL3U*yk+kB|TJ%iI5xmlFLdf0scmhb{NUF44Zo_Ztn*L>bI?^a~TK zU%gZ!@xb{5|LVW}5(Qf#zCOln;_RT?r#?)4sNN+fR>GW88Y0BAvxgpp9zlf?$aMWntGaN`?~uH4}gL{dR%pdnOhUCFfg$lNMevrIHGm} zXhD1p)2d_BS*0X8IhHdTI8XR_!{o6^g2omb2W|cv3!Wale(_Vn78{9cPZ=3rZaW>G T!uzp>0SG)@{an^LB{Ts5E3c0z 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 fccdf9abad41c027f1a340a0eb51812c620accb4..65bacce330a25819340ecfbac67484e4ed14ee92 100644 GIT binary patch delta 190 zcmV;v073uG2EYZdv;mXG0Zfy#0TX|iNkl1gt^Y z+#8HxnqmC8i&x+^1g=@xJrRsy2Eh3E48f@xh6x#hVgO9Dl&J?8!wiwL4gSw9rH9)9 zz>% delta 346 zcmV-g0j2)H18T5+~p1z)Zpu8cizsMH?y!MtTpHI+kDdlB9i7mKED94w_Dm&RFkDx??|!U5fPCv zigoOIzN~+$%dA!|?H`fN7s%!d`a;E*Hy*C%r&_d3WPqoKJ1rgHaC8B{`(n=3S)bGX zF@TjZ^`Xek-jHT*2*CaAH6L%UEM_yKi7De0If?0H3_uVB080gq=v;mXG0Zfy#0TX|_Nkl4v;hwWVoOIv0NDWF0OVX0@;j5U0Z)GcN=ZaPR5;6}ld(zzK@>&L zu9!rl5mE#Ksq9Q)GZ5^ol}Tf1WwoFn*adAuDhn$~A;||=+9c=)SWhE@jR*mELkc6B zRTME}BSThaL&Vp;Vea9+H+SHl<=76TlzOypx7*IdL@A~AeP8>&ucef_(P-$bGpi?Z z;CY^|R;zz>yIla3Qsnb_28kiNYn$}->n|s6oVe9$88JZp;uM<`H%=^sV3-6WEW`k6wHit(gb+OGcL0`CQ&e&#E5!3W9RWxw5khbuJ^*m= zc+J(@C+nGMDHW;k3!C$KPV(zD!CG2 zr#C){OW8$zcy?m?`SctBJLw`vx0if7HtDoR)CO5_k_15z5nG?pHcp&4*1>2x0kg0_ ceX2?83tA1O#=&dcA^-pY07*qoM6N<$g5G+kPXGV_ 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 8afe60c779cddbf93e69e97c486dc5ae8e84f088..986c5a6c34e9969f926ca551d0d094ded3c76f26 100644 GIT binary patch delta 137 zcmV;40CxYz1&jr-v;mXG0Zo&%0TOv+Nklw;RrNmzQ`I1x zia0np2-;v`VuITcVhn(}2CE?;13;2AHvl=72F?|DV+3RfD6}ay1Qz#X82~c`hS3cJ rxsad%_zVJJm>5Y(8rd+CHRA&SqhU{$feyb500000NkvXXu0mjfV}LJ4 delta 208 zcmV;>05AWH1;z!iv;hwWVoOIv0R8~{0R3^5QC5?&0Z@OQNklD2yYQ^jH z2t7&nS@#Ma!?TS}AVn!8dol1B_+%a*k|g<`)zPwr$_$vfIAR>f6+r>Zo~r0D&+`|< zFvJB-*`uv)=<2&mYwbc*Q0wzsa0S!oi`#+{7Ck}1Qvp-*0Fxq?KWE#`E-XYS^An6K z_7G{|poE*`qKXTYY?c>L(m1lc2Irhb1tL`-ED@G`%0n3!@Is6q`}!{gO@0rML!lf1 O0000WEtH-+jL>{1)iT4 zaL!ey9u0p5S!Mt*5+x7`B#L-_dBgnn=5MCh0rMl?*$$YmMpK@QXS|QKO@~jKIJ&%M zr8J}v_$-$IfL^zc5IsWlC^sM2diT4D#Dow$62+hA0Bg5`Qu^4uCP4^(Crtz{{3J!j8D)Ibj`>HfY>1MxbAM;hQOQ+(}1iQ*#M`cr2hmpqZvK>U@v>gOM0fQXc*fis`9o-OQ4AMxHW@JNPnvgXTr5PUC5SS*C cG!6&=04+v7U-?D>t^fc407*qoM6N<$f@;=HF#rGn delta 318 zcmV-E0m1&T1+oUPv;hwWVoOIv0R8~{0R3^5QC5?&0Z)Gc3Q0skR5;7cQZ0|dKoEU2 zSllE{LsEs~U)(>aAeHC}6k-htSxNW@CkV(>r?88uxdhu)sC9)kmjyl$>kL=6dD*>BPC#b)Rd^ zi4e|KL7RWPn9UFqK^27~tcn6L5k^F4iUjQqVj>u80RWRQ1OQyh(!tzsH-CdvO5SD} zjI|!2Db}u_>lSVImh($07*qoM6N<$g6Hj%aR2}S 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 d19bdd4bbd34b6ad69205e71a82473d0897e8248..378c4b9c7bf249d1aa315cf89fd14928d4dcbe95 100644 GIT binary patch delta 211 zcmV;^04)F82Gs?yv;mXG0Zfy#0TX|%Nkl;IP&JOI-q8v-)mTI6~#25FX7b|%jiWNU^2kVdjKgMhSh)_-A1FY*ik zX;d|s^q(X{K#E~l%X}H!01z8w2n=J@ET>Tc)&RmF4d@1du#VL_Fvc(hq!~k`07Qc@ zOcO{k%s^zP1=|L&v;hwWVoOIv0NDWF0OVX0@;j5U0Z)GcGf6~2R5;7EkTFlfP!xr~ zVogbfbf5`Av@s!=4u%lsZf*>XJ1nejZVvtk7gqlO&dvr}2Ld}|_ z)dO;cy7*OsV(By-)O3r5?*NcDngAj9HolS83=H#JDCu1QwiSJCQ{HHj+DZps5tzt< zIHOgVO?m*h(*c@pp{RSI!Nj=@-#c0*G?AhSGM`HX3*W(=4ptJVSrgwI0WhER!WvR^ zXuo_WmP;QsYpzxBy%F=TgwY#F-(5C(%mAJ{ToL{mJ^-4Iclu154P^iT002ovPDHLk FV1hN~oGJhS 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 60b78f4563195531ebbfe59f136c3f70a61b0730..882398eb4d56ad8e0e2957c80f10b0299c8075e1 100644 GIT binary patch delta 411 zcmV;M0c8H&2$~15v;mXG0Zfy#0TO=!a!Eu%R5;76Q_F6HFc8dFRBD@m4UVym0k!HW zhvWcxNl_vpKB!+;$#v=sq>kM3Ax<)JQW0Q zb>wr2Sddxz4j*93klxZX0xHHs)#L~D4e&382e1ZdjNkRW5F2X zO-f0eb7G897={$bF(pYtX_`_|6jYX_V;l7VR>vMa%QBCCJRWZ+c6K_Q{92ypJ}%t# zMmivAt)<;=cPsk*`u>1*@!@drEbu=n&YVa{g8s1hdc96Slv3VXJx@i2>!i-5Tf<`bt)nNnx^?vM07%HJrxnt z)zL$%>bjn^@q9k}HFOHGmaGTC7z2GcKwtw8$~YArLJKh~`~9Aj2*#5L^vmTkIu-Hn zT*m~gdjO{>M1d`%Rvh>?*S-tn5JP?A=)dQR9`cfZ0823;lI+=o-~bB%002ovPDHLk FV1lnszHI;i delta 619 zcmV-x0+jul2i*v;v;hwWVoOIv0R8~{0R3^5QC5?&0Z)GdHAzH4R5;6}lg&;OQ5c4w zGe2cgW7Kp+Qz$T$TFQ?lCN-?Q8+KfPiEK=~1b5z!D+mY_DU^bigf6U*Qc9=vbmnu> z&Y*5NoAa0VIqx~&d*Fdh0W8gX5{jUd!Y~ZVho|cGLRL@;L-TV9OpzMT;CFrIytH(EpJ(m_<7tmhr^EE*xb%8=OisVz`O9hU`WgTW zz_u+Ci3E*CgGBNvgTVl;HT8NO5y474#GeKBj5nx-Qvl zmQdd!gDwOrzy% z_a8n2ptWWuTR?>SD@6AD{eE2ENF2F{=3yA3wMJ|G7eIt+WiNK{<@2YlNE`{HHPbY? zIIn*ZhH?)mSI$&)a%uHl4EbNYyz&;?wgIU9xKMEt=W|oy`#lW%5t&Q|*LB(6{Q6I1 zi*HtGwOU+VU2)U;MKYPBQvEqh!$N*WW^+qat5vkthzMaAVi*RNWntSkQG^Y{Ae~NA zKdo?7J0C_l2m;#eHibeV4v-3a6pKX`7Z*?I-du9q{!Qn)iRXFvzCZFl>hjr12}X~i z6r7x0sNC#?3`-@WJ2%vvHz9_huuKcbaoE{EQV;#j`~j-x5%4E1UXuU-002ovPDHLk FV1mqiAHo0t 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 a35f0ffdf24c90b78243768f780c439e2389b0d0..69d449069f9664635eecef7f8b30db6e091c5fbb 100644 GIT binary patch delta 186 zcmV;r07d`y0*nHXIDY`7Nkl1M+D?p~=(@2y7#MqA00Aw%V@;hDw zU@pLAD>BAu04&jf6u@X)7-xC_X?%Im`oC}yH=Mq(Lg7Dq6fXmQjmQRoG$3o-+`|s$ o6Kw!U1D6jAiUA-#Q3k*af%z4g%|HtP09oQz2%rwTLX%JdCV-z#`Tzg` delta 292 zcmV+<0o(qJ0`>xsIDY{SNkl++UGNZgv6qZ(~XQGy}> 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 e7d34cfdebef806ee31c9fe170deb8d311703835..95e58b8976aa0cb56158996f83e3ec55891c6a66 100644 GIT binary patch delta 159 zcmV;Q0AT<10qy~i7zqRe0002(-QrS_Awz!wbW%=J06^y0W&i*HXh}ptR0tgf!NCmy zK@T$=OZ8&C|uX$Z0b zAZLT=vv0weC__L7fG|vRbL9fC<_#bI|L@#+6RRO0^(Y2_oD9+oQVcSHkReYf27rL3 zkP=uC4C6Bd2<6jteKRXM}EbWw8-6@HGz~ab4-9HO3e$fybtaR+_kGIcGO<_;!!UR)HsM}fY`*YreFsD<&FOR+-}eEqZ5zWd*xfDw zurR+$SdwTxvk-!#{7F@F7$o|gsFWg=O5rD`7~NQ>y({q}Bmh{Jg;EM31pE0j0J;a` zBv+P0A*N~SDu9#{Ap|>HUw^*ev$?x^WO!mI6jFaR1*MdAdT_j1E;a-3Tq@EUYvbwM z0i&0`@gCmd2|liGd3h=^y(myh;W&;JB)E9IjvU%M0r|nZ36fkMJG6ClkX%`2dQtqm zi6ij=J(pSwbZ4R;fX10gMqV!g$mMducGgUxUXt1NQjIwj5C&OT8pHFv8gt!as@zTh g&+{7oepJ7$Pc^=+A-(ZNvj6}907*qoM6N<$f(NS2e*gdg 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 5522a07fda66ecbc77f0ddd2787022d9b5eab22d..661297552537747f67b583d252d3be06280246b1 100644 GIT binary patch delta 138 zcmV;50CoS$1&sx;v;mXG0Zo&%0TOy;Nkl=)!lC}wMfxK^Fzwd} s|9%S~!rU=*SOOV@aIFL)?D6HS2K8id%hqpJYybcN07*qoM6N<$f@*j_ssI20 delta 212 zcmV;_04x8E1X|>jr(yc&d)-lHUv1m1t1pz>7?KGyHe(bn^DgFi75L8HFpqp#} O0000z{{3J!j8D)Ibj`>HfY>1MxbAM;hQOQ+(}1iQ*#M`cr2hmpqZvK>U@v>gOM0fQXc*fis`9o-OQ4AMxHW@JNPnvgXTr5PUC5SS*C cG!6&=04+v7U-?D>t^fc407*qoM6N<$f@;=HF#rGn delta 318 zcmV-E0m1&T1+oUPv;hwWVoOIv0R8~{0R3^5QC5?&0Z)Gc3Q0skR5;7cQZ0|dKoEU2 zSllE{LsEs~U)(>aAeHC}6k-htSxNW@CkV(>r?88uxdhu)sC9)kmjyl$>kL=6dD*>BPC#b)Rd^ zi4e|KL7RWPn9UFqK^27~tcn6L5k^F4iUjQqVj>u80RWRQ1OQyh(!tzsH-CdvO5SD} zjI|!2Db}u_>lSVImh($07*qoM6N<$g6Hj%aR2}S 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 68fbde001dd7c93b8c2ddd0970d74cab570f77b9..f2ab62827832f8294a4fb5a117f872608dc16366 100644 GIT binary patch delta 195 zcmV;!06hP^2E_%iv;mXG0Zfy#0TX|nNklYhJOry82%Pc4Gf#Sk@ZFovUm9B@5fv zy_QhkCBbcWwoY*#4+up4vI5iB_2L>m)+PSv&*Ko@_3WLb>1IGfFCD$$yB_=Sr@hT)R=&*fYbjxY_ z!}I3<;ydvRJ9KYrwoU9PkYX@qnc1)7YwXADcA9z1rvsc(axeO)u`hmlaDyH{L(S|3 U*Ee^mH8B8zr>mdKI;Vst0H}wE;{X5v 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 abad19aac08b362e0044fdd88f0568fbc2432ec7..0466f0b85f2745c95042199e6b79e29a1105692b 100644 GIT binary patch delta 187 zcmV;s07U=V2E7Hav;mXG0Zfy#0TX|fNklM@R^wQG#yM?9kfM zA~bfS_D)pv;FqVRzM+I~5d1!Ts?<;GOdylj0@;W} penfDX2}RjQAjU{!A4%~2x&VmoDVJH9m2&_9002ovPDHLkV1l&?RLTGV delta 358 zcmV-s0h#{21=1R5;6}ld(#}P!xv0 z1Pi+P0=__okSu{#30>5JOX<{S5Znyh%}s=eOTlMw3XYa8A(RHPs5STkmJSufr6>qq zmnP&kQ42lOIp?1L-1GnUTv*ezU-EL*`l<%<>ikhjOeb>yF0UIUj8+A{y*mM*-Rd!& z%t=xKz~_H^gJgW6;PfhRP480EyZJ#DZ*GrpOrcQJs=xq82fO*`0Qb)i0DOJlD3j<_ zN@d_M4hZ7_0Nb#by}c4eky5)+4BD+;E?Ncz2;+cey~E+&K3jY!4Rqi4cZzBk$6m#B z&+}fgAW22ewOQ}vMVa3p4l?d#3d@4ohNU#=4+l#BZk9Yf9+9LXe+k>L6!6pd4966u zU0|t+bX`{h(lLc}Od%KVUiAN7<;Aj;tm@{qKhxUw1G5T<8?pIE(EtDd07*qoM6N<$ Eg0ADJ>Hq)$ 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 7c58ad6d7f2a653ee91534bc7c5f04145e4b97f7..0babe00b650053dd05d3c7b1eb11d77f801ad26b 100644 GIT binary patch delta 223 zcmV<503iRD2jK;$25BxWUGU$*%AK$w=$ZoqOW~Sf7^fk)G`lD z&IZv9ZdbvWC__L7fG|w6pL-Zs^UUmf{|odlU^N7!9>oBVlR=t6ia`btG6YY>01(g= zQUWW2VSI*wT#7Y);4%cH8DtQM25H7=03ky_-UMNSsS}$apzt9|Gd4p=){Gv#WMwL1 Z0RXim6cN5f?{WYD002ovPDHLkV1k;JR*C=s delta 420 zcmV;V0bBmz1(ye~v;hwWVoOIv0R8~{0R3^5QC5?&0Z)GcZ%IT!R5;6}ld($zVHn1L zmzY@)r3cYaU@CERL`4mf5!Z124-F2vO4Cgal`Z`hIoj6Jlup4l33vM+t$zB!SI*OcqNJ>J`jBR`4S=rSA&Ozjcb5RH zCU#t%jCqM;u@xT6S3{58AdVTBW{qTWo2iE_665Ct#secEHsInSk7?GB3G?wF zRlP%X_L%->pUARGrE-H|6b0Ks_5E3ReKc)Azu&W+RP_!sbAF`O8IfgmbP~OsEH$$D zRRe%hsbHtm@e5u~7E&(`u`9q0va24@XtW(-&t)1+CqS#!_;soNTt6OTnf*w1ZkYf8 O000O{MNUMnLSTa8xWJhJ 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 b0e864c7d301def4c892a472350ac05c046c6263..e0c2ee9b677bf8a8ea6c7dd06f57b6ab74c4ba3f 100644 GIT binary patch delta 137 zcmV;40CxZ01&jr-v;mXG0Zo&%0TOv+Nkl45J$c rav?zj@EHWcFfo#nG_qkNYsLovf7WR*kYuiD00000NkvXXu0mjfN02f* delta 232 zcmVOa)v;hwWVoOIv0R8~{0R3^5QC5?&0Z@OoNkl Date: Sun, 5 Apr 2026 19:29:07 +0200 Subject: [PATCH 15/43] Matter Manipulator does not copy fluids in dual interfaces (#33) Co-authored-by: Martin Robertz --- dependencies.gradle | 1 + .../common/building/AEAnalysisResult.java | 41 ++++++++++++ .../common/building/AEPartData.java | 67 +++++++++++++++++++ .../matter_manipulator/common/utils/Mods.java | 2 + 4 files changed, 111 insertions(+) diff --git a/dependencies.gradle b/dependencies.gradle index 9ba94b49..b046d608 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -43,6 +43,7 @@ dependencies { implementation("com.github.GTNewHorizons:GTNHLib:0.9.47:dev") compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.17-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.64-gtnh:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritia:1.93:dev") { transitive = false } 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..625e5383 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 @@ -32,13 +32,17 @@ import appeng.util.Platform; import appeng.util.SettingsFrom; +import com.glodblock.github.inventory.IDualHost; import com.google.gson.JsonElement; +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 +55,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 +108,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 +144,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 +201,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); @@ -471,6 +507,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 +525,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 +552,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..f0f794df 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,6 +15,7 @@ import appeng.api.parts.IPartHost; import appeng.api.parts.IPartItem; import appeng.api.parts.PartItemStack; +import appeng.api.storage.StorageName; import appeng.api.util.IConfigurableObject; import appeng.helpers.ICustomNameObject; import appeng.helpers.IOreFilterable; @@ -24,10 +25,15 @@ import appeng.parts.automation.UpgradeInventory; import appeng.parts.p2p.PartP2PTunnel; import appeng.parts.p2p.PartP2PTunnelNormal; +import appeng.tile.inventory.IAEStackInventory; +import appeng.tile.inventory.IIAEStackInventory; +import com.glodblock.github.inventory.IDualHost; 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.utils.MMUtils; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods; +import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; /** * Stores data for AE facade parts. Also stores the data for AE cables. @@ -39,8 +45,11 @@ public class AEPartData { public NBTTagCompound mSettings = null; public String mCustomName = null; + public NBTTagCompound mBusConfig = null; + public PortableItemStack[] mAEUpgrades = null; public InventoryAnalysis mConfig = null; + public InventoryAnalysis mFluidConfig = null; public InventoryAnalysis mAEPatterns = null; public String mOreDict = null; @@ -103,11 +112,33 @@ public AEPartData(IPart part) { } } + if (part instanceof IIAEStackInventory aeInv) { + IAEStackInventory aeConfig = aeInv.getAEInventoryByName(StorageName.CONFIG); + if (aeConfig != null && !aeConfig.isEmpty()) { + mBusConfig = new NBTTagCompound(); + aeConfig.writeToNBT(mBusConfig, "cfg"); + } + } + + if (Mods.AE2FluidCraft.isModLoaded()) { + analyzeFluidConfig(part); + } + if (part instanceof IPriorityHost priorityHost) { priority = priorityHost.getPriority(); } } + @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) + private void analyzeFluidConfig(IPart part) { + if (part instanceof IDualHost dualHost) { + IInventory fluidConfig = dualHost.getConfig(); + if (fluidConfig != null) { + mFluidConfig = InventoryAnalysis.fromInventory(fluidConfig, false); + } + } + } + public Class getPartClass() { if (mPartClass == null) { if (mPart.toStack().getItem() instanceof IPartItem partItem) { @@ -196,6 +227,19 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD } } + if (part instanceof IIAEStackInventory aeInv && mBusConfig != null) { + IAEStackInventory aeConfig = aeInv.getAEInventoryByName(StorageName.CONFIG); + if (aeConfig != null) { + aeConfig.readFromNBT(mBusConfig, "cfg"); + } + } + + if (Mods.AE2FluidCraft.isModLoaded() && mFluidConfig != null) { + if (!applyFluidConfig(context, part)) { + success = false; + } + } + if (part instanceof IOreFilterable filterable) { filterable.setFilter(mOreDict == null ? "" : mOreDict); } @@ -207,6 +251,19 @@ public boolean updatePart(IBlockApplyContext context, IPartHost partHost, ForgeD return success; } + @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) + private boolean applyFluidConfig(IBlockApplyContext context, IPart part) { + if (part instanceof IDualHost dualHost) { + IInventory fluidConfig = dualHost.getConfig(); + if (fluidConfig != null) { + boolean result = mFluidConfig.apply(context, fluidConfig, false, false); + dualHost.getDualityFluid().loadConfigFromPacket(fluidConfig); + return result; + } + } + return true; + } + /** * Gets any required items for a part that exists in the world. * Effectively a no-op, should not be called with a real IBlockApplyContext. @@ -280,9 +337,11 @@ public AEPartData clone() { dup.mPart = mPart == null ? null : mPart.clone(); dup.mSettingsName = mSettingsName; dup.mSettings = mSettings == null ? null : (NBTTagCompound) mSettings.copy(); + dup.mBusConfig = mBusConfig == null ? null : (NBTTagCompound) mBusConfig.copy(); dup.mCustomName = mCustomName; dup.mAEUpgrades = mAEUpgrades == null ? null : MMUtils.mapToArray(mAEUpgrades, PortableItemStack[]::new, x -> x == null ? null : x.clone()); dup.mConfig = mConfig == null ? null : mConfig.clone(); + dup.mFluidConfig = mFluidConfig == null ? null : mFluidConfig.clone(); dup.mAEPatterns = mAEPatterns == null ? null : mAEPatterns.clone(); dup.mOreDict = mOreDict; dup.mP2POutput = mP2POutput; @@ -299,9 +358,11 @@ public int hashCode() { 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 + ((mBusConfig == null) ? 0 : mBusConfig.hashCode()); result = prime * result + ((mCustomName == null) ? 0 : mCustomName.hashCode()); result = prime * result + Arrays.hashCode(mAEUpgrades); result = prime * result + ((mConfig == null) ? 0 : mConfig.hashCode()); + result = prime * result + ((mFluidConfig == null) ? 0 : mFluidConfig.hashCode()); result = prime * result + ((mAEPatterns == null) ? 0 : mAEPatterns.hashCode()); result = prime * result + ((mOreDict == null) ? 0 : mOreDict.hashCode()); result = prime * result + Boolean.hashCode(mP2POutput); @@ -325,6 +386,9 @@ public boolean equals(Object obj) { if (mSettings == null) { if (other.mSettings != null) return false; } else if (!mSettings.equals(other.mSettings)) return false; + if (mBusConfig == null) { + if (other.mBusConfig != null) return false; + } else if (!mBusConfig.equals(other.mBusConfig)) return false; if (mCustomName == null) { if (other.mCustomName != null) return false; } else if (!mCustomName.equals(other.mCustomName)) return false; @@ -332,6 +396,9 @@ public boolean equals(Object obj) { if (mConfig == null) { if (other.mConfig != null) return false; } else if (!mConfig.equals(other.mConfig)) return false; + if (mFluidConfig == null) { + if (other.mFluidConfig != null) return false; + } else if (!mFluidConfig.equals(other.mFluidConfig)) return false; if (mAEPatterns == null) { if (other.mAEPatterns != null) return false; } else if (!mAEPatterns.equals(other.mAEPatterns)) return false; 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..34e67088 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,6 +14,7 @@ public enum Mods implements IMod, ITargetMod { + AE2FluidCraft(Names.AE2_FLUID_CRAFT), AE2Stuff(Names.AE2STUFF), Angelica(Names.ANGELICA), AppliedEnergistics2(Names.APPLIED_ENERGISTICS2), @@ -61,6 +62,7 @@ public boolean isModLoaded() { public static class Names { + public static final String AE2_FLUID_CRAFT = "ae2fc"; public static final String AE2STUFF = "ae2stuff"; public static final String ANGELICA = "angelica"; public static final String APPLIED_ENERGISTICS2 = "appliedenergistics2"; From 9d76ed6e8ec87da8f55404893ea3b5ebe6912759 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 5 Apr 2026 19:42:24 +0200 Subject: [PATCH 16/43] Implement FMP and Project Red in Matter Manipulator in copy mode (#34) Co-authored-by: RecursivePineapple --- .../common/building/BlockAnalyzer.java | 6 + .../common/building/FMPPartTransforms.java | 221 ++++++++++ .../building/ITileAnalysisIntegration.java | 9 + .../common/building/InteropConstants.java | 1 - .../building/MultipartAnalysisResult.java | 376 +++++++++++++++++- .../common/building/PendingBlock.java | 24 +- .../common/items/manipulator/MMRenderer.java | 20 +- 7 files changed, 637 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/FMPPartTransforms.java 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..d83ae821 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 @@ -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; 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/ITileAnalysisIntegration.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/ITileAnalysisIntegration.java index e9834fab..dfa2811d 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,6 +2,7 @@ import java.util.List; +import net.minecraft.block.Block; import net.minecraft.nbt.NBTTagCompound; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; @@ -24,4 +25,12 @@ public interface ITileAnalysisIntegration { 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/InteropConstants.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InteropConstants.java index 3596aaca..ca517119 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 @@ -44,7 +44,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; diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java index 3c799c59..f21cee13 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java @@ -1,92 +1,442 @@ package com.recursive_pineapple.matter_manipulator.common.building; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import net.minecraft.block.Block; +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 com.google.gson.annotations.SerializedName; +import com.recursive_pineapple.matter_manipulator.MMMod; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; import com.recursive_pineapple.matter_manipulator.common.items.manipulator.Transform; +import com.recursive_pineapple.matter_manipulator.common.utils.BigItemStack; import codechicken.lib.vec.BlockCoord; -import codechicken.microblock.Microblock; +import codechicken.multipart.MultiPartRegistry; +import codechicken.multipart.TMultiPart; import codechicken.multipart.TileMultipart; +/** + * Stores and restores ForgeMultipart (FMP) tile data. + */ public class MultipartAnalysisResult implements ITileAnalysisIntegration { + @SerializedName("p") + public PartData[] parts; + + public static class PartData { + + @SerializedName("t") + public String typeId; + @SerializedName("n") + public NBTTagCompound nbt; + @SerializedName("d") + public PortableItemStack[] drops; + + public PartData() {} + + public PartData clone() { + PartData dup = new PartData(); + dup.typeId = typeId; + dup.nbt = nbt == null ? null : (NBTTagCompound) nbt.copy(); + if (drops != null) { + dup.drops = new PortableItemStack[drops.length]; + for (int i = 0; i < drops.length; i++) { + dup.drops[i] = drops[i] == null ? null : drops[i].clone(); + } + } + return dup; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((typeId == null) ? 0 : typeId.hashCode()); + result = prime * result + ((nbt == null) ? 0 : nbt.hashCode()); + result = prime * result + Arrays.hashCode(drops); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + PartData other = (PartData) obj; + if (typeId == null) { + if (other.typeId != null) return false; + } else if (!typeId.equals(other.typeId)) return false; + if (nbt == null) { + if (other.nbt != null) return false; + } else if (!nbt.equals(other.nbt)) return false; + if (!Arrays.equals(drops, other.drops)) return false; + return true; + } + } + public static MultipartAnalysisResult analyze(TileEntity tile) { if (!(tile instanceof TileMultipart multipart)) return null; var partList = multipart.jPartList(); - Microblock m = (Microblock) partList.get(0); + if (partList.isEmpty()) return null; - return null; + MultipartAnalysisResult result = new MultipartAnalysisResult(); + result.parts = new PartData[partList.size()]; + + for (int i = 0; i < partList.size(); i++) { + result.parts[i] = capturePart(partList.get(i)); + } + + return result; + } + + private static PartData capturePart(TMultiPart part) { + PartData data = new PartData(); + + data.typeId = part.getType(); + + data.nbt = new NBTTagCompound(); + data.nbt.setString("id", data.typeId); + part.save(data.nbt); + + 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("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(); + + if (!ok) { + ctx.warn("Could not find item: " + stack.getDisplayName()); - BlockCoord coord = new BlockCoord(x, y, z); + for (ItemStack refund : consumed) { + ctx.givePlayerItems(refund); + } + return false; + } - TileMultipart tile = TileMultipart.getOrConvertTile(ctx.getWorld(), coord); + consumed.add(stack); + } - var partList = tile.jPartList(); + 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("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("Could not place multipart: " + data.typeId); + } + } catch (Exception e) { + MMMod.LOG.error("Failed to add multipart " + data.typeId, e); + refundPartItems(ctx, data); + ctx.error("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; } + 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 getItemTag(NBTTagCompound tag) { + 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 getItemTag(NBTTagCompound tag) { + if (parts != null) { + tag.setInteger("FMPParts", parts.length); + } } @Override public void getItemDetails(List details) { + if (parts != null && parts.length > 0) { + details.add(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 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() { - - } + 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..f61de3f6 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 @@ -67,6 +67,7 @@ public PendingBlock reset() { this.gt = null; this.ae = null; this.arch = null; + this.mp = null; this.inventory = null; this.renderOrder = 0; this.buildOrder = 0; @@ -109,6 +110,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,7 +135,7 @@ 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); return list; } @@ -428,9 +444,9 @@ 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_INV) != 0 && te instanceof IInventory inventory) { this.inventory = InventoryAnalysis.fromInventory(inventory, false); 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..e5549fb9 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; @@ -526,11 +527,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; @@ -561,7 +577,7 @@ private static void drawHints( pendingBlock.y, pendingBlock.z, block, - pendingBlock.spec.getBlockMeta(), + meta, tint ); } From 02b16bbf4fd8a908bcb028439f25b061eb77f0df Mon Sep 17 00:00:00 2001 From: WhyDoLogic2 <147456089+WhyDoLogic2@users.noreply.github.com> Date: Mon, 6 Apr 2026 03:50:09 +1000 Subject: [PATCH 17/43] Matter Manip saves NBT between modes (#31) Co-authored-by: WhyDoLogic <84210614+WhyDoLogic@users.noreply.github.com> Co-authored-by: RecursivePineapple --- .../manipulator/ItemMatterManipulator.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) 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 83d87640..33e66db6 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 @@ -407,7 +407,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) { From 4035dfb599ae624b933ffedc4aad8f2a60f049c9 Mon Sep 17 00:00:00 2001 From: Discreater Date: Mon, 6 Apr 2026 01:57:07 +0800 Subject: [PATCH 18/43] Localize chat in client (#19) Co-authored-by: RecursivePineapple --- .../common/building/AEAnalysisResult.java | 23 ++- .../common/building/AbstractBuildable.java | 3 +- .../ArchitectureCraftAnalysisResult.java | 13 +- .../common/building/BlockAnalyzer.java | 34 ++--- .../common/building/BlockSpec.java | 39 ++++- .../common/building/CopyableProperty.java | 18 +++ .../common/building/GTAnalysisResult.java | 27 +++- .../building/ITileAnalysisIntegration.java | 3 +- .../common/building/ImmutableBlockSpec.java | 3 +- .../common/building/InventoryAnalysis.java | 23 ++- .../common/building/MMInventory.java | 31 ++-- .../building/MultipartAnalysisResult.java | 16 +- .../common/building/PendingBlock.java | 33 ++++- .../common/building/PendingBuild.java | 139 ++++++++++-------- .../common/building/PendingMove.java | 56 +++---- .../common/compat/BlockPropertyRegistry.java | 20 ++- .../common/compat/IntrinsicProperty.java | 3 +- .../common/data/WeightedSpecList.java | 2 +- .../manipulator/ItemMatterManipulator.java | 48 +++--- .../common/uplink/MTEMMUplink.java | 8 +- .../common/uplink/MTEMMUplinkMEHatch.java | 28 ++-- .../common/uplink/UplinkStatus.java | 9 ++ .../common/utils/InventoryAdapter.java | 5 +- .../common/utils/MMUtils.java | 98 +++++++----- .../server/BlockStateCommand.java | 27 ++-- .../assets/matter-manipulator/lang/en_US.lang | 79 ++++++++-- 26 files changed, 507 insertions(+), 281 deletions(-) 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 625e5383..7529466c 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; @@ -34,6 +36,7 @@ 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; @@ -234,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; } } @@ -246,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; } @@ -287,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; } } @@ -306,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; } @@ -364,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; } @@ -462,7 +473,7 @@ public void getItemTag(NBTTagCompound tag) { } @Override - public void getItemDetails(List details) { + public void getItemDetailsChat(List details) { } 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 bb27fa08..c569602f 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 @@ -17,7 +17,6 @@ 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.util.ForgeDirection; @@ -441,7 +440,7 @@ protected boolean isEditable(World world, int x, int y, int z) { 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..c7183bf2 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; } @@ -139,14 +143,15 @@ public void getItemTag(NBTTagCompound 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 d83ae821..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; @@ -111,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); } /** @@ -267,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 dd7b3e7f..92f123d5 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; @@ -254,7 +258,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 +269,7 @@ public void getItemDetails(List details) { if (prop == null) continue; - prop.getItemDetails(details, e.getValue()); + prop.getItemDetailsChat(details, e.getValue()); } } } @@ -316,19 +321,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; 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/GTAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java index 58f774e7..c9145a7d 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 @@ -9,6 +9,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.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -36,6 +38,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; @@ -340,7 +343,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 { @@ -511,12 +519,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; } @@ -610,7 +629,7 @@ public void getItemTag(NBTTagCompound tag) { } @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 dfa2811d..c7e2641a 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 @@ -4,6 +4,7 @@ import net.minecraft.block.Block; import net.minecraft.nbt.NBTTagCompound; +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; @@ -18,7 +19,7 @@ public interface ITileAnalysisIntegration { void getItemTag(NBTTagCompound tag); - void getItemDetails(List details); + void getItemDetailsChat(List details); void transform(Transform transform); 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/InventoryAnalysis.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/InventoryAnalysis.java index 304caaa9..28eb2a50 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,7 +4,9 @@ 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; @@ -80,7 +82,7 @@ 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) + ")"); + context.warn(new ChatComponentTranslation("mm.info.warning.inventory_was_the_wrong_size", mItems.length, adapter.getSizeInventory(inv))); return false; } @@ -97,7 +99,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 +115,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 cb334e47..9d719b32 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 @@ -17,7 +17,7 @@ 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; @@ -30,6 +30,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; @@ -237,8 +238,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(" %s of %s", formatFluid(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()) + ); } } @@ -252,10 +258,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()) ); } } @@ -335,10 +339,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()) ); } } @@ -591,11 +593,8 @@ private void consumeItemsFromUplink( printedUplinkWarning = true; sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_request_items_from_uplink", - result.left() - .toString() - ) + "mm.info.error.could_not_request_items_from_uplink", + result.left().toUnlocalizedString() ); } diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java index f21cee13..d37e1530 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java @@ -9,6 +9,8 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.IChatComponent; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; @@ -136,7 +138,7 @@ public boolean apply(IBlockApplyContext ctx) { for (PartData data : sorted) { if (!hasSupportBlock(world, data, x, y, z)) { - ctx.warn("Could not place " + data.typeId + ": no support block"); + ctx.warn(new ChatComponentText("Could not place " + data.typeId + ": no support block")); continue; } @@ -208,7 +210,7 @@ private static boolean tryConsumePartItems(IBlockApplyContext ctx, PartData data .leftBoolean(); if (!ok) { - ctx.warn("Could not find item: " + stack.getDisplayName()); + ctx.warn(new ChatComponentText("Could not find item: " + stack.getDisplayName())); for (ItemStack refund : consumed) { ctx.givePlayerItems(refund); @@ -227,7 +229,7 @@ private static boolean tryPlacePart(IBlockApplyContext ctx, World world, BlockCo if (part == null) { refundPartItems(ctx, data); - ctx.error("Could not create multipart: " + data.typeId); + ctx.error(new ChatComponentText("Could not create multipart: " + data.typeId)); return false; } @@ -246,12 +248,12 @@ private static boolean tryPlacePart(IBlockApplyContext ctx, World world, BlockCo coord.z ); refundPartItems(ctx, data); - ctx.warn("Could not place multipart: " + data.typeId); + 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("Failed to add multipart: " + data.typeId); + ctx.error(new ChatComponentText("Failed to add multipart: " + data.typeId)); return false; } @@ -380,9 +382,9 @@ public void getItemTag(NBTTagCompound tag) { } @Override - public void getItemDetails(List details) { + public void getItemDetailsChat(List details) { if (parts != null && parts.length > 0) { - details.add(parts.length + (parts.length == 1 ? " part" : " parts")); + details.add(new ChatComponentText(parts.length + (parts.length == 1 ? " part" : " 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 f61de3f6..4f21bd61 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 @@ -13,11 +13,15 @@ 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 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.common.building.BlockAnalyzer.IBlockApplyContext; @@ -140,16 +144,23 @@ private List getIntegrations() { 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); } - return details.isEmpty() ? "" : String.format(" (%s)", String.join(", ", 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 new ChatComponentText(" (").appendSibling(out).appendText(")"); } public ItemStack getStack() { @@ -208,8 +219,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() { @@ -339,7 +350,13 @@ 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() + ) + ); } } } 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..75dceafd 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 @@ -20,6 +20,8 @@ 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; @@ -176,7 +178,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 +202,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 +222,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 +251,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) { @@ -358,21 +354,17 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { ); 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) { @@ -499,76 +491,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..8f50d12f 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 @@ -82,13 +82,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (!isEditable(world, s.x, s.y, s.z) || !isEditable(world, d.x, d.y, d.z)) { 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 +102,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 +126,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 +139,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,7 +154,7 @@ 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; } @@ -169,11 +163,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { sendErrorToPlayer( player, StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_block", + "mm.info.error.could_not_move_block.new", s.x, s.y, s.z, - source.getDisplayName() + source.getChatComponent() ) ); } @@ -193,16 +187,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 +215,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; } 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 34abce09..16f1ba1a 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 @@ -40,6 +40,8 @@ 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; @@ -58,6 +60,7 @@ 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; @@ -1357,9 +1360,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()))); } } @@ -1403,9 +1406,9 @@ 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")); } } } @@ -1452,7 +1455,7 @@ public void setValue(NBTTagCompound itemTag, JsonElement value) { } @Override - public void getItemDetails(List details, JsonElement value) { + public void getItemDetailsChat(List details, JsonElement value) { int circuitID = 0; CircuitImprint imprint = null; @@ -1461,7 +1464,12 @@ public void getItemDetails(List details, JsonElement value) { imprint = CircuitImprint.IMPRINT_LOOKUPS_BY_IDS.get(circuitID); } - details.add(String.format("Imprint: %s", imprint == null ? "None" : imprint.imprint.get(1).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/ItemMatterManipulator.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/ItemMatterManipulator.java index 33e66db6..24fe8d2f 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 @@ -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; @@ -825,7 +826,7 @@ private void onPickBlock( BlockSpec block = BlockSpec.fromPickBlock(world, player, hit); - String what = null; + String whatKey = null; boolean add = player.isSneaking(); @@ -833,25 +834,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: { @@ -863,7 +864,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; } } @@ -871,12 +872,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() ); } } @@ -898,10 +903,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() ); } @@ -925,10 +928,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() ); } @@ -942,10 +943,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() ); } @@ -966,7 +965,8 @@ private void onPickCable(World world, EntityPlayer player, ItemStack stack, MMSt sendInfoToPlayer( player, - StatCollector.translateToLocalFormatted("mm.info.set_cable", cable.getDisplayName()) + "mm.info.set_cable", + cable.getChatComponent() ); } @@ -982,13 +982,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; } @@ -1066,7 +1066,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" ); } } 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 5202cb7b..6322cd0e 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 @@ -24,7 +24,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; @@ -711,10 +710,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 5a6d5ff2..06d06930 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; @@ -57,12 +58,12 @@ public boolean validate(BlockAnalyzer.IBlockApplyContext context, IInventory inv MTEHatchRack rack = (MTEHatchRack) igte.getMetaTileEntity(); if (rack.heat > 2000) { - context.error("QC Rack is too hot to extract or insert items"); + 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; } 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 f759f5df..a0ed371a 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 @@ -60,12 +60,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; @@ -102,6 +103,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; @@ -412,28 +415,37 @@ 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)); + 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 sendWarningToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(GOLD + aChatMessage)); - } + public static void sendErrorToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendChatToPlayerWithColor(aPlayer, new ChatComponentTranslation(aChatMessageKey, args), EnumChatFormatting.RED); } - public static void sendInfoToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(GRAY + aChatMessage)); - } + public static void sendWarningToPlayer(EntityPlayer aPlayer, String aChatMessageKey, Object... args) { + sendChatToPlayerWithColor(aPlayer, new ChatComponentTranslation(aChatMessageKey, args), EnumChatFormatting.GOLD); } - public static void sendChatToPlayer(EntityPlayer aPlayer, String aChatMessage) { - if (aPlayer instanceof EntityPlayerMP && aChatMessage != null) { - aPlayer.addChatComponentMessage(new ChatComponentText(aChatMessage)); - } + 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) { @@ -857,7 +869,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; } } @@ -901,7 +919,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; } @@ -1297,7 +1315,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; } @@ -1307,7 +1325,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; } } @@ -1330,7 +1348,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() @@ -1341,10 +1359,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(), @@ -1354,10 +1371,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(), @@ -1368,7 +1384,7 @@ public static void createPlanImpl( .sorted() .forEach(message -> { sendInfoToPlayer(player, message); }); } else { - sendInfoToPlayer(player, StatCollector.translateToLocal("mm.info.none")); + sendInfoToPlayer(player, "mm.info.none"); } if (!requiredItems.isEmpty()) { @@ -1400,10 +1416,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"); } } } @@ -1554,6 +1570,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/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 1ddb87c3..6eb4c29e 100644 --- a/src/main/resources/assets/matter-manipulator/lang/en_US.lang +++ b/src/main/resources/assets/matter-manipulator/lang/en_US.lang @@ -96,26 +96,30 @@ mm.gui.set_block=Set Block mm.gui.set_block_to_replace_with=Set Block To Replace With mm.gui.set_cable=Set Cable -# 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 +147,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 From 6b5755b2b3fe7d0210686a603b95d6b0c8f9f6ad Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Thu, 16 Apr 2026 21:53:22 +0200 Subject: [PATCH 19/43] [MatterManipulator] Fix Disconnect when trying to create a Plan (#41) --- .../matter_manipulator/common/utils/MMUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 a0ed371a..5064f279 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 @@ -20,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; @@ -1381,7 +1382,7 @@ public static void createPlanImpl( ); } }) - .sorted() + .sorted(Comparator.comparing(IChatComponent::getUnformattedText)) .forEach(message -> { sendInfoToPlayer(player, message); }); } else { sendInfoToPlayer(player, "mm.info.none"); From b4e7ab91b42327477eeaef96afb92b05b1fcb5f9 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:04:17 +0200 Subject: [PATCH 20/43] [MatterManipulator] Fix: Matter Manipulator does not move wireless hubs and wireless connectors correctly (#37) Co-authored-by: RecursivePineapple --- .../common/building/PendingMove.java | 259 +++++------------- .../common/building/movers/BlockMover.java | 14 + .../common/building/movers/BlockMovers.java | 31 +++ .../common/building/movers/FMPBlock.java | 12 + .../common/building/movers/FMPBlockMover.java | 51 ++++ .../common/building/movers/GTBlockMover.java | 59 ++++ .../common/building/movers/StandardBlock.java | 11 + .../building/movers/StandardBlockMover.java | 52 ++++ .../building/movers/WirelessBlockMover.java | 63 +++++ 9 files changed, 359 insertions(+), 193 deletions(-) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMover.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMovers.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlock.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/FMPBlockMover.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/GTBlockMover.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlock.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/StandardBlockMover.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/WirelessBlockMover.java 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 8f50d12f..9a383859 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,14 @@ 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 +29,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 +38,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(); @@ -159,7 +188,7 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { } // 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( @@ -235,6 +264,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++) { @@ -256,7 +295,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; @@ -276,18 +322,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); @@ -315,177 +361,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/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..99852270 --- /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.AE2STUFF) + 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..40fee6eb --- /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.setTileEntity(x, y, z, null); + 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..94df79b6 --- /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.setTileEntity(x, y, z, null); + 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..c8a155ea --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/WirelessBlockMover.java @@ -0,0 +1,63 @@ +package com.recursive_pineapple.matter_manipulator.common.building.movers; + +import net.minecraft.tileentity.TileEntity; +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; + +import net.bdew.ae2stuff.machines.wireless.TileWireless; +import net.bdew.lib.block.BlockRef; + +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.WIRELESS_CONNECTOR.matches(world.getBlock(x, y, z), OreDictionary.WILDCARD_VALUE); + } + + @Override + public void place(PendingMove pendingMove, World world, int x, int y, int z, StandardBlock standardBlock) { + super.place(pendingMove, world, x, y, z, standardBlock); + + if (pendingMove == null) return; + + TileEntity te = world.getTileEntity(x, y, z); + if (te == null) return; + + adjustWirelessLink((TileWireless) te, pendingMove); + } + + private void adjustWirelessLink(TileWireless wireless, PendingMove pendingMove) { + scala.Option linkOpt = wireless.link().value(); + + if (linkOpt.isEmpty()) return; + + BlockRef ref = linkOpt.get(); + int lx = ref.x(); + int ly = ref.y(); + int lz = ref.z(); + + // only adjust if the linked partner was within the source region (i.e. it's also being moved) + if ( + lx >= pendingMove.getSrcMinX() && lx <= pendingMove.getSrcMaxX() && + ly >= pendingMove.getSrcMinY() && + ly <= pendingMove.getSrcMaxY() && + lz >= pendingMove.getSrcMinZ() && + lz <= pendingMove.getSrcMaxZ() + ) { + wireless.link() + .set( + new BlockRef( + lx + pendingMove.getMoveOffsetX(), + ly + pendingMove.getMoveOffsetY(), + lz + pendingMove.getMoveOffsetZ() + ) + ); + } + } +} From 0233e9a57c154495e25c8339e652f6aedf5e80f0 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:33:43 +0200 Subject: [PATCH 21/43] [MatterManipulator] add Advanced Copy Options (#40) Co-authored-by: RecursivePineapple --- .../common/building/AEAnalysisResult.java | 2 +- .../ArchitectureCraftAnalysisResult.java | 4 +- .../common/building/BlockSpec.java | 7 +- .../common/building/GTAnalysisResult.java | 2 +- .../building/ITileAnalysisIntegration.java | 4 +- .../building/MultipartAnalysisResult.java | 4 +- .../common/building/PendingBlock.java | 11 +- .../common/building/PendingBuild.java | 182 ++++++++++++ .../common/building/SmartCopyIntegration.java | 103 +++++++ .../common/building/WirelessLinkFixer.java | 274 ++++++++++++++++++ .../manipulator/ItemMatterManipulator.java | 42 ++- .../common/items/manipulator/MMConfig.java | 19 ++ .../common/items/manipulator/MMState.java | 178 ++++++++++-- .../common/networking/Messages.java | 9 + .../common/utils/MMUtils.java | 28 ++ .../assets/matter-manipulator/lang/en_US.lang | 10 + 16 files changed, 838 insertions(+), 41 deletions(-) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/SmartCopyIntegration.java create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/WirelessLinkFixer.java 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 7529466c..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 @@ -468,7 +468,7 @@ public boolean getRequiredItemsForNewBlock(IBlockApplyContext context) { } @Override - public void getItemTag(NBTTagCompound tag) { + public void getItemTag(ItemStack stack) { } 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 c7183bf2..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 @@ -136,10 +136,12 @@ 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 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 92f123d5..5253739e 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 @@ -220,10 +220,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<>(); @@ -247,7 +246,7 @@ public ItemStack toStack(int amount) { } } - tag = stack2.getTagCompound(); + NBTTagCompound tag = stack2.getTagCompound(); if (tag != null && tag.hasNoTags()) stack2.setTagCompound(null); } 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 c9145a7d..83bfcaf2 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 @@ -624,7 +624,7 @@ private boolean canPlace(IGregTechTileEntity gte, ForgeDirection side, CoverData } @Override - public void getItemTag(NBTTagCompound tag) { + public void getItemTag(ItemStack stack) { } 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 c7e2641a..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 @@ -3,7 +3,7 @@ import java.util.List; import net.minecraft.block.Block; -import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.item.ItemStack; import net.minecraft.util.IChatComponent; import com.recursive_pineapple.matter_manipulator.common.building.BlockAnalyzer.IBlockApplyContext; @@ -17,7 +17,7 @@ public interface ITileAnalysisIntegration { boolean getRequiredItemsForNewBlock(IBlockApplyContext context); - void getItemTag(NBTTagCompound tag); + void getItemTag(ItemStack stack); void getItemDetailsChat(List details); diff --git a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java index d37e1530..4dd9921d 100644 --- a/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/MultipartAnalysisResult.java @@ -375,9 +375,11 @@ public int getPreviewMeta() { } @Override - public void getItemTag(NBTTagCompound tag) { + 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); } } 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 4f21bd61..aad13646 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 @@ -11,7 +11,6 @@ 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; @@ -51,6 +50,7 @@ public class PendingBlock extends Location { public ITileAnalysisIntegration ae; public ITileAnalysisIntegration arch; public ITileAnalysisIntegration mp; + public transient SmartCopyIntegration smartCopy; public InventoryAnalysis inventory = null; @@ -72,6 +72,7 @@ public PendingBlock reset() { this.ae = null; this.arch = null; this.mp = null; + this.smartCopy = null; this.inventory = null; this.renderOrder = 0; this.buildOrder = 0; @@ -140,6 +141,7 @@ private List getIntegrations() { if (ae != null) list.add(ae); if (arch != null) list.add(arch); if (mp != null) list.add(mp); + if (smartCopy != null) list.add(smartCopy); return list; } @@ -168,14 +170,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; } @@ -239,6 +237,7 @@ 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 (smartCopy != null) dup.smartCopy = smartCopy.clone(); if (inventory != null) dup.inventory = inventory.clone(); dup.renderOrder = renderOrder; dup.buildOrder = buildOrder; 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 75dceafd..c747ba40 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,6 +17,7 @@ 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; @@ -62,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, @@ -70,6 +73,7 @@ public PendingBuild( ) { super(player, state, tier); this.pendingBlocks = new ArrayDeque<>(pendingBlocks); + this.wirelessLinkFixer = new WirelessLinkFixer(state); } @Override @@ -83,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); @@ -145,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; } @@ -295,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; } @@ -342,6 +353,8 @@ 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()) { @@ -386,10 +399,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) { 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..39727bb2 --- /dev/null +++ b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/WirelessLinkFixer.java @@ -0,0 +1,274 @@ +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 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 net.bdew.ae2stuff.machines.wireless.TileWireless; +import net.bdew.lib.block.BlockRef; + +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.AE2Stuff.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.AE2Stuff.isModLoaded()) { + apply(world, x, y, z); + } + } + + @Optional(Names.AE2STUFF) + 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.WIRELESS_CONNECTOR.matches(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 TileWireless tw)) continue; + + scala.Option linkOpt = tw.link().value(); + if (linkOpt.isEmpty()) continue; + + BlockRef linkRef = linkOpt.get(); + int lx = linkRef.x(); + int ly = linkRef.y(); + int lz = linkRef.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 TileWireless 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.put(srcPacked, new long[] { + destPacked, linkSrcPacked + }); + } + } + } + + if (tempMap.isEmpty()) return; + + wirelessLinkMap = new HashMap<>(); + + for (var entry : tempMap.entrySet()) { + long destPacked = entry.getValue()[0]; + long linkSrcPacked = entry.getValue()[1]; + + Long partnerDest = allConnectorDests.get(linkSrcPacked); + if (partnerDest != null) { + wirelessLinkMap.put(destPacked, partnerDest); + } else if (linkExternalHubs) { + wirelessLinkMap.put(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); + wirelessLinkMap.clear(); + + MMUtils.forEachArrayOffset(state.config.arraySpan, deltas, d -> { + t.apply(d); + + for (var baseEntry : baseMap.entrySet()) { + long baseDest = baseEntry.getKey(); + long basePartner = baseEntry.getValue(); + + 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); + + 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; + + wirelessLinkMap.put(offsetDest, CoordinatePacker.pack(px, py, pz)); + } else { + wirelessLinkMap.put(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()) { + MMMod.LOG.trace( + "[WirelessLink] ({},{},{}) -> ({},{},{})", + CoordinatePacker.unpackX(e.getKey()), + CoordinatePacker.unpackY(e.getKey()), + CoordinatePacker.unpackZ(e.getKey()), + CoordinatePacker.unpackX(e.getValue()), + CoordinatePacker.unpackY(e.getValue()), + CoordinatePacker.unpackZ(e.getValue()) + ); + } + } + } finally { + t.uncacheRotation(); + } + } + + @Optional(Names.AE2STUFF) + private void apply(World world, int x, int y, int z) { + long packed = CoordinatePacker.pack(x, y, z); + Long partnerPacked = wirelessLinkMap.get(packed); + if (partnerPacked == null) return; + + Block block = world.getBlock(x, y, z); + if (!InteropConstants.WIRELESS_CONNECTOR.matches(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 TileWireless wireless)) { + MMMod.LOG.debug("[WirelessLink] applyWirelessLink({},{},{}): tile entity is not a TileWireless", x, y, z); + return; + } + + 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.link().set(new BlockRef(px, py, pz)); + + scala.Option verifyLink = wireless.link().value(); + if (verifyLink.isDefined()) { + BlockRef ref = verifyLink.get(); + MMMod.LOG.trace( + "[WirelessLink] Verified link at ({},{},{}): ({},{},{})", + x, + y, + z, + ref.x(), + ref.y(), + ref.z() + ); + } else { + MMMod.LOG.warn("[WirelessLink] Link was NOT persisted at ({},{},{})", x, y, z); + } + } +} 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 24fe8d2f..7e3e82b1 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 @@ -155,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; @@ -193,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 ); @@ -584,6 +585,17 @@ public void addInformation( 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) { @@ -1436,6 +1448,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/MMState.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java index 257fba2d..ddaf1626 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; @@ -54,6 +60,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 +73,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; @@ -333,6 +343,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 +372,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 +402,135 @@ 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); + 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 = partData.mFluidConfig != null && p2pDualIfaceStack != null; + 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; + } + private List getExchangeBlocks(ManipulatorTier tier, World world) { Location coordA = config.coordA; Location coordB = config.coordB; @@ -1100,4 +1229,5 @@ public static enum PlaceMode { EXCHANGING, CABLES, } + } 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/utils/MMUtils.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/utils/MMUtils.java index 5064f279..5755d185 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 @@ -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; @@ -416,6 +417,33 @@ public static List getBlocksInBB(Location l, Vector3i deltas) { return blocks; } + /** + * 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 sendChatToPlayerWithColor(EntityPlayer aPlayer, IChatComponent aChat, EnumChatFormatting color) { if (aPlayer instanceof EntityPlayerMP && aChat != null) { if (color != null) { 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 6eb4c29e..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,6 +95,13 @@ 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, only accept %s in this section @@ -287,6 +294,9 @@ 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 From 54177cbb1a79eb74f572650b0a544ed69cd7b765 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 23:30:06 +0200 Subject: [PATCH 22/43] [MatterManipulator] Fix AE2 not detecting GT hatches after replacement without server restart (#36) --- .../common/building/InventoryAnalysis.java | 9 +- .../common/building/PendingBlock.java | 90 +++++++++++++++++-- 2 files changed, 91 insertions(+), 8 deletions(-) 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 28eb2a50..3414d751 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 @@ -34,10 +34,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; @@ -82,7 +85,9 @@ private boolean apply(IBlockApplyContext context, IInventory inv, InventoryAdapt if (!adapter.validate(context, inv)) return false; if (adapter.getSizeInventory(inv) != mItems.length) { - context.warn(new ChatComponentTranslation("mm.info.warning.inventory_was_the_wrong_size", mItems.length, 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; } 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 aad13646..2b576d6f 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,7 +8,6 @@ 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; @@ -20,9 +20,16 @@ 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; @@ -31,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; @@ -362,21 +370,91 @@ class RefCell { 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); } - world.notifyBlockOfNeighborChange(x, y, z, Blocks.air); + try { + sharedBusCachedAdaptorHash = PartSharedItemBus.class.getDeclaredField("cachedAdaptorHash"); + sharedBusCachedAdaptorHash.setAccessible(true); + } catch (Exception e) { + MMMod.LOG.warn("Could not find PartSharedItemBus.cachedAdaptorHash", e); + } + } + + @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 From ecfe52388d7c15cca290f287e63d0cad3fe64cac Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sat, 18 Apr 2026 23:59:00 +0200 Subject: [PATCH 23/43] Fix stacked Fluid Cells where not filled by MM (#42) --- .../common/building/MMInventory.java | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) 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 9d719b32..21d9ddd8 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 @@ -11,7 +11,6 @@ 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; @@ -371,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( From 2b9ed2c9f6745886f6e2c2a1c7d1e480eb8fe362 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 26 Apr 2026 21:32:33 +0200 Subject: [PATCH 24/43] [MatterManipulator] Fix: Matter Manipulator voids half the slab it uses (#39) Co-authored-by: RecursivePineapple --- .../matter_manipulator/common/building/BlockSpec.java | 3 +++ .../matter_manipulator/common/building/PendingBuild.java | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) 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 5253739e..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 @@ -190,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) { 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 c747ba40..51586172 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 @@ -321,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, From 5af793677c96cffdb586b31a9492a84d08e4ddd2 Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 26 Apr 2026 21:37:29 +0200 Subject: [PATCH 25/43] Fix copying Crafting Input Buffer manual slots with Matter Manipulator (#43) Co-authored-by: RecursivePineapple --- .../matter_manipulator/common/utils/InventoryAdapter.java | 4 ++++ 1 file changed, 4 insertions(+) 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 06d06930..77fd26a3 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 @@ -14,6 +14,8 @@ import gregtech.api.metatileentity.implementations.MTEHatchInputBus; import gregtech.api.metatileentity.implementations.MTEHatchOutputBus; import gregtech.api.metatileentity.implementations.MTEMultiBlockBase; +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; @@ -84,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; From 97205b222973757c270207f4dfa2a164602ccd0a Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 26 Apr 2026 21:41:02 +0200 Subject: [PATCH 26/43] [MatterManipulator] Add Open Computers Cable support to Cable Mode (#44) Co-authored-by: RecursivePineapple --- .../common/building/InteropConstants.java | 1 + .../manipulator/ItemMatterManipulator.java | 4 ++++ .../common/items/manipulator/MMState.java | 19 +++++++++++++++++++ .../common/utils/MMUtils.java | 13 +++++++++++++ .../matter_manipulator/common/utils/Mods.java | 2 ++ 5 files changed, 39 insertions(+) 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 ca517119..d0830154 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 @@ -32,6 +32,7 @@ private InteropConstants() {} public static final LazyBlock WIRELESS_CONNECTOR = new LazyBlock(Mods.AE2Stuff, "Wireless"); 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 isAir(Block block, int meta) { 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 7e3e82b1..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 @@ -971,6 +971,10 @@ 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; 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 ddaf1626..d132dbe6 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 @@ -649,11 +649,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) { 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 5755d185..6c0c89ff 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 @@ -1572,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) { 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 34e67088..03a9f664 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 @@ -46,6 +46,7 @@ protected String getEffectiveModID() { IndustrialCraft2(Names.INDUSTRIAL_CRAFT2), LogisticsPipes(Names.LOGISTICS_PIPES), MatterManipulator(Names.MATTER_MANIPULATOR), + OpenComputers(Names.OPEN_COMPUTERS), Minecraft(Names.MINECRAFT) { @Override @@ -88,6 +89,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"; From cc8eda0f467e4d3a8440588a34fad5749e7d161c Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 26 Apr 2026 21:44:53 +0200 Subject: [PATCH 27/43] Fix Matter Manipulator not copying Carpenter's Blocks correctly (#46) Co-authored-by: RecursivePineapple --- dependencies.gradle | 1 + .../CarpentersBlocksAnalysisResult.java | 282 ++++++++++++++++++ .../common/building/PendingBlock.java | 14 + 3 files changed, 297 insertions(+) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/CarpentersBlocksAnalysisResult.java diff --git a/dependencies.gradle b/dependencies.gradle index b046d608..26990195 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -46,6 +46,7 @@ dependencies { compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.64-gtnh:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") { transitive = false } + compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritia:1.93:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } 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/PendingBlock.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/PendingBlock.java index 2b576d6f..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 @@ -58,6 +58,7 @@ 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; @@ -80,6 +81,7 @@ public PendingBlock reset() { this.ae = null; this.arch = null; this.mp = null; + this.cb = null; this.smartCopy = null; this.inventory = null; this.renderOrder = 0; @@ -149,6 +151,7 @@ private List getIntegrations() { if (ae != null) list.add(ae); if (arch != null) list.add(arch); if (mp != null) list.add(mp); + if (cb != null) list.add(cb); if (smartCopy != null) list.add(smartCopy); return list; @@ -245,6 +248,7 @@ 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; @@ -466,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; @@ -493,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; @@ -521,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; @@ -542,6 +551,10 @@ public PendingBlock analyze(TileEntity te, int flags) { 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); } @@ -555,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; } From 37acef5934de822cad490ec22875d48400c9791e Mon Sep 17 00:00:00 2001 From: Julia <78276563+Vlamonster@users.noreply.github.com> Date: Wed, 29 Apr 2026 20:58:19 +0200 Subject: [PATCH 28/43] Implement `IItemProvider` for OpenComputer Components (#47) --- .../common/building/InventoryAnalysis.java | 6 + .../ComputerComponentItemProvider.java | 136 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 src/main/java/com/recursive_pineapple/matter_manipulator/common/building/providers/ComputerComponentItemProvider.java 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 3414d751..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 @@ -10,6 +10,7 @@ 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; @@ -54,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); 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); + } +} From c8835f4b05017dbd03266e715dfdd475d7c8557e Mon Sep 17 00:00:00 2001 From: Julia <78276563+Vlamonster@users.noreply.github.com> Date: Wed, 29 Apr 2026 22:54:10 +0200 Subject: [PATCH 29/43] Use Correct Orientation for OpenComputer TEs (#48) --- .../common/compat/BlockPropertyRegistry.java | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) 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 16f1ba1a..1b653ad6 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 @@ -85,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 { @@ -268,6 +269,7 @@ public static void init() { if (Mods.AE2Stuff.isModLoaded()) initAE2Stuff(); if (Mods.EnderStorage.isModLoaded()) initEnderStorage(); if (Mods.ExtraUtilities.isModLoaded()) initEXU(); + if (Mods.OpenComputers.isModLoaded()) initOpenComputers(); } // #region Vanilla @@ -1172,6 +1174,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); From c8e89e994147f4395fc8d7bff91e22b9e0f41756 Mon Sep 17 00:00:00 2001 From: RecursivePineapple Date: Sat, 2 May 2026 14:57:47 -0400 Subject: [PATCH 30/43] Fix IllegalAccessError for MTEHatch Amperes field (#49) --- dependencies.gradle | 54 +++++++++---------- .../common/building/GTAnalysisResult.java | 8 +-- .../common/uplink/MTEMMUplink.java | 19 ++----- .../common/utils/InventoryAdapter.java | 2 +- 4 files changed, 36 insertions(+), 47 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 26990195..79347d2d 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -38,55 +38,55 @@ def asmVersion = "9.7" dependencies { implementation("com.github.GTNewHorizons:StructureLib:1.4.34:dev") - implementation("com.github.GTNewHorizons:ModularUI:1.3.2:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.51-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.9.47:dev") + implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.59-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.9.57:dev") - compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.17-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.64-gtnh:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.80-gtnh:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-922-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") { transitive = false } compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.93:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Avaritia:1.96:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.14:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.22:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.25:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3: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.3:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.399:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.116:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.476:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.133: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.84-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.22-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.93-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.38-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.19.22:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:waila:1.19.24: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.17-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.15:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-882-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.93:dev") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.19:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-922-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.96:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.22:dev') +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.25:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.399:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.5:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.3:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.116:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.476:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.6:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.133:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.93-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.0:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.1:dev") compileOnly("com.google.auto.value:auto-value-annotations:1.10.1") { transitive = false } annotationProcessor("com.google.auto.value:auto-value:1.10.1") @@ -109,7 +109,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.11-GTNH")) + .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.14-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); 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 83bfcaf2..6dfba645 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 @@ -255,11 +255,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()) { @@ -486,11 +486,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) { 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 6322cd0e..8d21952f 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 @@ -34,7 +34,6 @@ import cpw.mods.fml.relauncher.SideOnly; import gregtech.api.enums.Materials; -import gregtech.api.enums.StructureError; import gregtech.api.enums.TierEU; import gregtech.api.interfaces.IHatchElement; import gregtech.api.interfaces.IIconContainer; @@ -54,6 +53,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; @@ -170,21 +170,10 @@ public boolean checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack i } @Override - protected void validateStructure(Collection errors, NBTTagCompound context) { - super.validateStructure(errors, context); + protected void validateStructure(Collection errors) { + super.validateStructure(errors); - structureInstanceInfo.validate(errors, context); - } - - @Override - protected void localizeStructureErrors( - Collection errors, - NBTTagCompound context, - List lines - ) { - super.localizeStructureErrors(errors, context, lines); - - structureInstanceInfo.localizeStructureErrors(errors, context, lines); + structureInstanceInfo.validate(errors); } private enum UplinkHatchAdder implements IHatchElement { 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 77fd26a3..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 @@ -59,7 +59,7 @@ public boolean validate(BlockAnalyzer.IBlockApplyContext context, IInventory inv IGregTechTileEntity igte = (IGregTechTileEntity) inv; MTEHatchRack rack = (MTEHatchRack) igte.getMetaTileEntity(); - if (rack.heat > 2000) { + if (rack.getHeat() > 2000) { context.error(new ChatComponentTranslation("mm.info.error.qc_rack_is_too_hot")); return false; } From f857ee6f16f20e5ade72ef4590d7d790efaf8e7f Mon Sep 17 00:00:00 2001 From: Lyft <127234178+Lyfts@users.noreply.github.com> Date: Wed, 13 May 2026 20:42:07 +0200 Subject: [PATCH 31/43] Prevent some modes from bypassing grief protection (#51) --- .../common/building/AbstractBuildable.java | 18 ++++++++++++++++-- .../common/building/PendingBuild.java | 2 +- .../common/building/PendingMove.java | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) 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 c569602f..86a79aea 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 @@ -19,8 +19,11 @@ import net.minecraft.tileentity.TileEntity; 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; @@ -432,10 +435,21 @@ 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( 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 51586172..0edf7fef 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 @@ -122,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; } 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 9a383859..2d5e6dd4 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 @@ -108,7 +108,7 @@ 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, "mm.info.error.could_not_move_protected_block.new", From 46e2bd5b5ad0d880798ba47d90bdf0fd7d1995cd Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sat, 16 May 2026 17:15:11 +0200 Subject: [PATCH 32/43] Update IFluidLockable to IFluidLockableMui2 API (#52) --- .../common/building/GTAnalysisResult.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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 6dfba645..93542321 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 @@ -14,6 +14,7 @@ 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; @@ -23,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; @@ -209,8 +210,8 @@ 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 @@ -416,8 +417,9 @@ public boolean apply(IBlockApplyContext ctx) { } // 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 From e17f9777e3da3cdeed13f35473128ee56d9916a5 Mon Sep 17 00:00:00 2001 From: Kogepan229 <73163994+Kogepan229@users.noreply.github.com> Date: Wed, 20 May 2026 19:38:20 +0900 Subject: [PATCH 33/43] Adapt to GT5u changes (#53) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- dependencies.gradle | 44 +++++++++---------- .../common/building/GTAnalysisResult.java | 2 +- .../common/uplink/MTEMMUplink.java | 16 +------ 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 79347d2d..c146e9bc 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -39,52 +39,52 @@ def asmVersion = "9.7" dependencies { implementation("com.github.GTNewHorizons:StructureLib:1.4.34:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.59-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.9.57:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.65-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.10.0:dev") compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.80-gtnh:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-922-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") { transitive = false } + compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.84-gtnh:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") { transitive = false } compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.96:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Avaritia:1.97:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:BloodMagic:1.8.14:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.3:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.25:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:5.2.62:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") { transitive = false } compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.476:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.133:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.527:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.143: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.93-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.38-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.98-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.42-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.19.24:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:waila:1.19.28: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.19-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.10") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.19:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-922-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.96:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.25:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.97:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.25:dev') +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:5.2.62:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.476:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.527:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.6:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.133:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.143:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.93-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.98-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.1:dev") @@ -109,7 +109,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.14-GTNH")) + .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.19-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); 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 93542321..569acb9e 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 @@ -413,7 +413,7 @@ 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 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 8d21952f..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 @@ -13,7 +13,6 @@ import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.List; import javax.annotation.Nonnull; @@ -165,14 +164,8 @@ 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) { - super.validateStructure(errors); - + public void checkMachine(IGregTechTileEntity iGregTechTileEntity, ItemStack itemStack, List errors) { + if (!structure.checkStructure(this, errors)) return; structureInstanceInfo.validate(errors); } @@ -380,11 +373,6 @@ public int getDamageToComponent(ItemStack aStack) { return 0; } - @Override - public boolean explodesOnComponentBreak(ItemStack aStack) { - return false; - } - // #endregion @Override From e4a969b694a380e3514d1238d6f0fc678320719b Mon Sep 17 00:00:00 2001 From: Kogepan229 <73163994+Kogepan229@users.noreply.github.com> Date: Wed, 20 May 2026 21:29:16 +0900 Subject: [PATCH 34/43] Port AE2Stuff Wireless Connector/Hub to AE2 (#50) --- dependencies.gradle | 5 +- gradle/gradle-daemon-jvm.properties | 20 +- .../common/building/AbstractBuildable.java | 2 +- .../common/building/InteropConstants.java | 7 +- .../common/building/WirelessLinkFixer.java | 189 +++++++++--------- .../common/building/movers/BlockMovers.java | 2 +- .../building/movers/WirelessBlockMover.java | 70 +++---- .../common/compat/BlockPropertyRegistry.java | 24 ++- .../matter_manipulator/common/utils/Mods.java | 2 - 9 files changed, 157 insertions(+), 164 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index c146e9bc..8ea4dc2b 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -42,14 +42,12 @@ dependencies { implementation("com.github.GTNewHorizons:ModularUI2:2.3.65-1.7.10:dev") implementation("com.github.GTNewHorizons:GTNHLib:0.10.0:dev") - compileOnly("com.github.GTNewHorizons:ae2stuff:0.10.19-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.84-gtnh:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") { transitive = false } compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritia:1.97:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:bdlib:1.11.0-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.3:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderIO:5.2.62:dev") { transitive = false } @@ -68,8 +66,7 @@ dependencies { 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.19-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.25:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 991a69c7..fa4ed510 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,12 +1,12 @@ #This file is generated by updateDaemonJvm -toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect -toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect -toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect -toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect -toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect -toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect -toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect -toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect +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/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 86a79aea..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 @@ -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)); 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 d0830154..f226b96c 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 @@ -29,12 +29,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; 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 index 39727bb2..e95c2926 100644 --- 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 @@ -9,6 +9,9 @@ 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; @@ -20,15 +23,12 @@ import com.recursive_pineapple.matter_manipulator.common.utils.Mods; import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; -import net.bdew.ae2stuff.machines.wireless.TileWireless; -import net.bdew.lib.block.BlockRef; - import org.joml.Vector3i; public class WirelessLinkFixer { private final MMState state; - private HashMap wirelessLinkMap = null; + private HashMap> wirelessLinkMap = null; private boolean initialized = false; public WirelessLinkFixer(MMState state) { @@ -40,7 +40,7 @@ public boolean hasLinks() { } public void tryInit(World world) { - if (!initialized && Mods.AE2Stuff.isModLoaded()) { + if (!initialized && Mods.AppliedEnergistics2.isModLoaded()) { try { init(world); } catch (Exception e) { @@ -51,12 +51,12 @@ public void tryInit(World world) { } public void tryApply(World world, int x, int y, int z) { - if (wirelessLinkMap != null && Mods.AE2Stuff.isModLoaded()) { + if (wirelessLinkMap != null && Mods.AppliedEnergistics2.isModLoaded()) { apply(world, x, y, z); } } - @Optional(Names.AE2STUFF) + @Optional(Names.APPLIED_ENERGISTICS2) private void init(World world) { initialized = true; @@ -90,13 +90,13 @@ private void init(World world) { try { HashMap allConnectorDests = new HashMap<>(); - HashMap tempMap = 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.WIRELESS_CONNECTOR.matches(block, OreDictionary.WILDCARD_VALUE)) continue; + 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); @@ -107,51 +107,49 @@ private void init(World world) { allConnectorDests.put(srcPacked, destPacked); TileEntity te = world.getTileEntity(sx, sy, sz); - if (!(te instanceof TileWireless tw)) continue; + if (!(te instanceof TileWirelessBase tw)) continue; - scala.Option linkOpt = tw.link().value(); - if (linkOpt.isEmpty()) continue; + for (DimensionalCoord linkCoord : tw.getConnectedCoords()) { + if (linkCoord.getDimension() != world.provider.dimensionId) continue; - BlockRef linkRef = linkOpt.get(); - int lx = linkRef.x(); - int ly = linkRef.y(); - int lz = linkRef.z(); + 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 linkInRegion = lx >= srcMinX && lx <= srcMaxX && ly >= srcMinY && ly <= srcMaxY && lz >= srcMinZ && lz <= srcMaxZ; - boolean shouldInclude = false; + 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 TileWireless linkTw) { - if (linkTw.isHub()) { - shouldInclude = true; + 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; + if (!shouldInclude) continue; - MMMod.LOG.trace( - "[WirelessLink] Found connector at ({},{},{}) linked to ({},{},{}), linkInRegion={}", - sx, - sy, - sz, - lx, - ly, - lz, - linkInRegion - ); + MMMod.LOG.trace( + "[WirelessLink] Found connector at ({},{},{}) linked to ({},{},{}), linkInRegion={}", + sx, + sy, + sz, + lx, + ly, + lz, + linkInRegion + ); - long linkSrcPacked = CoordinatePacker.pack(lx, ly, lz); + long linkSrcPacked = CoordinatePacker.pack(lx, ly, lz); - tempMap.put(srcPacked, new long[] { - destPacked, linkSrcPacked - }); + tempMap.computeIfAbsent(srcPacked, ignored -> new HashSet<>()).add(linkSrcPacked); + } } } } @@ -161,14 +159,15 @@ private void init(World world) { wirelessLinkMap = new HashMap<>(); for (var entry : tempMap.entrySet()) { - long destPacked = entry.getValue()[0]; - long linkSrcPacked = entry.getValue()[1]; - - Long partnerDest = allConnectorDests.get(linkSrcPacked); - if (partnerDest != null) { - wirelessLinkMap.put(destPacked, partnerDest); - } else if (linkExternalHubs) { - wirelessLinkMap.put(destPacked, linkSrcPacked); + 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); + } } } @@ -178,7 +177,8 @@ private void init(World world) { HashSet internalDests = new HashSet<>(allConnectorDests.values()); - HashMap baseMap = new HashMap<>(wirelessLinkMap); + HashMap> baseMap = new HashMap<>(); + wirelessLinkMap.forEach((key, value) -> baseMap.put(key, new HashSet<>(value))); wirelessLinkMap.clear(); MMUtils.forEachArrayOffset(state.config.arraySpan, deltas, d -> { @@ -186,7 +186,6 @@ private void init(World world) { for (var baseEntry : baseMap.entrySet()) { long baseDest = baseEntry.getKey(); - long basePartner = baseEntry.getValue(); int bx = CoordinatePacker.unpackX(baseDest) + d.x; int by = CoordinatePacker.unpackY(baseDest) + d.y; @@ -194,14 +193,16 @@ private void init(World world) { long offsetDest = CoordinatePacker.pack(bx, by, bz); - 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; + 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; - wirelessLinkMap.put(offsetDest, CoordinatePacker.pack(px, py, pz)); - } else { - wirelessLinkMap.put(offsetDest, basePartner); + addLink(wirelessLinkMap, offsetDest, CoordinatePacker.pack(px, py, pz)); + } else { + addLink(wirelessLinkMap, offsetDest, basePartner); + } } } }); @@ -213,15 +214,17 @@ private void init(World world) { } else { MMMod.LOG.debug("[WirelessLink] Built link map with {} entries", wirelessLinkMap.size()); for (var e : wirelessLinkMap.entrySet()) { - MMMod.LOG.trace( - "[WirelessLink] ({},{},{}) -> ({},{},{})", - CoordinatePacker.unpackX(e.getKey()), - CoordinatePacker.unpackY(e.getKey()), - CoordinatePacker.unpackZ(e.getKey()), - CoordinatePacker.unpackX(e.getValue()), - CoordinatePacker.unpackY(e.getValue()), - CoordinatePacker.unpackZ(e.getValue()) - ); + 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 { @@ -229,46 +232,40 @@ private void init(World world) { } } - @Optional(Names.AE2STUFF) + 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); - Long partnerPacked = wirelessLinkMap.get(packed); - if (partnerPacked == null) return; + HashSet partnerPackedSet = wirelessLinkMap.get(packed); + if (partnerPackedSet == null) return; Block block = world.getBlock(x, y, z); - if (!InteropConstants.WIRELESS_CONNECTOR.matches(block, OreDictionary.WILDCARD_VALUE)) { + 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 TileWireless wireless)) { - MMMod.LOG.debug("[WirelessLink] applyWirelessLink({},{},{}): tile entity is not a TileWireless", x, y, z); + if (!(te instanceof TileWirelessBase wireless)) { + MMMod.LOG.debug("[WirelessLink] applyWirelessLink({},{},{}): tile entity is not a TileWirelessBase", x, y, z); return; } - 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.link().set(new BlockRef(px, py, pz)); - - scala.Option verifyLink = wireless.link().value(); - if (verifyLink.isDefined()) { - BlockRef ref = verifyLink.get(); - MMMod.LOG.trace( - "[WirelessLink] Verified link at ({},{},{}): ({},{},{})", - x, - y, - z, - ref.x(), - ref.y(), - ref.z() - ); - } else { - MMMod.LOG.warn("[WirelessLink] Link was NOT persisted at ({},{},{})", x, y, z); + 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/BlockMovers.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/movers/BlockMovers.java index 99852270..a557d277 100644 --- 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 @@ -11,7 +11,7 @@ public enum BlockMovers { FMP(FMPBlockMover.INSTANCE), @Optional(Names.GREG_TECH_NH) GT(GTBlockMover.INSTANCE), - @Optional(Names.AE2STUFF) + @Optional(Names.APPLIED_ENERGISTICS2) Wireless(WirelessBlockMover.INSTANCE), Standard(StandardBlockMover.INSTANCE); 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 index c8a155ea..6fe60d61 100644 --- 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 @@ -1,6 +1,6 @@ package com.recursive_pineapple.matter_manipulator.common.building.movers; -import net.minecraft.tileentity.TileEntity; +import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; import net.minecraftforge.oredict.OreDictionary; @@ -8,56 +8,50 @@ import com.recursive_pineapple.matter_manipulator.common.building.InteropConstants; import com.recursive_pineapple.matter_manipulator.common.building.PendingMove; -import net.bdew.ae2stuff.machines.wireless.TileWireless; -import net.bdew.lib.block.BlockRef; - 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.WIRELESS_CONNECTOR.matches(world.getBlock(x, y, z), OreDictionary.WILDCARD_VALUE); + 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) { - super.place(pendingMove, world, x, y, z, standardBlock); - - if (pendingMove == null) return; - - TileEntity te = world.getTileEntity(x, y, z); - if (te == null) return; + if (pendingMove != null) { + adjustWirelessLinkNbt(standardBlock, pendingMove); + } - adjustWirelessLink((TileWireless) te, pendingMove); + super.place(pendingMove, world, x, y, z, standardBlock); } - private void adjustWirelessLink(TileWireless wireless, PendingMove pendingMove) { - scala.Option linkOpt = wireless.link().value(); - - if (linkOpt.isEmpty()) return; - - BlockRef ref = linkOpt.get(); - int lx = ref.x(); - int ly = ref.y(); - int lz = ref.z(); - - // only adjust if the linked partner was within the source region (i.e. it's also being moved) - if ( - lx >= pendingMove.getSrcMinX() && lx <= pendingMove.getSrcMaxX() && - ly >= pendingMove.getSrcMinY() && - ly <= pendingMove.getSrcMaxY() && - lz >= pendingMove.getSrcMinZ() && - lz <= pendingMove.getSrcMaxZ() - ) { - wireless.link() - .set( - new BlockRef( - lx + pendingMove.getMoveOffsetX(), - ly + pendingMove.getMoveOffsetY(), - lz + pendingMove.getMoveOffsetZ() - ) - ); + 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/compat/BlockPropertyRegistry.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/compat/BlockPropertyRegistry.java index 1b653ad6..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,6 +35,7 @@ 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; @@ -55,6 +56,7 @@ 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; @@ -70,8 +72,6 @@ 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; @@ -266,7 +266,7 @@ 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(); @@ -1078,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 @@ -1489,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 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 03a9f664..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 @@ -15,7 +15,6 @@ public enum Mods implements IMod, ITargetMod { AE2FluidCraft(Names.AE2_FLUID_CRAFT), - AE2Stuff(Names.AE2STUFF), Angelica(Names.ANGELICA), AppliedEnergistics2(Names.APPLIED_ENERGISTICS2), ArchitectureCraft(Names.ARCHITECTURE_CRAFT), @@ -64,7 +63,6 @@ public boolean isModLoaded() { public static class Names { public static final String AE2_FLUID_CRAFT = "ae2fc"; - public static final String AE2STUFF = "ae2stuff"; public static final String ANGELICA = "angelica"; public static final String APPLIED_ENERGISTICS2 = "appliedenergistics2"; public static final String ARCHITECTURE_CRAFT = "ArchitectureCraft"; From 05bb9d5fc12872cf8f44977ea217ac7d03092534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Dip=C4=8D=C3=A1r?= Date: Sat, 30 May 2026 13:16:35 +0200 Subject: [PATCH 35/43] Fix incorrect EnderIO dependency version (#55) --- dependencies.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 8ea4dc2b..c89f76d8 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -50,7 +50,7 @@ dependencies { compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.3:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:5.2.62:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.27:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") { transitive = false } compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } @@ -66,7 +66,7 @@ dependencies { compileOnly rfg.deobf("maven.modrinth:immibis-microblocks:59.1.2") compileOnly rfg.deobf("maven.modrinth:immibis-core:59.1.4") - + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.25:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") From 565380669564fa5b72230c9ca5e119760f8e264b Mon Sep 17 00:00:00 2001 From: Luca <38906805+Luca-Guettinger@users.noreply.github.com> Date: Sun, 7 Jun 2026 01:44:34 +0200 Subject: [PATCH 36/43] Fix crash when replacing CRIBs with proxies with ghost circuit set (#56) --- .../common/building/GTAnalysisResult.java | 16 ++++++++++------ .../common/items/manipulator/MMState.java | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) 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 569acb9e..2ae1f5eb 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 @@ -387,14 +387,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 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 d132dbe6..c9396b6c 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 @@ -448,6 +448,7 @@ private void applySmartCopyCribs(World world, List blocks, Locatio 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; From a56b4d4b3985d45e90311fa6df82cecf4c116be7 Mon Sep 17 00:00:00 2001 From: sisyphussy <221229741+sisyphussy@users.noreply.github.com> Date: Wed, 10 Jun 2026 02:39:52 +0200 Subject: [PATCH 37/43] CapturingTessellator -> LocalTessellator (#57) --- dependencies.gradle | 42 +++--- .../client/rendering/BoxRenderer.java | 31 +++-- .../common/building/GTAnalysisResult.java | 3 +- .../manipulator/FixedLengthVertexBuffer.java | 8 +- .../common/items/manipulator/MMRenderer.java | 2 - .../common/items/manipulator/RenderHints.java | 124 ++++++++++-------- .../manipulator/StreamingVertexBuffer.java | 50 +++---- 7 files changed, 145 insertions(+), 115 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index c89f76d8..6ce9b912 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -37,51 +37,51 @@ def asmVersion = "9.7" dependencies { - implementation("com.github.GTNewHorizons:StructureLib:1.4.34:dev") + implementation("com.github.GTNewHorizons:StructureLib:1.4.38:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.65-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.10.0:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.73-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.11.10:dev") - compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.84-gtnh:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.11") { transitive = false } + compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.88-gtnh:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-977-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") { transitive = false } compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritia:1.97:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.3:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.4:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.27:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.30:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") { transitive = false } compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.527:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.143:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.591:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.160: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.98-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.42-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.101-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.44-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.19.28:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:waila:1.19.30: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:ArchitectureCraft:1.12.11") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.25:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-941-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.32:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-977-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.97:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:5.2.62:dev') +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.30:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.527:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.591:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.6:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.143:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.160:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.98-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.101-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.1:dev") @@ -106,7 +106,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.19-GTNH")) + .using(ds.module("com.github.GTNewHorizons:Baubles-Expanded:2.2.21-GTNH")) .withClassifier("dev") .because("Baubles-Expanded replaces Baubles"); }); 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/GTAnalysisResult.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/building/GTAnalysisResult.java index 2ae1f5eb..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 @@ -299,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; 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/MMRenderer.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMRenderer.java index e5549fb9..ef6a6be9 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 @@ -23,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; @@ -170,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(); 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..2b8d52fb 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,5 +1,7 @@ package com.recursive_pineapple.matter_manipulator.common.items.manipulator; +import static com.gtnewhorizon.gtnhlib.bytebuf.MemoryUtilities.*; + import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Comparator; @@ -15,7 +17,6 @@ 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.event.world.WorldEvent; @@ -23,6 +24,7 @@ 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; @@ -30,6 +32,7 @@ import com.recursive_pineapple.matter_manipulator.MMMod; import org.joml.Vector3d; +import org.joml.Vector3f; import org.joml.Vector3i; import org.lwjgl.LWJGLException; import org.lwjgl.opengl.Display; @@ -66,9 +69,25 @@ public class RenderHints { private static final ExecutorService WORKER_THREAD = Executors.newFixedThreadPool(1); private static Future renderTask; + private static final VertexFormat VBO_FORMAT = DefaultVertexFormat.POSITION_TEXTURE_COLOR; + @Setter private static boolean drawOnTop = false; + public static void orphan() { + if (renderTask != null) { + renderTask.cancel(true); + renderTask = null; + } + if (activeVBO != null) { + activeVBO.orphan(); + } + if (pendingVBO != null) { + pendingVBO.orphan(); + } + HINTS.clear(); + } + public static void reset() { if (renderTask != null) { renderTask.cancel(true); @@ -104,70 +123,80 @@ public static void addHint(int x, int y, int z, Block block, int meta, short[] t public static void onWorldLoad(WorldEvent.Load e) { if (e.world.isRemote) { reset(); + orphan(); } } 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); + 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))); + // Subtract by 0.5 because Hint stores the corner coordinates + final Vector3f eyes = new Vector3f((float) (xd - 0.5f), (float) (yd - 0.5f), (float) (zd - 0.5f)); + hints.sort(Comparator.comparingDouble(info -> -eyes.distanceSquared(info.x, info.y, info.z))); - Tessellator tes = TessellatorManager.startCapturingAndGet(); + final VertexFormat format = VBO_FORMAT; + final int vertexSize = format.getVertexSize(); - tes.startDrawing(GL11.GL_QUADS); + final LocalTessellator tes = TessellatorManager.enterLocalMode(); + tes.startDrawing(GL11.GL_QUADS); - int hintCount = hints.size(); + final int hintCount = hints.size(); - for (int i = 0; i < hintCount; i++) { - hints.get(i).draw(tes, xd, yd, zd, xi, yi, zi); - } + int capacity = hintCount * 6 * 4 * format.getVertexSize(); + final long basePtr = nmemAllocChecked(capacity); + long writePtr = basePtr; + long endPtr = writePtr + capacity; - final var quads = TessellatorManager.stopCapturingToPooledQuads(); + for (int i = 0; i < hintCount; i++) { + hints.get(i).draw(tes, xd, yd, zd, xi, yi, zi); + if (writePtr + tes.getDataSize(vertexSize) > endPtr) { + capacity = Math.max(capacity + tes.getDataSize(vertexSize), (int) (capacity * 1.5)); - final VertexFormat format = DefaultVertexFormat.POSITION_TEXTURE_COLOR; + writePtr = nmemReallocChecked(writePtr, capacity); + endPtr = writePtr + capacity; + } + writePtr = tes.writeToBuffer0(writePtr, format); + } - // noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (vbo) { - vbo.allocate(quads.size() * 4, GL15.GL_STREAM_DRAW); + tes.exitLocalMode(); - ByteBuffer buffer = vbo.map(GL30.GL_MAP_WRITE_BIT); + final int dataSize = (int) (writePtr - basePtr); + final int vertexCount = dataSize / vertexSize; - buffer.rewind(); + // noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (vbo) { + vbo.bind(); + vbo.allocate(vertexCount, GL15.GL_STREAM_DRAW); - long expectedSize = (long) format.getVertexSize() * quads.size() * 4; + final ByteBuffer buffer = vbo.map(GL30.GL_MAP_WRITE_BIT); - 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() - ); + memCopy(basePtr, memAddress0(buffer), dataSize); - return new VBOResult(new Vector3i(xi, yi, zi), 0); - } + buffer.position(0).limit(dataSize); - for (int i = 0, quadsSize = quads.size(); i < quadsSize; i++) { - format.writeQuad(quads.get(i), buffer); - } + nmemFree(basePtr); - buffer.rewind(); + if (dataSize > buffer.capacity()) { + MMMod.LOG.error( + "Could not upload hint VBO: Could not insert hint quads into GL buffer (expectedSize={}, buffer.capacity={})", + dataSize, + buffer.capacity() + ); - vbo.unmap(); + return new VBOResult(new Vector3i(xi, yi, zi), 0); } - return new VBOResult(new Vector3i(xi, yi, zi), quads.size() * 4); - } finally { - TessellatorManager.cleanup(); + vbo.unmap(); + vbo.unbind(); } + + return new VBOResult(new Vector3i(xi, yi, zi), vertexCount); } @SubscribeEvent @@ -195,11 +224,11 @@ public static void onRenderWorldLast(RenderWorldLastEvent e) { Vector3d currentPos = new Vector3d(xd, yd, zd); if (activeVBO == null) { - activeVBO = new StreamingVertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_COLOR, GL11.GL_QUADS); + activeVBO = new StreamingVertexBuffer(VBO_FORMAT, GL11.GL_QUADS); } if (pendingVBO == null) { - pendingVBO = new StreamingVertexBuffer(DefaultVertexFormat.POSITION_TEXTURE_COLOR, GL11.GL_QUADS); + pendingVBO = new StreamingVertexBuffer(VBO_FORMAT, GL11.GL_QUADS); } if (renderTask != null && renderTask.isDone()) { @@ -289,11 +318,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 +348,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 +355,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 +362,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 +369,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 +376,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 +383,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); 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..0f4e3224 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; } } @@ -123,13 +140,11 @@ public void render() { /// since the length is the same. /// Reference: [Buffer Object Streaming](https://wikis.khronos.org/opengl/Buffer_Object_Streaming) public void reallocate() { - bind(); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, length, bufferFlags); - unbind(); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, allocatedBytes, bufferFlags); } public void allocate( - int vertexCount, + int newVertexCount, @MagicConstant(intValues = { GL15.GL_STREAM_DRAW, GL15.GL_STREAM_READ, @@ -142,15 +157,14 @@ public void allocate( GL15.GL_DYNAMIC_COPY, }) int usage ) { - bind(); - this.vertexCount = vertexCount; - this.length = vertexCount * (long) format.getVertexSize(); - this.bufferFlags = usage; + if (newVertexCount != vertexCount) { + this.vertexCount = newVertexCount; + this.allocatedBytes = newVertexCount * (long) format.getVertexSize(); + this.bufferFlags = usage; - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.length, this.bufferFlags); - - unbind(); + GL15.glBufferData(GL15.GL_ARRAY_BUFFER, this.allocatedBytes, this.bufferFlags); + } } /// Maps the buffer into the client memory space (CPU) and returns a [ByteBuffer] wrapper for it. @@ -167,13 +181,11 @@ public ByteBuffer map( ) { if (mapped) throw new IllegalStateException("cannot map the same buffer twice"); - bind(); - if (mappedBuffer != null) { 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())); @@ -181,8 +193,6 @@ public ByteBuffer map( mapped = true; } - unbind(); - return mappedBuffer; } @@ -190,8 +200,6 @@ public ByteBuffer map( public boolean unmap() { if (!mapped) throw new IllegalStateException("cannot unmap the same buffer twice"); - bind(); - boolean valid = true; if (!GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER)) { @@ -208,8 +216,6 @@ public boolean unmap() { mapped = false; - unbind(); - return valid; } } From a9565931fc7fcd15992dc3e06cf3f88b6e17fa6a Mon Sep 17 00:00:00 2001 From: Kogepan229 <73163994+Kogepan229@users.noreply.github.com> Date: Sat, 13 Jun 2026 05:58:18 +0900 Subject: [PATCH 38/43] Use memory card methods for copying AE2 parts (#58) Co-authored-by: Martin Robertz Co-authored-by: UltraProdigy <187078471+UltraProdigy@users.noreply.github.com> --- dependencies.gradle | 33 ++-- settings.gradle | 2 +- .../common/building/AEPartData.java | 167 +++--------------- .../common/items/manipulator/MMState.java | 8 +- .../common/utils/MMUtils.java | 2 +- 5 files changed, 47 insertions(+), 165 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 6ce9b912..b65ed5b0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -39,28 +39,31 @@ def asmVersion = "9.7" dependencies { implementation("com.github.GTNewHorizons:StructureLib:1.4.38:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") - implementation("com.github.GTNewHorizons:ModularUI2:2.3.73-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.11.10:dev") + implementation("com.github.GTNewHorizons:ModularUI2:2.3.74-1.7.10:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.11.15:dev") - compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.88-gtnh:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-977-GTNH:dev") { transitive = false } + api("org.joml:joml:1.10.8") + api("it.unimi.dsi:fastutil:8.5.18") + + compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.91-gtnh:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") { transitive = false } compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritia:1.97:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.4:dev") { transitive = false } compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.30:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:EnderIO:2.10.31:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") { transitive = false } compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.591:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.160:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.601:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.162: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.101-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.44-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.102-GTNH:dev") { transitive = false } + compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.45-GTNH:dev") { transitive = false } compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } compileOnly("com.github.GTNewHorizons:waila:1.19.30:dev") { transitive = false } @@ -68,20 +71,20 @@ dependencies { compileOnly rfg.deobf("maven.modrinth:immibis-core:59.1.4") runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.32:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-977-GTNH:dev") + runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.36:dev') + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.97:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.30:dev') +// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.31:dev') runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.591:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.601:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.6:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.160:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.162:dev") runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.101-GTNH:dev") + runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.102-GTNH:dev") runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.1:dev") diff --git a/settings.gradle b/settings.gradle index ea29dace..bc075499 100644 --- a/settings.gradle +++ b/settings.gradle @@ -17,5 +17,5 @@ pluginManagement { } plugins { - id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24' + id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.26' } 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 f0f794df..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,25 +15,17 @@ import appeng.api.parts.IPartHost; import appeng.api.parts.IPartItem; import appeng.api.parts.PartItemStack; -import appeng.api.storage.StorageName; -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.tile.inventory.IAEStackInventory; -import appeng.tile.inventory.IIAEStackInventory; +import appeng.util.SettingsFrom; -import com.glodblock.github.inventory.IDualHost; 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.utils.MMUtils; -import com.recursive_pineapple.matter_manipulator.common.utils.Mods; -import com.recursive_pineapple.matter_manipulator.common.utils.Mods.Names; /** * Stores data for AE facade parts. Also stores the data for AE cables. @@ -41,23 +33,15 @@ public class AEPartData { public PortableItemStack mPart; - public String mSettingsName = null; - public NBTTagCompound mSettings = null; - public String mCustomName = null; - public NBTTagCompound mBusConfig = null; + public NBTTagCompound mPartSettings = null; public PortableItemStack[] mAEUpgrades = null; - public InventoryAnalysis mConfig = null; - public InventoryAnalysis mFluidConfig = 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() { @@ -70,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(); @@ -101,42 +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 IIAEStackInventory aeInv) { - IAEStackInventory aeConfig = aeInv.getAEInventoryByName(StorageName.CONFIG); - if (aeConfig != null && !aeConfig.isEmpty()) { - mBusConfig = new NBTTagCompound(); - aeConfig.writeToNBT(mBusConfig, "cfg"); - } - } - - if (Mods.AE2FluidCraft.isModLoaded()) { - analyzeFluidConfig(part); - } - - if (part instanceof IPriorityHost priorityHost) { - priority = priorityHost.getPriority(); - } } - @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) - private void analyzeFluidConfig(IPart part) { - if (part instanceof IDualHost dualHost) { - IInventory fluidConfig = dualHost.getConfig(); - if (fluidConfig != null) { - mFluidConfig = InventoryAnalysis.fromInventory(fluidConfig, false); - } - } + 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() { @@ -200,70 +145,29 @@ 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 IIAEStackInventory aeInv && mBusConfig != null) { - IAEStackInventory aeConfig = aeInv.getAEInventoryByName(StorageName.CONFIG); - if (aeConfig != null) { - aeConfig.readFromNBT(mBusConfig, "cfg"); - } - } - - if (Mods.AE2FluidCraft.isModLoaded() && mFluidConfig != null) { - if (!applyFluidConfig(context, part)) { - success = false; - } - } - - if (part instanceof IOreFilterable filterable) { - filterable.setFilter(mOreDict == null ? "" : mOreDict); - } - - if (part instanceof IPriorityHost priorityHost) { - priorityHost.setPriority(priority); - } - return success; } - @com.recursive_pineapple.matter_manipulator.asm.Optional(Names.AE2_FLUID_CRAFT) - private boolean applyFluidConfig(IBlockApplyContext context, IPart part) { - if (part instanceof IDualHost dualHost) { - IInventory fluidConfig = dualHost.getConfig(); - if (fluidConfig != null) { - boolean result = mFluidConfig.apply(context, fluidConfig, false, false); - dualHost.getDualityFluid().loadConfigFromPacket(fluidConfig); - return result; - } - } - return true; - } - /** * Gets any required items for a part that exists in the world. * Effectively a no-op, should not be called with a real IBlockApplyContext. @@ -335,18 +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.mBusConfig = mBusConfig == null ? null : (NBTTagCompound) mBusConfig.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.mFluidConfig = mFluidConfig == null ? null : mFluidConfig.clone(); dup.mAEPatterns = mAEPatterns == null ? null : mAEPatterns.clone(); - dup.mOreDict = mOreDict; dup.mP2POutput = mP2POutput; dup.mP2PFreq = mP2PFreq; - dup.priority = priority; return dup; } @@ -356,15 +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 + ((mBusConfig == null) ? 0 : mBusConfig.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 + ((mFluidConfig == null) ? 0 : mFluidConfig.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()); @@ -380,31 +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 (mBusConfig == null) { - if (other.mBusConfig != null) return false; - } else if (!mBusConfig.equals(other.mBusConfig)) 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 (mFluidConfig == null) { - if (other.mFluidConfig != null) return false; - } else if (!mFluidConfig.equals(other.mFluidConfig)) 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/items/manipulator/MMState.java b/src/main/java/com/recursive_pineapple/matter_manipulator/common/items/manipulator/MMState.java index c9396b6c..76b19317 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 @@ -491,7 +491,7 @@ private void applySmartCopyP2P(World world, List blocks, Location if (partData.mAEPatterns == null) continue; if (partData.isP2P()) continue; - boolean isDual = partData.mFluidConfig != null && p2pDualIfaceStack != null; + boolean isDual = p2pDualIfaceStack != null && isFluidInterfaceStack(partData.mPart); ItemStack replacementStack = isDual ? p2pDualIfaceStack : p2pIfaceStack; ForgeDirection side = AEAnalysisResult.ALL_DIRECTIONS[i]; @@ -532,6 +532,12 @@ private static ItemStack getFluidP2PInterfaceStack() { 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; 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 6c0c89ff..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 @@ -552,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)); From 4552f10a73aad4815e51b76e61b4a5def999f786 Mon Sep 17 00:00:00 2001 From: Algent Date: Sun, 14 Jun 2026 19:28:58 +0200 Subject: [PATCH 39/43] Fix container duping in some specific conditions (#59) --- .../common/building/movers/FMPBlockMover.java | 2 +- .../common/building/movers/StandardBlockMover.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 index 40fee6eb..aa1b9ff8 100644 --- 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 @@ -23,7 +23,7 @@ public FMPBlock remove(PendingMove pendingMove, World world, int x, int y, int z int meta = world.getBlockMetadata(x, y, z); TileMultipart te = (TileMultipart) world.getTileEntity(x, y, z); - world.setTileEntity(x, y, z, null); + world.removeTileEntity(x, y, z); world.setBlockToAir(x, y, z); return new FMPBlock(block, meta, te); 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 index 94df79b6..0839cce0 100644 --- 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 @@ -29,7 +29,7 @@ public StandardBlock remove(PendingMove pendingMove, World world, int x, int y, te.writeToNBT(tag); } - world.setTileEntity(x, y, z, null); + world.removeTileEntity(x, y, z); world.setBlockToAir(x, y, z); return new StandardBlock(block, meta, tag); From c0634a1b344cdf07a781f3774623dce47ec55ca1 Mon Sep 17 00:00:00 2001 From: RecursivePineapple Date: Tue, 16 Jun 2026 11:59:39 -0400 Subject: [PATCH 40/43] Improve Hint Renderer (#60) --- dependencies.gradle | 109 +++--- .../common/items/manipulator/MMRenderer.java | 16 +- .../common/items/manipulator/MMState.java | 7 +- .../common/items/manipulator/RenderHints.java | 316 ++++++++---------- .../manipulator/StreamingVertexBuffer.java | 18 +- 5 files changed, 246 insertions(+), 220 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index b65ed5b0..066464c3 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -36,59 +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.38:dev") implementation("com.github.GTNewHorizons:ModularUI:1.3.4:dev") implementation("com.github.GTNewHorizons:ModularUI2:2.3.74-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.11.15:dev") + implementation("com.github.GTNewHorizons:GTNHLib:0.11.16:dev") api("org.joml:joml:1.10.8") api("it.unimi.dsi:fastutil:8.5.18") - compileOnly("com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.91-gtnh:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") { transitive = false } - compileOnly("com.github.GTNewHorizons:CarpentersBlocks:3.7.2-GTNH") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritia:1.97:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:BloodMagic:1.9.4:dev") { transitive = false } - compileOnly("curse.maven:cofh-lib-220333:2388748") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderIO:2.10.31:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") { transitive = false } - compileOnly(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - compileOnly("com.github.GTNewHorizons:FloodLights:1.5.6:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.601:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:Hodgepodge:2.7.162: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.102-GTNH:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:OpenComputers:1.12.45-GTNH:dev") { transitive = false } - compileOnly("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - compileOnly("com.github.GTNewHorizons:waila:1.19.30:dev") { transitive = false } - - compileOnly rfg.deobf("maven.modrinth:immibis-microblocks:59.1.2") - compileOnly rfg.deobf("maven.modrinth:immibis-core:59.1.4") + devOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.162:dev") + devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.103-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ArchitectureCraft:1.12.13") runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.36:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritia:1.97:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Avaritiaddons:1.9.4-GTNH:dev") runtimeOnlyNonPublishable("com.github.GTNewHorizons:DuraDisplay:1.4.2:dev") -// runtimeOnlyNonPublishable('com.github.GTNewHorizons:EnderIO:2.10.31:dev') - runtimeOnlyNonPublishable("com.github.GTNewHorizons:EnderStorage:1.8.3:dev") - runtimeOnlyNonPublishable(rfg.deobf("curse.maven:extra-utilities-225561:2264384")) - runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.52.601:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:FloodLights:1.5.6:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:ForgeMultipart:1.7.10:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:Hodgepodge:2.7.162:dev") - runtimeOnlyNonPublishable("net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") - runtimeOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.102-GTNH:dev") - runtimeOnlyNonPublishable("thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") { transitive = false } - runtimeOnlyNonPublishable("com.github.GTNewHorizons:WanionLib:1.10.1:dev") - - compileOnly("com.google.auto.value:auto-value-annotations:1.10.1") { transitive = false } + + 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') @@ -101,6 +83,49 @@ dependencies { 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.91-gtnh:dev") + addCompat(ae2, "com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH:dev") + + addCompat(arch, "com.github.GTNewHorizons:ArchitectureCraft:1.12.13") + addCompat(carpenters, "com.github.GTNewHorizons:CarpentersBlocks:3.7.2-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.606:dev") + + addCompat(ic2, "net.industrial-craft:industrialcraft-2:2.2.828-experimental:dev") + + addCompat(oc, "com.github.GTNewHorizons:OpenComputers:1.12.46-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 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 ef6a6be9..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 @@ -160,7 +160,7 @@ private static void clear(EntityPlayer player) { needsHintDraw = false; needsAnalysis = false; - RenderHints.reset(); + RenderHints.INSTANCE.reset(); AboveHotbarHUD.renderTextAboveHotbar("", 0, false, false); } @@ -510,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; @@ -561,7 +561,7 @@ private static void drawHints( } if (pendingBlock.spec.isAir()) { - RenderHints.addHint( + RenderHints.INSTANCE.addHint( pendingBlock.x, pendingBlock.y, pendingBlock.z, @@ -570,7 +570,7 @@ private static void drawHints( tint ); } else { - RenderHints.addHint( + RenderHints.INSTANCE.addHint( pendingBlock.x, pendingBlock.y, pendingBlock.z, @@ -587,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); } } @@ -597,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 76b19317..388f3b75 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 @@ -736,13 +736,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; 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 2b8d52fb..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 @@ -3,8 +3,10 @@ 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; @@ -19,88 +21,116 @@ import net.minecraft.util.IIcon; 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.Vector3f; 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); - - /// 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; + public static final RenderHints INSTANCE = new RenderHints(); - private static final Vector3d LAST_PLAYER_POSITION = new Vector3d(); - private static final Vector3i LAST_RENDERED_PLAYER_POSITION = new Vector3i(); + private static final VertexFormat VBO_FORMAT = DefaultVertexFormat.POSITION_TEXTURE_COLOR; - 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; + private static class RenderState { - /// An opengl context that's active on the background thread and is used for writing to the pending VBO. - private static SharedDrawable backgroundContext; + public final ArrayList hints; + public long expiration; + public boolean depthTest = false; - private static final ExecutorService WORKER_THREAD = Executors.newFixedThreadPool(1); - private static Future renderTask; + public RenderState(ArrayList hints) { + this.hints = hints; + } + } - private static final VertexFormat VBO_FORMAT = DefaultVertexFormat.POSITION_TEXTURE_COLOR; + private static class TessellationResult { - @Setter - private static boolean drawOnTop = false; + public Vector3i vboOffset; + public long dataPtr; + public int dataSize; - public static void orphan() { - 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; } - if (activeVBO != null) { - activeVBO.orphan(); - } - if (pendingVBO != null) { - pendingVBO.orphan(); - } - HINTS.clear(); } - public static void reset() { - if (renderTask != null) { - renderTask.cancel(true); - renderTask = null; - } + /// 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; + + /// 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; - HINTS.clear(); - drawnHints = null; + 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; @@ -113,31 +143,17 @@ 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(); - orphan(); } } - private static VBOResult buildVBO(StreamingVertexBuffer vbo, ArrayList hints, double xd, double yd, double zd, int xi, int yi, int zi) { - try { - if (!backgroundContext.isCurrent()) { - backgroundContext.makeCurrent(); - } - } catch (LWJGLException e) { - throw new RuntimeException("Could not activate background GL context", e); - } - - // Subtract by 0.5 because Hint stores the corner coordinates - final Vector3f eyes = new Vector3f((float) (xd - 0.5f), (float) (yd - 0.5f), (float) (zd - 0.5f)); + private TessellationResult buildVBO(ArrayList hints, Vector3d eyes, Vector3i worldPos) { hints.sort(Comparator.comparingDouble(info -> -eyes.distanceSquared(info.x, info.y, info.z))); final VertexFormat format = VBO_FORMAT; @@ -149,68 +165,44 @@ private static VBOResult buildVBO(StreamingVertexBuffer vbo, ArrayList hin final int hintCount = hints.size(); int capacity = hintCount * 6 * 4 * format.getVertexSize(); - final long basePtr = nmemAllocChecked(capacity); + final int quadSize = tes.getDataSize(vertexSize); + + long basePtr = nmemAllocChecked(capacity); long writePtr = basePtr; long endPtr = writePtr + capacity; for (int i = 0; i < hintCount; i++) { - hints.get(i).draw(tes, xd, yd, zd, xi, yi, zi); - if (writePtr + tes.getDataSize(vertexSize) > endPtr) { - capacity = Math.max(capacity + tes.getDataSize(vertexSize), (int) (capacity * 1.5)); + hints.get(i).draw(tes, eyes.x, eyes.y, eyes.z, worldPos.x, worldPos.y, worldPos.z); - writePtr = nmemReallocChecked(writePtr, capacity); + if (writePtr + quadSize > endPtr) { + capacity = Math.max(capacity + quadSize, (int) (capacity * 1.5)); + + long offset = writePtr - basePtr; + + basePtr = nmemReallocChecked(basePtr, capacity); + + writePtr = basePtr + offset; endPtr = writePtr + capacity; } + writePtr = tes.writeToBuffer0(writePtr, format); } tes.exitLocalMode(); final int dataSize = (int) (writePtr - basePtr); - final int vertexCount = dataSize / vertexSize; - - // noinspection SynchronizationOnLocalVariableOrMethodParameter - synchronized (vbo) { - vbo.bind(); - vbo.allocate(vertexCount, GL15.GL_STREAM_DRAW); - - final ByteBuffer buffer = vbo.map(GL30.GL_MAP_WRITE_BIT); - memCopy(basePtr, memAddress0(buffer), dataSize); - - buffer.position(0).limit(dataSize); - - nmemFree(basePtr); - - if (dataSize > buffer.capacity()) { - MMMod.LOG.error( - "Could not upload hint VBO: Could not insert hint quads into GL buffer (expectedSize={}, buffer.capacity={})", - dataSize, - buffer.capacity() - ); - - return new VBOResult(new Vector3i(xi, yi, zi), 0); - } - - vbo.unmap(); - vbo.unbind(); - } - - return new VBOResult(new Vector3i(xi, yi, zi), vertexCount); + 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"); @@ -219,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); - - if (activeVBO == null) { - activeVBO = new StreamingVertexBuffer(VBO_FORMAT, GL11.GL_QUADS); - } - - if (pendingVBO == null) { - pendingVBO = 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; + Vector3i worldPos = new Vector3i((int) xd, (int) yd, (int) zd); - // 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); - } + // Subtract by 0.5 because Hint stores the corner coordinates + final Vector3d eyes = new Vector3d(xd - 0.5f, yd - 0.5f, zd - 0.5f); - renderTask = WORKER_THREAD.submit(() -> buildVBO(pendingVBO, drawnHints, xd, yd, zd, xi, yi, zi)); + if (vbo == null) { + vbo = new StreamingVertexBuffer(VBO_FORMAT, GL11.GL_QUADS); } - if (activeVBO.getVertexCount() > 0) { + if (vbo.getVertexCount() > 0) { p.startSection("Draw MM Hints"); GL11.glPushMatrix(); @@ -272,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); @@ -280,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(); @@ -298,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(); } @@ -393,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 0f4e3224..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 @@ -140,7 +140,13 @@ public void render() { /// since the length is the same. /// Reference: [Buffer Object Streaming](https://wikis.khronos.org/opengl/Buffer_Object_Streaming) public void reallocate() { + bind(); GL15.glBufferData(GL15.GL_ARRAY_BUFFER, allocatedBytes, bufferFlags); + unbind(); + } + + public void clear() { + this.vertexCount = 0; } public void allocate( @@ -163,14 +169,16 @@ public void allocate( this.allocatedBytes = newVertexCount * (long) format.getVertexSize(); this.bufferFlags = usage; + 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, @@ -181,6 +189,8 @@ public ByteBuffer map( ) { if (mapped) throw new IllegalStateException("cannot map the same buffer twice"); + bind(); + if (mappedBuffer != null) { mappedBuffer.clear(); } @@ -193,6 +203,8 @@ public ByteBuffer map( mapped = true; } + unbind(); + return mappedBuffer; } @@ -200,6 +212,8 @@ public ByteBuffer map( public boolean unmap() { if (!mapped) throw new IllegalStateException("cannot unmap the same buffer twice"); + bind(); + boolean valid = true; if (!GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER)) { @@ -216,6 +230,8 @@ public boolean unmap() { mapped = false; + unbind(); + return valid; } } From 87d7bc2b58957ade11e37a5ee82b29e80add86bb Mon Sep 17 00:00:00 2001 From: LazyFlesh <40587985+LazyFlesh@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:30:28 -0700 Subject: [PATCH 41/43] Fix wireless access point connection (#61) Co-authored-by: LazyFleshWasTaken <40587985+LazyFleshWasTaken@users.noreply.github.com> --- .../common/building/MMInventory.java | 6 +++--- .../common/items/manipulator/MMState.java | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) 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 21d9ddd8..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 @@ -266,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); } @@ -326,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); } @@ -564,7 +564,7 @@ private void consumeItemsFromAE(List requestedItems, List Date: Fri, 19 Jun 2026 16:18:31 -0400 Subject: [PATCH 42/43] Use new isGTRenderer method (#62) --- dependencies.gradle | 20 +++++++++---------- .../common/building/InteropConstants.java | 14 +------------ 2 files changed, 11 insertions(+), 23 deletions(-) diff --git a/dependencies.gradle b/dependencies.gradle index 066464c3..622b071a 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -54,18 +54,18 @@ def waila = false def all = false dependencies { - implementation("com.github.GTNewHorizons:StructureLib:1.4.38:dev") + 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.74-1.7.10:dev") - implementation("com.github.GTNewHorizons:GTNHLib:0.11.16: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.103-GTNH:dev") + devOnlyNonPublishable("com.github.GTNewHorizons:NotEnoughItems:2.8.104-GTNH:dev") - runtimeOnlyNonPublishable('com.github.GTNewHorizons:Angelica:2.1.36: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") @@ -95,11 +95,11 @@ dependencies { }; addCompat(ae2, "com.github.GTNewHorizons:WanionLib:1.10.1:dev") - addCompat(ae2, "com.github.GTNewHorizons:AE2FluidCraft-Rework:1.5.91-gtnh:dev") - addCompat(ae2, "com.github.GTNewHorizons:Applied-Energistics-2-Unofficial:rv3-beta-982-GTNH: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.2-GTNH") + 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") @@ -117,11 +117,11 @@ dependencies { 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.606: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.46-GTNH:dev") + addCompat(oc, "com.github.GTNewHorizons:OpenComputers:1.12.47-GTNH:dev") addCompat(tc, "thaumcraft:Thaumcraft:1.7.10-4.2.3.5:dev") 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 f226b96c..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. */ @@ -68,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) { From 7126f7bf9255a568c7ff27f026037508cfedac6b Mon Sep 17 00:00:00 2001 From: Eldrinn-Elantey <46845681+Eldrinn-Elantey@users.noreply.github.com> Date: Thu, 25 Jun 2026 07:01:58 +0400 Subject: [PATCH 43/43] Fix block warning messages not translating on dedicated servers These warnings were translated with StatCollector on the server, whose locale is always en_US, and the resulting English string was then passed back in as a translation key. Clients never saw a real key, so the text stayed English regardless of their language. Send the key and args instead so the client translates it. Tested in-game with ru_RU. --- .../common/building/PendingBuild.java | 6 ++---- .../common/building/PendingMove.java | 13 +++++-------- 2 files changed, 7 insertions(+), 12 deletions(-) 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 0edf7fef..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 @@ -360,10 +360,8 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { 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, 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 2d5e6dd4..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,7 +10,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; -import net.minecraft.util.StatCollector; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; @@ -191,13 +190,11 @@ public void tryPlaceBlocks(ItemStack stack, EntityPlayer player) { if (!moveBlock(this, world, s, source, d, target)) { sendErrorToPlayer( player, - StatCollector.translateToLocalFormatted( - "mm.info.error.could_not_move_block.new", - s.x, - s.y, - s.z, - source.getChatComponent() - ) + "mm.info.error.could_not_move_block.new", + s.x, + s.y, + s.z, + source.getChatComponent() ); }