|
| 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 | +} |
0 commit comments