From ae9cb298d19abaedbaac79b047ccb15d45a757be Mon Sep 17 00:00:00 2001 From: tier940 Date: Fri, 10 Apr 2026 19:32:46 +0900 Subject: [PATCH 1/3] Fixed GT main-hand + TiC off-hand combination dropping items twice --- CHANGELOG.md | 6 ++++ buildscript.properties | 2 +- .../gtmt/integration/tic/DualToolHandler.java | 29 +++++++++++-------- .../mixins/tic/MixinDualToolHarvestUtils.java | 28 +++++++++++------- .../gtmt/mixins/tic/MixinForgeHooks.java | 11 +++---- .../gtmt/mixins/tic/MixinItemGTTool.java | 1 - 6 files changed, 45 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af2a8e1..10e875e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# v1.2.4 +## Tinkers' Construct Integration +- Fixed GT main-hand + TiC off-hand combination dropping items twice + +* * * + # v1.2.3 ## Tinkers' Construct Integration - GT tool in main-hand + TiC tool in off-hand (and vice versa) now mines blocks using the off-hand tool diff --git a/buildscript.properties b/buildscript.properties index d74d1ef..7fc05dc 100644 --- a/buildscript.properties +++ b/buildscript.properties @@ -30,7 +30,7 @@ autoUpdateBuildScript = false minecraftVersion = 1.12.2 # Debug mod compatibility -debug_all = false +debug_all = true debug_chisel = false debug_bbw = false debug_tic = false diff --git a/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java b/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java index 7fe581c..6099117 100644 --- a/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java +++ b/src/main/java/com/github/gtexpert/gtmt/integration/tic/DualToolHandler.java @@ -18,14 +18,14 @@ import slimeknights.tconstruct.library.utils.ToolHelper; /** - * Extends TiC's off-hand mining to work with GT tools. + * Extends TiC's off-hand mining to cover GT + TiC cross-hand combinations. * * * - * Runs at {@link EventPriority#LOW} so TiC's own {@code BreakSpeed} handler fires first. + * Registered at {@link EventPriority#LOW} so TiC's own {@code BreakSpeed} handler fires first. */ public final class DualToolHandler { @@ -46,6 +46,8 @@ public static void onBreakSpeed(net.minecraftforge.event.entity.player.PlayerEve float speed = ToolHelper.calcDigSpeed(offhand, state); BlockPos pos = event.getPos(); if (pos == null || !ForgeHooks.canHarvestBlock(state.getBlock(), player, player.world, pos)) { + // Block hardness is divided by 100 instead of 30 when canHarvestBlock + // returns false; multiply to restore TiC's intended mining speed. speed *= (100.0f / 30.0f); } event.setNewSpeed(speed); @@ -66,10 +68,11 @@ public static void onBreakSpeed(net.minecraftforge.event.entity.player.PlayerEve /** * Damages the off-hand tool after a block is broken via off-hand mining. - * For Case 1, also spawns drops using the off-hand TiC tool when - * {@code ForgeHooks.canHarvestBlock} returns {@code false} for the GT main-hand tool. + * Drops are handled by vanilla: {@code MixinEntityPlayer} makes + * {@code player.canHarvestBlock()} return {@code true} for GT+TiC combos, + * so {@code tryHarvestBlock} calls {@code harvestBlock} exactly once. * - * Runs at {@link EventPriority#LOWEST} so cancellations by other mods are resolved first. + * Registered at {@link EventPriority#LOWEST} so other mods' cancellations are resolved first. */ @SubscribeEvent(priority = EventPriority.LOWEST) public static void onBlockBreak(BlockEvent.BreakEvent event) { @@ -87,10 +90,9 @@ public static void onBlockBreak(BlockEvent.BreakEvent event) { if (mainhand.getItem() instanceof IGTTool && offhand.getItem() instanceof TinkerToolCore) { if (!canHarvestForDrops(player, mainhand, state) && ToolHelper.canHarvest(offhand, state)) { - if (!ForgeHooks.canHarvestBlock(state.getBlock(), player, player.world, event.getPos())) { - state.getBlock().harvestBlock(player.world, player, event.getPos(), state, - player.world.getTileEntity(event.getPos()), offhand); - } + // Drops are provided by vanilla: MixinEntityPlayer makes player.canHarvestBlock() + // return true for this combo, so tryHarvestBlock's harvestBlock() call fires normally. + // Only damage the TiC off-hand tool for its contribution to the harvest. offhand.getItem().onBlockDestroyed(offhand, player.world, state, event.getPos(), player); } @@ -111,7 +113,10 @@ private static boolean canHarvestForDrops(EntityPlayer player, ItemStack stack, if (stack.isEmpty() || toolType == null) { return player.canHarvestBlock(state); } - int level = stack.getItem().getHarvestLevel(stack, toolType, player, state); + // Null player: bypasses MixinItemGTTool's off-hand elevation so the raw level of + // this specific tool is returned. The fallback only fires when the tool itself + // cannot harvest. + int level = stack.getItem().getHarvestLevel(stack, toolType, null, state); if (level < 0) return player.canHarvestBlock(state); return level >= block.getHarvestLevel(state); } diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java index 24ef634..4ccd1b7 100644 --- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java +++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinDualToolHarvestUtils.java @@ -40,7 +40,7 @@ private static void gtOffHandSupportState(EntityLivingBase entity, IBlockState s (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool); if (!isCrossCombo) return; - if (!gtmt$canHarvest(tool, state, player) && gtmt$canHarvest(offhand, state, player)) { + if (gtmt$shouldPreferOffhand(tool, offhand, state)) { cir.setReturnValue(true); } } @@ -61,23 +61,29 @@ private static void gtOffHandSupportPos(EntityLivingBase entity, BlockPos pos, (tool.getItem() instanceof TinkerToolCore && offhand.getItem() instanceof IGTTool); if (!isCrossCombo) return; - if (!gtmt$canHarvest(tool, state, player) && gtmt$canHarvest(offhand, state, player)) { + if (gtmt$shouldPreferOffhand(tool, offhand, state)) { cir.setReturnValue(true); } } - /** - * Always passes {@code null} for player to {@code getHarvestLevel} to get the raw level, - * bypassing both TiC's {@code shouldUseOffhand} recursion guard and GT's harvest-level - * elevation from {@link MixinItemGTTool}. - */ @Unique - private static boolean gtmt$canHarvest(ItemStack stack, IBlockState state, EntityPlayer player) { - if (state.getMaterial().isToolNotRequired()) return true; + private static boolean gtmt$shouldPreferOffhand(ItemStack main, ItemStack offhand, IBlockState state) { + if (state.getMaterial().isToolNotRequired()) return false; Block block = state.getBlock(); String toolType = block.getHarvestTool(state); + if (toolType == null) return false; int requiredLevel = block.getHarvestLevel(state); - if (toolType == null || requiredLevel < 0) return true; - return stack.getItem().getHarvestLevel(stack, toolType, null, state) >= requiredLevel; + + int mainRaw = main.getItem().getHarvestLevel(main, toolType, null, state); + int offRaw = offhand.getItem().getHarvestLevel(offhand, toolType, null, state); + + // Offhand can harvest, main cannot. + if (requiredLevel >= 0 && offRaw >= requiredLevel && mainRaw < requiredLevel) return true; + + // Main is wrong type, offhand is correct type — prefer the appropriate tool + // even when neither can produce drops (mirrors TiC+TiC cross-type behaviour). + if (mainRaw < 0 && offRaw >= 0) return true; + + return false; } } diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java index 1c10d02..cb1a2a5 100644 --- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java +++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java @@ -19,17 +19,14 @@ import slimeknights.tconstruct.library.utils.ToolHelper; /** - * Allows GT main-hand + TiC off-hand to pass the harvest check. - * NOTE: ForgeHooks is loaded before MixinBooter's late phase, so this Mixin - * may not apply in all environments. {@link MixinItemGTTool} is the primary fix. + * Allows GT main-hand + TiC off-hand to pass {@code ForgeHooks.canHarvestBlock}. + * ForgeHooks loads before MixinBooter's late phase, so this Mixin may not apply + * in all environments; {@link MixinItemGTTool} is the primary coverage. */ @Mixin(value = ForgeHooks.class, remap = false) public class MixinForgeHooks { - @Inject( - method = "canHarvestBlock(Lnet/minecraft/block/Block;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/world/IBlockAccess;Lnet/minecraft/util/math/BlockPos;)Z", - at = @At("RETURN"), - cancellable = true) + @Inject(method = "canHarvestBlock", at = @At("RETURN"), cancellable = true) private static void checkOffHandTool(Block block, EntityPlayer player, IBlockAccess world, BlockPos pos, CallbackInfoReturnable cir) { if (Boolean.TRUE.equals(cir.getReturnValue())) return; diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinItemGTTool.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinItemGTTool.java index ab7d985..dcdba8e 100644 --- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinItemGTTool.java +++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinItemGTTool.java @@ -21,7 +21,6 @@ /** * Replicates the hand-swap logic from {@code ToolCore.onBlockStartBreak} for GT tools. - * */ @Mixin(value = ItemGTTool.class, remap = false) public class MixinItemGTTool { From 84c9ddc65e837efcc332b9d83c916c6ff2307d00 Mon Sep 17 00:00:00 2001 From: tier940 Date: Fri, 10 Apr 2026 19:33:27 +0900 Subject: [PATCH 2/3] fix --- buildscript.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildscript.properties b/buildscript.properties index 7fc05dc..d74d1ef 100644 --- a/buildscript.properties +++ b/buildscript.properties @@ -30,7 +30,7 @@ autoUpdateBuildScript = false minecraftVersion = 1.12.2 # Debug mod compatibility -debug_all = true +debug_all = false debug_chisel = false debug_bbw = false debug_tic = false From ede87c9ff3a158f486da6aaba895a5b1ed2f040d Mon Sep 17 00:00:00 2001 From: tier940 Date: Fri, 10 Apr 2026 20:49:42 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E3=83=AC=E3=83=93=E3=83=A5=E3=83=BC?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integration/tic/materials/ToolMaterialRegistrar.java | 6 +++--- .../github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/github/gtexpert/gtmt/integration/tic/materials/ToolMaterialRegistrar.java b/src/main/java/com/github/gtexpert/gtmt/integration/tic/materials/ToolMaterialRegistrar.java index f843956..6170daf 100644 --- a/src/main/java/com/github/gtexpert/gtmt/integration/tic/materials/ToolMaterialRegistrar.java +++ b/src/main/java/com/github/gtexpert/gtmt/integration/tic/materials/ToolMaterialRegistrar.java @@ -239,9 +239,9 @@ private static void registerHarvestLevelNames() { // Override TiC's native names (0–4) with vanilla/GT naming convention // so that the same harvest level shows the same name in both GT and TiC tooltips. java.util.Map ticNames = slimeknights.tconstruct.library.utils.HarvestLevels.harvestLevelNames; - ticNames.put(0, TextFormatting.DARK_GREEN + "Wood"); - ticNames.put(1, TextFormatting.GRAY + "Stone"); - ticNames.put(2, TextFormatting.WHITE + "Iron"); + ticNames.put(0, TextFormatting.GOLD + "Wood"); + ticNames.put(1, TextFormatting.DARK_GRAY + "Stone"); + ticNames.put(2, TextFormatting.GRAY + "Iron"); ticNames.put(3, TextFormatting.AQUA + "Diamond"); // Level 4 (Cobalt) — no vanilla equivalent, keep TiC's name diff --git a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java index cb1a2a5..6f10314 100644 --- a/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java +++ b/src/main/java/com/github/gtexpert/gtmt/mixins/tic/MixinForgeHooks.java @@ -26,10 +26,13 @@ @Mixin(value = ForgeHooks.class, remap = false) public class MixinForgeHooks { - @Inject(method = "canHarvestBlock", at = @At("RETURN"), cancellable = true) + @Inject(method = "canHarvestBlock(Lnet/minecraft/block/Block;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/world/IBlockAccess;Lnet/minecraft/util/math/BlockPos;)Z", + at = @At("RETURN"), + cancellable = true) private static void checkOffHandTool(Block block, EntityPlayer player, IBlockAccess world, BlockPos pos, CallbackInfoReturnable cir) { if (Boolean.TRUE.equals(cir.getReturnValue())) return; + if (pos == null) return; ItemStack mainhand = player.getHeldItemMainhand(); ItemStack offhand = player.getHeldItemOffhand();