Rewrite AttachCapabilitiesEvent hoisting to not rely on phases
This commit is contained in:
parent
ab8a8068e0
commit
94f1fbf4db
|
|
@ -7,64 +7,53 @@ import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||||
import net.minecraftforge.eventbus.api.Event;
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
import net.minecraftforge.eventbus.api.EventPriority;
|
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.CapabilityAnalysisResult;
|
||||||
import org.embeddedt.modernfix.forge.capability.analysis.CapabilityAnalyzer;
|
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.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@Mixin(AttachCapabilitiesEvent.class)
|
@Mixin(AttachCapabilitiesEvent.class)
|
||||||
public abstract class AttachCapabilitiesEventMixin extends Event {
|
public abstract class AttachCapabilitiesEventMixin extends Event implements IBatchingCapEvent {
|
||||||
@Shadow
|
@Shadow @Final
|
||||||
public abstract void addCapability(ResourceLocation key, ICapabilityProvider cap);
|
private Map<ResourceLocation, ICapabilityProvider> caps;
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private static final EventPriority MFIX_LAST_PRIO = EventPriority.values()[EventPriority.values().length - 1];
|
private final Map<ResourceLocation, EventPriority> mfix$phaseMap = new HashMap<>();
|
||||||
|
|
||||||
@Unique
|
|
||||||
private final List<Pair<ResourceLocation, ICapabilityProvider>> mfix$batchedCaps = new ArrayList<>();
|
|
||||||
|
|
||||||
private static final Comparator<Pair<ResourceLocation, ICapabilityProvider>> MFIX_COMPARATOR = Comparator.comparingInt(pair -> {
|
|
||||||
var result = CapabilityAnalyzer.analyze(pair.getRight().getClass());
|
|
||||||
return result instanceof CapabilityAnalysisResult.Indeterminate ? 1 : 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private boolean insertingBatch;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author embeddedt
|
* @author embeddedt
|
||||||
* @reason batch additions of capability providers within the same phase so that we can hoist all
|
* @reason record the current dispatch phase so we can sort within phase groups later
|
||||||
* the ones with statically known capability types to the beginning of the provider list
|
|
||||||
*/
|
*/
|
||||||
@WrapMethod(method = "addCapability", remap = false)
|
@WrapMethod(method = "addCapability", remap = false)
|
||||||
private void mfix$batchCaps(ResourceLocation key, ICapabilityProvider cap, Operation<Void> original) {
|
private void mfix$trackPhase(ResourceLocation key, ICapabilityProvider cap, Operation<Void> original) {
|
||||||
// For simplicity, we don't try to batch on the last phase
|
original.call(key, cap);
|
||||||
if (this.insertingBatch || this.getPhase() == MFIX_LAST_PRIO) {
|
mfix$phaseMap.put(key, this.getPhase());
|
||||||
original.call(key, cap);
|
|
||||||
} else {
|
|
||||||
mfix$batchedCaps.add(Pair.of(key, cap));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPhase(@NotNull EventPriority value) {
|
public void mfix$sortCaps() {
|
||||||
if (!this.mfix$batchedCaps.isEmpty()) {
|
if (caps.size() < 2) {
|
||||||
this.mfix$batchedCaps.sort(MFIX_COMPARATOR);
|
return;
|
||||||
this.insertingBatch = true;
|
|
||||||
try {
|
|
||||||
this.mfix$batchedCaps.forEach(p -> this.addCapability(p.getKey(), p.getValue()));
|
|
||||||
} finally {
|
|
||||||
this.insertingBatch = false;
|
|
||||||
this.mfix$batchedCaps.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<Boolean> original) {
|
||||||
|
boolean result = original.call(instance, event);
|
||||||
|
((IBatchingCapEvent) event).mfix$sortCaps();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.embeddedt.modernfix.duck;
|
||||||
|
|
||||||
|
public interface IBatchingCapEvent {
|
||||||
|
void mfix$sortCaps();
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user