diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/AttachCapabilitiesEventMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/AttachCapabilitiesEventMixin.java index a5c168c4..139eca6c 100644 --- a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/AttachCapabilitiesEventMixin.java +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/AttachCapabilitiesEventMixin.java @@ -7,64 +7,53 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider; import net.minecraftforge.event.AttachCapabilitiesEvent; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.EventPriority; -import org.apache.commons.lang3.tuple.Pair; +import org.embeddedt.modernfix.duck.IBatchingCapEvent; import org.embeddedt.modernfix.forge.capability.analysis.CapabilityAnalysisResult; import org.embeddedt.modernfix.forge.capability.analysis.CapabilityAnalyzer; -import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import java.util.ArrayList; import java.util.Comparator; -import java.util.List; +import java.util.HashMap; +import java.util.Map; @Mixin(AttachCapabilitiesEvent.class) -public abstract class AttachCapabilitiesEventMixin extends Event { - @Shadow - public abstract void addCapability(ResourceLocation key, ICapabilityProvider cap); +public abstract class AttachCapabilitiesEventMixin extends Event implements IBatchingCapEvent { + @Shadow @Final + private Map caps; @Unique - private static final EventPriority MFIX_LAST_PRIO = EventPriority.values()[EventPriority.values().length - 1]; - - @Unique - private final List> mfix$batchedCaps = new ArrayList<>(); - - private static final Comparator> MFIX_COMPARATOR = Comparator.comparingInt(pair -> { - var result = CapabilityAnalyzer.analyze(pair.getRight().getClass()); - return result instanceof CapabilityAnalysisResult.Indeterminate ? 1 : 0; - }); - - @Unique - private boolean insertingBatch; + private final Map mfix$phaseMap = new HashMap<>(); /** * @author embeddedt - * @reason batch additions of capability providers within the same phase so that we can hoist all - * the ones with statically known capability types to the beginning of the provider list + * @reason record the current dispatch phase so we can sort within phase groups later */ @WrapMethod(method = "addCapability", remap = false) - private void mfix$batchCaps(ResourceLocation key, ICapabilityProvider cap, Operation original) { - // For simplicity, we don't try to batch on the last phase - if (this.insertingBatch || this.getPhase() == MFIX_LAST_PRIO) { - original.call(key, cap); - } else { - mfix$batchedCaps.add(Pair.of(key, cap)); - } + private void mfix$trackPhase(ResourceLocation key, ICapabilityProvider cap, Operation original) { + original.call(key, cap); + mfix$phaseMap.put(key, this.getPhase()); } @Override - public void setPhase(@NotNull EventPriority value) { - if (!this.mfix$batchedCaps.isEmpty()) { - this.mfix$batchedCaps.sort(MFIX_COMPARATOR); - this.insertingBatch = true; - try { - this.mfix$batchedCaps.forEach(p -> this.addCapability(p.getKey(), p.getValue())); - } finally { - this.insertingBatch = false; - this.mfix$batchedCaps.clear(); - } + public void mfix$sortCaps() { + if (caps.size() < 2) { + return; } - super.setPhase(value); + var entries = new ArrayList<>(caps.entrySet()); + entries.sort(Comparator.comparingInt(e -> { + EventPriority phase = mfix$phaseMap.getOrDefault(e.getKey(), EventPriority.NORMAL); + var result = CapabilityAnalyzer.analyze(e.getValue().getClass()); + // Primary: preserve phase ordering (HIGHEST=0 .. LOWEST=4) + // Secondary: Known/AlwaysEmpty before Indeterminate within each phase + int capKey = result instanceof CapabilityAnalysisResult.Indeterminate ? 1 : 0; + return phase.ordinal() * 2 + capKey; + })); + caps.clear(); + entries.forEach(e -> caps.put(e.getKey(), e.getValue())); + mfix$phaseMap.clear(); } } diff --git a/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/ForgeEventFactoryMixin.java b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/ForgeEventFactoryMixin.java new file mode 100644 index 00000000..b4e19754 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/common/mixin/perf/faster_capabilities/bytecode_analysis/ForgeEventFactoryMixin.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.common.mixin.perf.faster_capabilities.bytecode_analysis; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.eventbus.api.Event; +import net.minecraftforge.eventbus.api.IEventBus; +import org.embeddedt.modernfix.duck.IBatchingCapEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(value = ForgeEventFactory.class, remap = false) +public class ForgeEventFactoryMixin { + @WrapOperation(method = "gatherCapabilities(Lnet/minecraftforge/event/AttachCapabilitiesEvent;Lnet/minecraftforge/common/capabilities/ICapabilityProvider;)Lnet/minecraftforge/common/capabilities/CapabilityDispatcher;", at = @At(value = "INVOKE", target = "Lnet/minecraftforge/eventbus/api/IEventBus;post(Lnet/minecraftforge/eventbus/api/Event;)Z")) + private static boolean modernfix$sortAfterPost(IEventBus instance, Event event, Operation original) { + boolean result = original.call(instance, event); + ((IBatchingCapEvent) event).mfix$sortCaps(); + return result; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/duck/IBatchingCapEvent.java b/src/main/java/org/embeddedt/modernfix/duck/IBatchingCapEvent.java new file mode 100644 index 00000000..4efa3ff3 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/IBatchingCapEvent.java @@ -0,0 +1,5 @@ +package org.embeddedt.modernfix.duck; + +public interface IBatchingCapEvent { + void mfix$sortCaps(); +}