From 68fa311ddc656e62d83bd8709c3c550a8a59ea65 Mon Sep 17 00:00:00 2001 From: isXander Date: Fri, 14 Jun 2024 19:13:21 +0100 Subject: [PATCH] bug fix data cache to fix compat with mixin squared --- .../debugify/test/DebugifyTestUtils.java | 16 +++-- .../debugify/test/suites/MC100991.java | 42 +++++++++++ .../debugify/test/suites/MC30391.java | 34 +++++++++ .../MC72151.java} | 4 +- .../MC8187.java} | 5 +- .../MC93018.java} | 7 +- .../debugify/gametest/structures/mc-8187.snbt | 2 +- src/gametest/resources/fabric.mod.json | 8 ++- .../debugify/mixinplugin/BugFixDataCache.java | 70 +++++++++++++++++++ .../mixinplugin/DebugifyErrorHandler.java | 9 +-- .../debugify/mixinplugin/MixinPlugin.java | 44 ++---------- 11 files changed, 176 insertions(+), 65 deletions(-) create mode 100644 src/gametest/java/dev/isxander/debugify/test/suites/MC100991.java create mode 100644 src/gametest/java/dev/isxander/debugify/test/suites/MC30391.java rename src/gametest/java/dev/isxander/debugify/test/{DebugifyMc72151TestSuite.java => suites/MC72151.java} (91%) rename src/gametest/java/dev/isxander/debugify/test/{DebugifyMc8187TestSuite.java => suites/MC8187.java} (98%) rename src/gametest/java/dev/isxander/debugify/test/{DebugifyMc93018TestSuite.java => suites/MC93018.java} (83%) create mode 100644 src/main/java/dev/isxander/debugify/mixinplugin/BugFixDataCache.java diff --git a/src/gametest/java/dev/isxander/debugify/test/DebugifyTestUtils.java b/src/gametest/java/dev/isxander/debugify/test/DebugifyTestUtils.java index a71c230..61c1c7a 100644 --- a/src/gametest/java/dev/isxander/debugify/test/DebugifyTestUtils.java +++ b/src/gametest/java/dev/isxander/debugify/test/DebugifyTestUtils.java @@ -1,11 +1,13 @@ package dev.isxander.debugify.test; +import dev.isxander.debugify.test.suites.MC8187; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; @@ -16,7 +18,7 @@ * Utilities for tests. * * @author Ampflower - * @see DebugifyMc8187TestSuite + * @see MC8187 * @since 1.20.1+1.2 **/ public final class DebugifyTestUtils { @@ -25,10 +27,10 @@ public final class DebugifyTestUtils { /** * Utility function to force-feed saplings bonemeal. */ - static void bonemealIndefinitely(GameTestHelper ctx, BlockPos pos) { - final var player = ctx.makeMockPlayer(); + public static void bonemealIndefinitely(GameTestHelper ctx, BlockPos pos) { + final var player = ctx.makeMockPlayer(GameType.SURVIVAL); - player.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.BONE_MEAL, Item.MAX_STACK_SIZE)); + player.setItemSlot(EquipmentSlot.MAINHAND, new ItemStack(Items.BONE_MEAL, Item.DEFAULT_MAX_STACK_SIZE)); ctx.onEachTick(() -> ctx.useBlock(pos, player)); } @@ -36,21 +38,21 @@ static void bonemealIndefinitely(GameTestHelper ctx, BlockPos pos) { /** * Asserts that blocks in a given area are as expected. */ - static void assertBlockFill(GameTestHelper ctx, BlockPos start, BlockPos end, Predicate test, String message) { + public static void assertBlockFill(GameTestHelper ctx, BlockPos start, BlockPos end, Predicate test, String message) { area(start, end, pos -> ctx.assertBlock(pos, test, message)); } /** * Asserts that blockstates in a given area are as expected. */ - static void assertBlockStateFill(GameTestHelper ctx, BlockPos start, BlockPos end, Predicate test, String message) { + public static void assertBlockStateFill(GameTestHelper ctx, BlockPos start, BlockPos end, Predicate test, String message) { area(start, end, pos -> ctx.assertBlockState(pos, test, () -> message)); } /** * Fills the area with the given blockstate. */ - static void fill(GameTestHelper ctx, BlockPos start, BlockPos end, BlockState state) { + public static void fill(GameTestHelper ctx, BlockPos start, BlockPos end, BlockState state) { area(start, end, pos -> ctx.setBlock(pos, state)); } diff --git a/src/gametest/java/dev/isxander/debugify/test/suites/MC100991.java b/src/gametest/java/dev/isxander/debugify/test/suites/MC100991.java new file mode 100644 index 0000000..891cc41 --- /dev/null +++ b/src/gametest/java/dev/isxander/debugify/test/suites/MC100991.java @@ -0,0 +1,42 @@ +package dev.isxander.debugify.test.suites; + +import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.entity.projectile.FishingHook; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; + +public class MC100991 implements FabricGameTest { + @GameTest(template = EMPTY_STRUCTURE) + public void statTrackRodKill(GameTestHelper ctx) { + Mob targetMob = ctx.spawnWithNoFreeWill(EntityType.CREEPER, new BlockPos(4, 0, 4)); + + Mob attacker = ctx.spawnWithNoFreeWill(EntityType.HUSK, new BlockPos(1, 0, 1)); + ItemStack rodStack = new ItemStack(Items.FISHING_ROD); + attacker.setItemInHand(InteractionHand.MAIN_HAND, rodStack); + + FishingHook hook = ctx.spawn(EntityType.FISHING_BOBBER, targetMob.blockPosition().above(1)); + + Player mockPlayer = ctx.makeMockPlayer(GameType.CREATIVE); + mockPlayer.setItemInHand(InteractionHand.MAIN_HAND, rodStack); + mockPlayer.setPos(attacker.position()); + hook.setOwner(mockPlayer); + + ctx.runAfterDelay(20, () -> { + // yank entity + hook.retrieve(rodStack); + + ctx.runAfterDelay(5, () -> { + System.out.println(targetMob.getCombatTracker().getDeathMessage().getString()); + System.out.println(targetMob.getCombatTracker().getDeathMessage()); + }); + }); + } +} diff --git a/src/gametest/java/dev/isxander/debugify/test/suites/MC30391.java b/src/gametest/java/dev/isxander/debugify/test/suites/MC30391.java new file mode 100644 index 0000000..19643bd --- /dev/null +++ b/src/gametest/java/dev/isxander/debugify/test/suites/MC30391.java @@ -0,0 +1,34 @@ +package dev.isxander.debugify.test.suites; + +import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; + +public class MC30391 implements FabricGameTest { + + @GameTest(template = EMPTY_STRUCTURE) + public void blaze(GameTestHelper ctx) { + testWithEntity(ctx, EntityType.BLAZE); + } + + @GameTest(template = EMPTY_STRUCTURE) + public void chicken(GameTestHelper ctx) { + testWithEntity(ctx, EntityType.CHICKEN); + } + + @GameTest(template = EMPTY_STRUCTURE) + public void wither(GameTestHelper ctx) { + testWithEntity(ctx, EntityType.WITHER); + } + + private void testWithEntity(GameTestHelper ctx, EntityType entityType) { + Mob mob = ctx.spawnWithNoFreeWill(entityType, new BlockPos(0, 4, 0)); + + // TODO: + ctx.runAfterDelay(50, ctx::succeed); + } +} diff --git a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc72151TestSuite.java b/src/gametest/java/dev/isxander/debugify/test/suites/MC72151.java similarity index 91% rename from src/gametest/java/dev/isxander/debugify/test/DebugifyMc72151TestSuite.java rename to src/gametest/java/dev/isxander/debugify/test/suites/MC72151.java index 426ca09..ee50ec5 100644 --- a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc72151TestSuite.java +++ b/src/gametest/java/dev/isxander/debugify/test/suites/MC72151.java @@ -1,4 +1,4 @@ -package dev.isxander.debugify.test; +package dev.isxander.debugify.test.suites; import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; import net.minecraft.core.BlockPos; @@ -9,7 +9,7 @@ import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.projectile.Snowball; -public class DebugifyMc72151TestSuite implements FabricGameTest { +public class MC72151 implements FabricGameTest { @GameTest(template = EMPTY_STRUCTURE) public void mc72151(GameTestHelper ctx) { BlockPos wolfPos = new BlockPos(4, 0, 4); diff --git a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc8187TestSuite.java b/src/gametest/java/dev/isxander/debugify/test/suites/MC8187.java similarity index 98% rename from src/gametest/java/dev/isxander/debugify/test/DebugifyMc8187TestSuite.java rename to src/gametest/java/dev/isxander/debugify/test/suites/MC8187.java index 4aa3272..c7cdfc3 100644 --- a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc8187TestSuite.java +++ b/src/gametest/java/dev/isxander/debugify/test/suites/MC8187.java @@ -1,5 +1,6 @@ -package dev.isxander.debugify.test; +package dev.isxander.debugify.test.suites; +import dev.isxander.debugify.test.DebugifyTestUtils; import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; import net.minecraft.core.BlockPos; import net.minecraft.gametest.framework.GameTest; @@ -16,7 +17,7 @@ * @author Ampflower * @since 1.20.1+1.2 **/ -public class DebugifyMc8187TestSuite implements FabricGameTest { +public class MC8187 implements FabricGameTest { private static final String TEMPLATE = "debugify:mc-8187"; // Adjust this if you move the center, as the tests assume this is accurate. diff --git a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc93018TestSuite.java b/src/gametest/java/dev/isxander/debugify/test/suites/MC93018.java similarity index 83% rename from src/gametest/java/dev/isxander/debugify/test/DebugifyMc93018TestSuite.java rename to src/gametest/java/dev/isxander/debugify/test/suites/MC93018.java index 3cdadad..6843bc9 100644 --- a/src/gametest/java/dev/isxander/debugify/test/DebugifyMc93018TestSuite.java +++ b/src/gametest/java/dev/isxander/debugify/test/suites/MC93018.java @@ -1,4 +1,4 @@ -package dev.isxander.debugify.test; +package dev.isxander.debugify.test.suites; import net.fabricmc.fabric.api.gametest.v1.FabricGameTest; import net.minecraft.gametest.framework.GameTest; @@ -9,12 +9,13 @@ import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; +import net.minecraft.world.level.GameType; -public class DebugifyMc93018TestSuite implements FabricGameTest { +public class MC93018 implements FabricGameTest { @GameTest(template = EMPTY_STRUCTURE) public void mc93018(GameTestHelper ctx) { - Player player = ctx.makeMockPlayer(); + Player player = ctx.makeMockPlayer(GameType.SURVIVAL); player.setItemInHand(InteractionHand.MAIN_HAND, Items.BONE.getDefaultInstance()); ctx.getLevel().addFreshEntity(player); diff --git a/src/gametest/resources/data/debugify/gametest/structures/mc-8187.snbt b/src/gametest/resources/data/debugify/gametest/structures/mc-8187.snbt index 3258154..13a4006 100644 --- a/src/gametest/resources/data/debugify/gametest/structures/mc-8187.snbt +++ b/src/gametest/resources/data/debugify/gametest/structures/mc-8187.snbt @@ -1,6 +1,6 @@ { DataVersion: 3463, - size: [18, 2, 18], + size: [18, 50, 18], data: [ {pos: [8, 0, 8], state: "minecraft:grass_block{snowy:false}"}, {pos: [8, 0, 9], state: "minecraft:grass_block{snowy:false}"}, diff --git a/src/gametest/resources/fabric.mod.json b/src/gametest/resources/fabric.mod.json index 9059fad..216ba8b 100644 --- a/src/gametest/resources/fabric.mod.json +++ b/src/gametest/resources/fabric.mod.json @@ -11,9 +11,11 @@ "dev.isxander.debugify.test.DebugifyGameTestMain" ], "fabric-gametest": [ - "dev.isxander.debugify.test.DebugifyMc72151TestSuite", - "dev.isxander.debugify.test.DebugifyMc8187TestSuite", - "dev.isxander.debugify.test.DebugifyMc93018TestSuite" + "dev.isxander.debugify.test.suites.MC72151", + "dev.isxander.debugify.test.suites.MC8187", + "dev.isxander.debugify.test.suites.MC93018", + "dev.isxander.debugify.test.suites.MC30391", + "dev.isxander.debugify.test.suites.MC100991" ], "debugify": [ "dev.isxander.debugify.test.DebugifyApiTest" diff --git a/src/main/java/dev/isxander/debugify/mixinplugin/BugFixDataCache.java b/src/main/java/dev/isxander/debugify/mixinplugin/BugFixDataCache.java new file mode 100644 index 0000000..ac7af2d --- /dev/null +++ b/src/main/java/dev/isxander/debugify/mixinplugin/BugFixDataCache.java @@ -0,0 +1,70 @@ +package dev.isxander.debugify.mixinplugin; + +import dev.isxander.debugify.fixes.BugFix; +import dev.isxander.debugify.fixes.BugFixData; +import dev.isxander.debugify.fixes.FixCategory; +import dev.isxander.debugify.fixes.OS; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.tree.AnnotationNode; +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.service.MixinService; +import org.spongepowered.asm.util.Annotations; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class BugFixDataCache { + private static final Map bugFixDataByMixinClass = new HashMap<>(); + + static Optional getOrResolve(String mixinClassName) { + return Optional.ofNullable( + bugFixDataByMixinClass.computeIfAbsent(mixinClassName, BugFixDataCache::resolve) + .data() + ); + } + + static Optional getIfResolved(String mixinClassName) { + return Optional.ofNullable(bugFixDataByMixinClass.get(mixinClassName)) + .flatMap(resolved -> Optional.ofNullable(resolved.data())); + } + + private static ResolvedBugFixData resolve(String mixinClassName) { + ClassNode classNode = getClassNode(mixinClassName); + if (classNode == null) { + return new ResolvedBugFixData(null); + } + + AnnotationNode annotationNode = Annotations.getVisible(classNode, BugFix.class); + + if (annotationNode == null) { + return new ResolvedBugFixData(null); + } + + String id = Annotations.getValue(annotationNode, "id"); + FixCategory category = getAnnotationEnumValue(annotationNode, "category", FixCategory.class); + BugFix.Env env = getAnnotationEnumValue(annotationNode, "env", BugFix.Env.class); + boolean enabledByDefault = Annotations.getValue(annotationNode, "enabled", Boolean.valueOf(true)); + List conflicts = Annotations.getValue(annotationNode, "modConflicts", true); + OS requiredOS = Annotations.getValue(annotationNode, "os", OS.class, OS.UNKNOWN); + + return new ResolvedBugFixData(new BugFixData(id, category, env, enabledByDefault, conflicts, requiredOS)); + } + + private static ClassNode getClassNode(String className) { + try { + return MixinService.getService().getBytecodeProvider().getClassNode(className); + } catch (ClassNotFoundException | IOException e) { + return null; + } + } + + private static > T getAnnotationEnumValue(AnnotationNode annotation, String key, Class enumClass) { + String[] value = Annotations.getValue(annotation, key); + return Enum.valueOf(enumClass, value[1]); + } + + record ResolvedBugFixData(@Nullable BugFixData data) {} +} diff --git a/src/main/java/dev/isxander/debugify/mixinplugin/DebugifyErrorHandler.java b/src/main/java/dev/isxander/debugify/mixinplugin/DebugifyErrorHandler.java index d8b6faa..8365cae 100644 --- a/src/main/java/dev/isxander/debugify/mixinplugin/DebugifyErrorHandler.java +++ b/src/main/java/dev/isxander/debugify/mixinplugin/DebugifyErrorHandler.java @@ -27,14 +27,7 @@ public ErrorAction onApplyError(String targetClassName, Throwable th, IMixinInfo } private ErrorAction handleError(ErrorAction usualAction, IMixinInfo mixin) { - ClassNode classNode; - try { - classNode = MixinService.getService().getBytecodeProvider().getClassNode(mixin.getClassName(), false); - } catch (ClassNotFoundException | IOException e) { - return usualAction; - } - - Optional bugFix = MixinPlugin.getBugFixForMixin(classNode); + Optional bugFix = BugFixDataCache.getIfResolved(mixin.getClassName()); if (bugFix.isEmpty()) return usualAction; diff --git a/src/main/java/dev/isxander/debugify/mixinplugin/MixinPlugin.java b/src/main/java/dev/isxander/debugify/mixinplugin/MixinPlugin.java index 6057050..6d6139a 100644 --- a/src/main/java/dev/isxander/debugify/mixinplugin/MixinPlugin.java +++ b/src/main/java/dev/isxander/debugify/mixinplugin/MixinPlugin.java @@ -7,6 +7,7 @@ import dev.isxander.debugify.fixes.BugFixData; import dev.isxander.debugify.fixes.OS; import net.fabricmc.loader.api.FabricLoader; +import org.jetbrains.annotations.Nullable; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; @@ -15,11 +16,10 @@ import org.spongepowered.asm.util.Annotations; import java.io.IOException; -import java.util.List; -import java.util.Optional; -import java.util.Set; +import java.util.*; public class MixinPlugin implements IMixinConfigPlugin { + @Override public void onLoad(String mixinPackage) { Debugify.onPreInitialize(); @@ -48,8 +48,7 @@ public String getRefMapperConfig() { @Override public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - ClassNode mixinClassNode = getClassNode(mixinClassName); - Optional bugFixOptional = getBugFixForMixin(mixinClassNode); + Optional bugFixOptional = BugFixDataCache.getOrResolve(mixinClassName); if (bugFixOptional.isEmpty()) return true; @@ -59,7 +58,7 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { Debugify.CONFIG.registerBugFix(bugFix); if (DebugifyErrorHandler.hasErrored(bugFix)) { - Debugify.LOGGER.warn("Preventing loading of {} mixin, {} because another mixin for the same bug fix failed to apply.", bugFix.bugId(), mixinClassNode.name); + Debugify.LOGGER.warn("Preventing loading of {} mixin, {} because another mixin for the same bug fix failed to apply.", bugFix.bugId(), mixinClassName); return false; } @@ -77,39 +76,6 @@ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { return Debugify.CONFIG.isBugFixEnabled(bugFix); } - static ClassNode getClassNode(String className) { - ClassNode classNode; - - try { - classNode = MixinService.getService().getBytecodeProvider().getClassNode(className); - } catch (ClassNotFoundException | IOException e) { - classNode = null; - } - - return classNode; - } - - static Optional getBugFixForMixin(ClassNode mixinClassNode) { - AnnotationNode annotationNode = Annotations.getVisible(mixinClassNode, BugFix.class); - - if (annotationNode == null) - return Optional.empty(); - - String id = Annotations.getValue(annotationNode, "id"); - FixCategory category = getAnnotationEnumValue(annotationNode, "category", FixCategory.class); - BugFix.Env env = getAnnotationEnumValue(annotationNode, "env", BugFix.Env.class); - boolean enabledByDefault = Annotations.getValue(annotationNode, "enabled", Boolean.valueOf(true)); - List conflicts = Annotations.getValue(annotationNode, "modConflicts", true); - OS requiredOS = Annotations.getValue(annotationNode, "os", OS.class, OS.UNKNOWN); - - return Optional.of(new BugFixData(id, category, env, enabledByDefault, conflicts, requiredOS)); - } - - private static > T getAnnotationEnumValue(AnnotationNode annotation, String key, Class enumClass) { - String[] value = Annotations.getValue(annotation, key); - return Enum.valueOf(enumClass, value[1]); - } - @Override public void acceptTargets(Set myTargets, Set otherTargets) {}