Skip to content

Commit 6eb82e1

Browse files
committed
Deduplicate capability provider lists
1 parent ad6425f commit 6eb82e1

File tree

4 files changed

+178
-0
lines changed

4 files changed

+178
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.embeddedt.modernfix.neoforge.caps;
2+
3+
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
4+
import net.neoforged.neoforge.capabilities.BaseCapability;
5+
import net.neoforged.neoforge.capabilities.BlockCapability;
6+
import net.neoforged.neoforge.capabilities.EntityCapability;
7+
import net.neoforged.neoforge.capabilities.ICapabilityProvider;
8+
import net.neoforged.neoforge.capabilities.ItemCapability;
9+
10+
import java.lang.invoke.MethodHandle;
11+
import java.lang.invoke.MethodHandles;
12+
import java.lang.reflect.Field;
13+
import java.util.HashMap;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
public class CapProviderGetter {
18+
private static final MethodHandle BLOCK_CAP_PROVIDERS, ITEM_CAP_PROVIDERS, ENTITY_CAP_PROVIDERS;
19+
private static final boolean FOUND_PROVIDERS;
20+
21+
static {
22+
MethodHandle bProvider, iProvider, eProvider;
23+
boolean found;
24+
try {
25+
bProvider = obtainForClass(BlockCapability.class);
26+
iProvider = obtainForClass(ItemCapability.class);
27+
eProvider = obtainForClass(EntityCapability.class);
28+
found = true;
29+
} catch(ReflectiveOperationException e) {
30+
e.printStackTrace();
31+
bProvider = null;
32+
iProvider = null;
33+
eProvider = null;
34+
found = false;
35+
}
36+
BLOCK_CAP_PROVIDERS = bProvider;
37+
ITEM_CAP_PROVIDERS = iProvider;
38+
ENTITY_CAP_PROVIDERS = eProvider;
39+
FOUND_PROVIDERS = found;
40+
}
41+
42+
private static MethodHandle obtainForClass(Class<? extends BaseCapability> clz) throws ReflectiveOperationException{
43+
Field field = clz.getDeclaredField("providers");
44+
field.setAccessible(true);
45+
return MethodHandles.publicLookup().unreflectGetter(field);
46+
}
47+
48+
private static final Map<?, List<ICapabilityProvider<?, ?, ?>>> DUMMY_MAP = new HashMap<>();
49+
50+
public static <T extends BaseCapability> Map<?, List<ICapabilityProvider<?, ?, ?>>> getProviderMap(T cap) {
51+
if(!FOUND_PROVIDERS) {
52+
return DUMMY_MAP;
53+
}
54+
55+
try {
56+
if(cap instanceof BlockCapability<?,?> blockCap) {
57+
return (Map<?, List<ICapabilityProvider<?, ?, ?>>>)BLOCK_CAP_PROVIDERS.invokeExact(blockCap);
58+
} else if(cap instanceof ItemCapability<?,?> itemCap) {
59+
return (Map<?, List<ICapabilityProvider<?, ?, ?>>>)ITEM_CAP_PROVIDERS.invokeExact(itemCap);
60+
} else if(cap instanceof EntityCapability<?,?> entityCap) {
61+
return (Map<?, List<ICapabilityProvider<?, ?, ?>>>)ENTITY_CAP_PROVIDERS.invokeExact(entityCap);
62+
} else {
63+
return DUMMY_MAP;
64+
}
65+
} catch(Throwable e) {
66+
throw new RuntimeException("Couldn't get map", e);
67+
}
68+
}
69+
70+
public static void deduplicateCap(BaseCapability<?, ?> cap) {
71+
var map = getProviderMap(cap);
72+
if(map.isEmpty()) {
73+
return;
74+
}
75+
ObjectOpenHashSet<List<ICapabilityProvider<?, ?, ?>>> capLists = new ObjectOpenHashSet<>();
76+
int hits = 0;
77+
for(Map.Entry<?, List<ICapabilityProvider<?, ?, ?>>> entry : map.entrySet()) {
78+
var v = entry.getValue();
79+
var canonicalList = capLists.get(v);
80+
if(canonicalList == null) {
81+
canonicalList = List.copyOf(v);
82+
// This works because List.equals/hashCode is well-defined across any List implementation
83+
capLists.add(canonicalList);
84+
} else {
85+
hits++;
86+
}
87+
entry.setValue(canonicalList);
88+
}
89+
//ModernFix.LOGGER.info("Deduplicated {}/{} lists", hits, map.size());
90+
}
91+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.embeddedt.modernfix.neoforge.caps;
2+
3+
import net.neoforged.neoforge.capabilities.BaseCapability;
4+
5+
import java.util.Set;
6+
7+
public interface ITrackingCapEvent {
8+
Set<BaseCapability<?, ?>> mfix$getTrackedCaps();
9+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.embeddedt.modernfix.neoforge.mixin.perf.capability_list_compaction;
2+
3+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
4+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
5+
import net.neoforged.bus.api.Event;
6+
import net.neoforged.neoforge.capabilities.BaseCapability;
7+
import net.neoforged.neoforge.capabilities.CapabilityHooks;
8+
import org.embeddedt.modernfix.neoforge.caps.CapProviderGetter;
9+
import org.embeddedt.modernfix.neoforge.caps.ITrackingCapEvent;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.injection.At;
12+
13+
@Mixin(value = CapabilityHooks.class, remap = false)
14+
public class CapabilityHooksMixin {
15+
@WrapOperation(method = "init", at = @At(value = "INVOKE", target = "Lnet/neoforged/fml/ModLoader;postEventWrapContainerInModOrder(Lnet/neoforged/bus/api/Event;)V"))
16+
private static void deduplicateCaps(Event event, Operation<Void> original) {
17+
original.call(event);
18+
if(event instanceof ITrackingCapEvent) {
19+
//var stopwatch = Stopwatch.createStarted();
20+
for(BaseCapability<?, ?> cap : ((ITrackingCapEvent)event).mfix$getTrackedCaps()) {
21+
CapProviderGetter.deduplicateCap(cap);
22+
}
23+
//stopwatch.stop();
24+
//ModernFix.LOGGER.info("Deduplicated capability lists in {}", stopwatch);
25+
}
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.embeddedt.modernfix.neoforge.mixin.perf.capability_list_compaction;
2+
3+
import net.minecraft.world.entity.EntityType;
4+
import net.minecraft.world.level.ItemLike;
5+
import net.minecraft.world.level.block.Block;
6+
import net.minecraft.world.level.block.entity.BlockEntityType;
7+
import net.neoforged.neoforge.capabilities.BaseCapability;
8+
import net.neoforged.neoforge.capabilities.BlockCapability;
9+
import net.neoforged.neoforge.capabilities.EntityCapability;
10+
import net.neoforged.neoforge.capabilities.IBlockCapabilityProvider;
11+
import net.neoforged.neoforge.capabilities.ICapabilityProvider;
12+
import net.neoforged.neoforge.capabilities.ItemCapability;
13+
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent;
14+
import org.embeddedt.modernfix.neoforge.caps.ITrackingCapEvent;
15+
import org.spongepowered.asm.mixin.Mixin;
16+
import org.spongepowered.asm.mixin.injection.At;
17+
import org.spongepowered.asm.mixin.injection.Inject;
18+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
19+
20+
import java.util.HashSet;
21+
import java.util.Set;
22+
23+
@Mixin(value = RegisterCapabilitiesEvent.class, remap = false)
24+
public class RegisterCapabilitiesEventMixin implements ITrackingCapEvent {
25+
private final Set<BaseCapability<?, ?>> mfix$trackedCapabilities = new HashSet<>();
26+
27+
@Inject(method = "registerBlock", at = @At("HEAD"))
28+
private void trackBlockCap(BlockCapability<?, ?> capability, IBlockCapabilityProvider<?, ?> provider, Block[] blocks, CallbackInfo ci) {
29+
mfix$trackedCapabilities.add(capability);
30+
}
31+
32+
@Inject(method = "registerBlockEntity", at = @At("HEAD"))
33+
private void trackBlockEntityCap(BlockCapability<?, ?> capability, BlockEntityType<?> type, ICapabilityProvider<?, ?, ?> provider, CallbackInfo ci) {
34+
mfix$trackedCapabilities.add(capability);
35+
}
36+
37+
@Inject(method = "registerItem", at = @At("HEAD"))
38+
private void trackItemCap(ItemCapability<?, ?> capability, ICapabilityProvider<?, ?, ?> provider, ItemLike[] items, CallbackInfo ci) {
39+
mfix$trackedCapabilities.add(capability);
40+
}
41+
42+
@Inject(method = "registerEntity", at = @At("HEAD"))
43+
private void trackEntityCap(EntityCapability<?, ?> capability, EntityType<?> type, ICapabilityProvider<?, ?, ?> provider, CallbackInfo ci) {
44+
mfix$trackedCapabilities.add(capability);
45+
}
46+
47+
@Override
48+
public Set<BaseCapability<?, ?>> mfix$getTrackedCaps() {
49+
return mfix$trackedCapabilities;
50+
}
51+
}

0 commit comments

Comments
 (0)