Remove unfinished/obsolete registry & block optimizations

These patches were unfinished, are known to be buggy, and won't make
sense in modern versions of Minecraft, where many of the underlying
issues have been addressed in other ways
This commit is contained in:
embeddedt 2024-02-28 20:07:56 -05:00
parent 12d69bec45
commit ad1e12a3bb
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 0 additions and 1278 deletions

View File

@ -1,24 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
import org.spongepowered.asm.mixin.Mixin;
@Mixin({ Block.class, Item.class })
@IgnoreOutsideDev
public class DirectObjectMixin implements DirectStorageRegistryObject {
private ResourceLocation mfix$resourceKey;
@Override
public ResourceLocation mfix$getResourceKey() {
return mfix$resourceKey;
}
@Override
public void mfix$setResourceKey(ResourceLocation key) {
mfix$resourceKey = key;
}
}

View File

@ -1,17 +1,11 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableSet;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.core.ModernFixMixinPlugin;
import org.embeddedt.modernfix.registry.DirectStorageRegistryObject;
import org.embeddedt.modernfix.registry.LifecycleMap;
import org.embeddedt.modernfix.registry.RegistryStorage;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
@ -29,12 +23,6 @@ public abstract class MappedRegistryMixin<T> extends Registry<T> {
@Final
@Mutable
private Map<T, Lifecycle> lifecycles;
@Shadow @Final @Mutable
private BiMap<ResourceLocation, T> storage;
@Shadow @Final @Mutable
private BiMap<ResourceKey<T>, T> keyStorage;
private static final ImmutableSet<ResourceLocation> MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item"));
protected MappedRegistryMixin(ResourceKey<? extends Registry<T>> resourceKey, Lifecycle lifecycle) {
super(resourceKey, lifecycle);
@ -43,10 +31,5 @@ public abstract class MappedRegistryMixin<T> extends Registry<T> {
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceStorage(CallbackInfo ci) {
this.lifecycles = new LifecycleMap<>();
if(MFIX$NEW_STORAGE_KEYS.contains(this.key().location())) {
ModernFixMixinPlugin.instance.logger.info("Using experimental registry storage for {}", this.key());
this.storage = (BiMap<ResourceLocation, T>) RegistryStorage.createStorage();
this.keyStorage = (BiMap<ResourceKey<T>, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap<ResourceLocation, DirectStorageRegistryObject>)this.storage);
}
}
}

View File

@ -1,26 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.util.Map;
import java.util.function.Function;
@Mixin(StateDefinition.class)
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
@Shadow @Final private O owner;
@ModifyVariable(method = "<init>", at = @At("HEAD"), ordinal = 0, argsOnly = true)
private static <O, S extends StateHolder<O, S>> StateDefinition.Factory<O, S> replaceMapCodec(StateDefinition.Factory<O, S> factory, Function<O, S> function, O object, StateDefinition.Factory<O, S> factory2, Map<String, Property<?>> map) {
if(object instanceof Block)
return (o, m, c) -> factory.create(o, m, null);
return factory;
}
}

View File

@ -1,58 +0,0 @@
package org.embeddedt.modernfix.common.mixin.perf.dynamic_block_codecs;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Decoder;
import com.mojang.serialization.Encoder;
import com.mojang.serialization.MapCodec;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.function.Supplier;
@Mixin(StateHolder.class)
public class StateHolderMixin {
private static final LoadingCache<Block, MapCodec<BlockState>> MODERNFIX_CODEC_CACHE = CacheBuilder.newBuilder()
.maximumSize(100000)
.build(new CacheLoader<Block, MapCodec<BlockState>>() {
@Override
public MapCodec<BlockState> load(Block block) throws Exception {
Supplier<BlockState> stateSupplier = block::defaultBlockState;
MapCodec<BlockState> mapCodec = MapCodec.of(Encoder.empty(), Decoder.unit(stateSupplier));
for(Property<?> property : block.getStateDefinition().getProperties()) {
mapCodec = StateDefinition.appendPropertyCodec(mapCodec, stateSupplier, property.getName(), property);
}
return mapCodec;
}
});
@Redirect(method = "codec", at = @At(value = "INVOKE", target = "Lcom/mojang/serialization/Codec;dispatch(Ljava/lang/String;Ljava/util/function/Function;Ljava/util/function/Function;)Lcom/mojang/serialization/Codec;", remap = false))
private static <O, S extends StateHolder<O, S>> Codec<S> obtainCodec(Codec<O> codec, String typeKey, Function<S, O> type, Function<O, ? extends Codec<S>> codecFn, Codec<O> codecMethodArg, Function<O, S> stateSupplier) {
return codec.dispatch(typeKey, type, block -> {
if(block instanceof Block) {
S state = stateSupplier.apply(block);
if(state.getValues().isEmpty())
return Codec.unit(state);
MapCodec<S> mapCodec;
try {
mapCodec = (MapCodec<S>)MODERNFIX_CODEC_CACHE.get((Block)block);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
return mapCodec.fieldOf("Properties").codec();
} else {
return codecFn.apply(block);
}
});
}
}

View File

@ -168,9 +168,7 @@ public class ModernFixEarlyConfig {
.put("mixin.perf.dynamic_resources", false)
.putConditionally(() -> !isFabric, "mixin.perf.async_jei", false)
.put("mixin.perf.reuse_datapacks", false)
.put("mixin.perf.dynamic_block_codecs", false)
.put("mixin.feature.direct_stack_trace", false)
.putConditionally(ModernFixPlatformHooks.INSTANCE::isDevEnv, "mixin.perf.rewrite_registry", false)
.put("mixin.perf.clear_mixin_classinfo", false)
.put("mixin.bugfix.packet_leak", false)
.put("mixin.perf.deduplicate_location", false)

View File

@ -1,184 +0,0 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class DirectStorageBiMap<K, V> implements BiMap<K, V> {
private final Function<V, K> keyGetter;
private final BiConsumer<V, K> keySetter;
private final Map<K, V> forwardMap;
public DirectStorageBiMap(Function<V, K> keyGetter, BiConsumer<V, K> keySetter) {
Objects.requireNonNull(keyGetter);
Objects.requireNonNull(keySetter);
this.keyGetter = keyGetter;
this.keySetter = keySetter;
this.forwardMap = new Object2ObjectOpenHashMap<>();
}
@Override
public int size() {
return this.forwardMap.size();
}
@Override
public boolean isEmpty() {
return this.forwardMap.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return this.forwardMap.containsKey(o);
}
@Override
public boolean containsValue(Object o) {
return o != null && keyGetter.apply((V)o) != null;
}
@Override
public V get(Object o) {
return this.forwardMap.get(o);
}
@Override
public V put(K key, V value) {
if(this.forwardMap.containsKey(key) || (value != null && keyGetter.apply(value) != null))
throw new IllegalArgumentException("Already have mapping for " + key);
return forcePut(key, value);
}
@Override
public V remove(Object o) {
return put((K)o, null);
}
@Override
public V forcePut(K key, V value) {
V previousValue = this.forwardMap.put(key, value);
if(previousValue != null)
keySetter.accept(previousValue, null);
if(value != null)
keySetter.accept(value, key);
return previousValue;
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
map.forEach(this::put);
}
@Override
public void clear() {
for(V value : this.forwardMap.values()) {
if(value != null)
keySetter.accept(value, null);
}
this.forwardMap.clear();
}
@NotNull
@Override
public Set<K> keySet() {
return this.forwardMap.keySet();
}
@Override
public Set<V> values() {
return new HashSet<>(this.forwardMap.values());
}
@NotNull
@Override
public Set<Entry<K, V>> entrySet() {
return this.forwardMap.entrySet();
}
@Override
public BiMap<V, K> inverse() {
return new Reverse();
}
class Reverse implements BiMap<V, K> {
@Override
public int size() {
return DirectStorageBiMap.this.size();
}
@Override
public boolean isEmpty() {
return DirectStorageBiMap.this.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return DirectStorageBiMap.this.containsValue(o);
}
@Override
public boolean containsValue(Object o) {
return DirectStorageBiMap.this.containsKey(o);
}
@Override
public K get(Object o) {
return o == null ? null : keyGetter.apply((V)o);
}
@Override
public K put(V key, K value) {
throw new UnsupportedOperationException();
}
@Override
public K remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public K forcePut(V key, K value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends V, ? extends K> map) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<V> keySet() {
return DirectStorageBiMap.this.values();
}
@Override
public Set<K> values() {
return DirectStorageBiMap.this.keySet();
}
@NotNull
@Override
public Set<Entry<V, K>> entrySet() {
return DirectStorageBiMap.this.entrySet().stream()
.map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey()))
.collect(Collectors.toSet());
}
@Override
public BiMap<K, V> inverse() {
return DirectStorageBiMap.this;
}
}
}

View File

@ -1,8 +0,0 @@
package org.embeddedt.modernfix.registry;
import net.minecraft.resources.ResourceLocation;
public interface DirectStorageRegistryObject {
ResourceLocation mfix$getResourceKey();
void mfix$setResourceKey(ResourceLocation key);
}

View File

@ -1,33 +0,0 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import java.util.function.Function;
public class RegistryStorage {
public static BiMap<ResourceLocation, DirectStorageRegistryObject> createStorage() {
return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey);
}
public static <T> BiMap<ResourceKey<T>, DirectStorageRegistryObject> createKeyStorage(ResourceKey<? extends Registry<T>> registryKey, BiMap<ResourceLocation, DirectStorageRegistryObject> storage) {
if(storage instanceof DirectStorageBiMap) {
// silently ignore put/putAll calls on this map
return new TransformingBiMap<ResourceLocation, DirectStorageRegistryObject, ResourceKey<T>, DirectStorageRegistryObject>(storage, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) {
@Override
public DirectStorageRegistryObject put(ResourceKey<T> key, DirectStorageRegistryObject value) {
return null;
}
@Override
public void putAll(Map<? extends ResourceKey<T>, ? extends DirectStorageRegistryObject> map) {
}
};
} else
throw new UnsupportedOperationException();
}
}

View File

@ -1,224 +0,0 @@
package org.embeddedt.modernfix.registry;
import com.google.common.collect.BiMap;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterators;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Function;
public class TransformingBiMap<KFrom, VFrom, KTo, VTo> implements BiMap<KTo, VTo> {
private final BiMap<KFrom, VFrom> delegate;
private final Function<KFrom, KTo> keyFwd;
private final Function<KTo, KFrom> keyBack;
private final Function<VFrom, VTo> valueFwd;
private final Function<VTo, VFrom> valueBack;
public TransformingBiMap(BiMap<KFrom, VFrom> map, Function<KFrom, KTo> keyFwd, Function<KTo, KFrom> keyBack, Function<VFrom, VTo> valueFwd, Function<VTo, VFrom> valueBack) {
this.delegate = map;
this.keyFwd = keyFwd;
this.keyBack = keyBack;
this.valueFwd = valueFwd;
this.valueBack = valueBack;
}
private KFrom keyBack(KTo key) {
return key == null ? null : this.keyBack.apply(key);
}
private KTo keyFwd(KFrom key) {
return key == null ? null : this.keyFwd.apply(key);
}
private VFrom valueBack(VTo value) {
return value == null ? null : this.valueBack.apply(value);
}
private VTo valueFwd(VFrom value) {
return value == null ? null : this.valueFwd.apply(value);
}
@Override
public int size() {
return this.delegate.size();
}
@Override
public boolean isEmpty() {
return this.delegate.isEmpty();
}
@Override
public boolean containsKey(Object o) {
return this.delegate.containsKey(keyBack((KTo)o));
}
@Override
public boolean containsValue(Object o) {
return false;
}
@Override
public VTo get(Object o) {
return valueFwd(this.delegate.get(keyBack((KTo)o)));
}
@Override
public VTo put(KTo key, VTo value) {
return valueFwd(this.delegate.put(keyBack(key), valueBack(value)));
}
@Override
public VTo remove(Object o) {
return valueFwd(this.delegate.remove(keyBack((KTo)o)));
}
@Override
public VTo forcePut(KTo key, VTo value) {
return valueFwd(this.delegate.forcePut(keyBack(key), valueBack(value)));
}
@Override
public void putAll(Map<? extends KTo, ? extends VTo> map) {
map.forEach((key, value) -> {
this.delegate.put(keyBack(key), valueBack(value));
});
}
@Override
public void clear() {
this.delegate.clear();
}
@NotNull
@Override
public Set<KTo> keySet() {
return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack);
}
@Override
public Set<VTo> values() {
return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack);
}
@NotNull
@Override
public Set<Entry<KTo, VTo>> entrySet() {
return new TransformingSet<>(this.delegate.entrySet(), entry -> {
return new AbstractMap.SimpleImmutableEntry<>(keyFwd(entry.getKey()), valueFwd(entry.getValue()));
}, entry -> {
return new AbstractMap.SimpleImmutableEntry<>(keyBack(entry.getKey()), valueBack(entry.getValue()));
});
}
@Override
public BiMap<VTo, KTo> inverse() {
return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack);
}
static class TransformingSet<TypeFrom, TypeTo> implements Set<TypeTo> {
private final Set<TypeFrom> delegate;
private final Function<TypeFrom, TypeTo> forward;
private final Function<TypeTo, TypeFrom> reverse;
public TransformingSet(Set<TypeFrom> set, Function<TypeFrom, TypeTo> forward, Function<TypeTo, TypeFrom> reverse) {
this.delegate = set;
this.forward = forward;
this.reverse = reverse;
}
private TypeTo forward(TypeFrom t) {
return t == null ? null : this.forward.apply(t);
}
private TypeFrom reverse(TypeTo t) {
return t == null ? null : this.reverse.apply(t);
}
@Override
public int size() {
return this.delegate.size();
}
@Override
public boolean isEmpty() {
return this.delegate.isEmpty();
}
@Override
public boolean contains(Object o) {
return this.delegate.contains(reverse((TypeTo)o));
}
@NotNull
@Override
public Iterator<TypeTo> iterator() {
return Iterators.transform(this.delegate.iterator(), this::forward);
}
@NotNull
@Override
public Object[] toArray() {
Object[] array = this.delegate.toArray();
for(int i = 0; i < array.length; i++) {
array[i] = this.forward((TypeFrom)array[i]);
}
return array;
}
@NotNull
@Override
public <T> T[] toArray(@NotNull T[] ts) {
if(ts.length >= this.delegate.size()) {
Object[] setContents = toArray();
System.arraycopy(setContents, 0, ts, 0, Math.min(setContents.length, ts.length));
if(ts.length > setContents.length)
ts[setContents.length] = null;
return ts;
} else {
T[] realArray = Arrays.copyOf(ts, this.delegate.size());
Iterator<TypeTo> iterator = this.iterator();
int i = 0;
while(iterator.hasNext())
realArray[i++] = (T)iterator.next();
return realArray;
}
}
@Override
public boolean add(TypeTo typeFrom) {
return this.delegate.add(reverse(typeFrom));
}
@Override
public boolean remove(Object o) {
return this.delegate.remove(reverse((TypeTo)o));
}
@Override
public boolean containsAll(@NotNull Collection<?> collection) {
return this.delegate.containsAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public boolean addAll(@NotNull Collection<? extends TypeTo> collection) {
return this.delegate.addAll(Collections2.transform(collection, this::reverse));
}
@Override
public boolean retainAll(@NotNull Collection<?> collection) {
return this.delegate.retainAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public boolean removeAll(@NotNull Collection<?> collection) {
return this.delegate.removeAll(Collections2.transform(collection, obj -> reverse((TypeTo)obj)));
}
@Override
public void clear() {
this.delegate.clear();
}
}
}

View File

@ -1,65 +0,0 @@
package org.embeddedt.modernfix.forge.mixin.perf.rewrite_registry;
import com.google.common.collect.BiMap;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.embeddedt.modernfix.forge.registry.FastForgeRegistry;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(value = ForgeRegistry.class, remap = false)
@IgnoreOutsideDev
public class ForgeRegistryMixin<V extends IForgeRegistryEntry<V>> {
@Shadow
@Final
@Mutable
private BiMap<Integer, V> ids;
@Shadow @Final @Mutable private BiMap<ResourceKey<V>, V> keys;
@Shadow @Final private ResourceKey<Registry<V>> key;
@Shadow @Final @Mutable private BiMap<ResourceLocation, V> names;
@Shadow @Final @Mutable private BiMap owners;
private FastForgeRegistry<V> fastRegistry;
/**
* The following code replaces the Forge HashBiMaps with a more efficient data structure based around
* an array list for IDs and one HashMap going from value -> information.
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceBackingMaps(CallbackInfo ci) {
this.fastRegistry = new FastForgeRegistry<>(this.key);
this.ids = fastRegistry.getIds();
this.keys = fastRegistry.getKeys();
this.names = fastRegistry.getNames();
this.owners = fastRegistry.getOwners();
}
@Inject(method = "freeze", at = @At("RETURN"))
private void optimizeRegistry(CallbackInfo ci) {
this.fastRegistry.optimize();
}
@Redirect(method = "sync", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;clear()V"))
private void clearBiMap(BiMap map) {
if(map == this.owners) {
this.fastRegistry.clear();
} else if(map == this.keys || map == this.names || map == this.ids) {
// do nothing, the registry is faster at clearing everything at once
} else
map.clear();
}
}

View File

@ -1,35 +0,0 @@
package org.embeddedt.modernfix.forge.mixin.perf.rewrite_registry;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistry;
import org.embeddedt.modernfix.annotation.IgnoreOutsideDev;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
import java.util.Set;
@Mixin(ForgeRegistry.Snapshot.class)
@IgnoreOutsideDev
public class ForgeRegistrySnapshotMixin {
@Shadow(remap = false) @Final @Mutable public Map<ResourceLocation, Integer> ids;
@Shadow(remap = false) @Final @Mutable public Set<ResourceLocation> dummied;
/**
* The only good reason to use tree maps here is to keep the order the same. But we are tracking IDs
* anyway so order shouldn't matter. We replace the maps that will be most used.
*/
@Inject(method = "<init>", at = @At("RETURN"))
private void replaceSnapshotMaps(CallbackInfo ci) {
this.ids = new Object2ObjectOpenHashMap<>();
this.dummied = new ObjectOpenHashSet<>();
}
}

View File

@ -1,602 +0,0 @@
package org.embeddedt.modernfix.forge.registry;
import com.google.common.collect.BiMap;
import com.google.common.collect.Iterators;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistryEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class FastForgeRegistry<V extends IForgeRegistryEntry<V>> {
private final BiMap<Integer, V> ids;
private final DataFieldBiMap<ResourceLocation> names;
private final DataFieldBiMap<ResourceKey<V>> keys;
private final DataFieldBiMap<?> owners;
private final ResourceKey<Registry<V>> registryKey;
private final ObjectArrayList<V> valuesById;
private final Object2ObjectOpenHashMap<V, RegistryValueData> infoByValue;
private void storeId(V value, int id) {
RegistryValueData pair = infoByValue.computeIfAbsent(value, k -> new RegistryValueData());
pair.id = id;
}
private void updateInfoPairAndClearIfNull(V v, Consumer<RegistryValueData> consumer) {
infoByValue.compute(v, (oldValue, oldPair) -> {
if(oldPair == null)
oldPair = new RegistryValueData();
consumer.accept(oldPair);
if(oldPair.isEmpty())
return null;
else
return oldPair;
});
}
private void ensureArrayCanFitId(int id) {
int desiredSize = id + 1;
while(valuesById.size() < desiredSize) {
valuesById.add(null);
}
}
public void clear() {
this.infoByValue.clear();
for(int i = 0; i < this.valuesById.size(); i++) {
this.valuesById.set(i, null);
}
this.names.clearUnsafe();
this.keys.clearUnsafe();
this.owners.clearUnsafe();
}
public FastForgeRegistry(ResourceKey<Registry<V>> registryKey) {
this.registryKey = registryKey;
this.valuesById = new ObjectArrayList<>();
this.infoByValue = new Object2ObjectOpenHashMap<>();
this.keys = new DataFieldBiMap<>(p -> (ResourceKey<V>) p.key, (p, k) -> p.key = k);
this.owners = new DataFieldBiMap<>(p -> p.overrideOwner, (p, k) -> p.overrideOwner = k);
this.names = new DataFieldBiMap<>(p -> p.location, (p, l) -> p.location = l);
// IDs require a specialized implementation, as we back the K->V direction with an array
this.ids = new BiMap<Integer, V>() {
@Nullable
@Override
public V put(@Nullable Integer key, @Nullable V value) {
RegistryValueData data = infoByValue.get(value);
int unboxedKey = key;
if(data != null && data.id != -1 && data.id != unboxedKey)
throw new IllegalArgumentException("Existing mapping for ID " + data.id + " value " + value + " when new ID " + unboxedKey + " was requested");
ensureArrayCanFitId(unboxedKey);
V oldValue = valuesById.set(unboxedKey, value);
storeId(value, unboxedKey);
return oldValue;
}
@Nullable
@Override
public V forcePut(@Nullable Integer key, @Nullable V value) {
ensureArrayCanFitId(key);
V oldValue = valuesById.set(key, value);
if(oldValue != null) {
updateInfoPairAndClearIfNull(oldValue, pair -> pair.id = -1);
}
storeId(value, key);
return oldValue;
}
@Override
public void putAll(Map<? extends Integer, ? extends V> map) {
map.forEach(this::put);
}
@Override
public Set<V> values() {
return Collections.unmodifiableSet(infoByValue.keySet());
}
@Override
public BiMap<V, Integer> inverse() {
return new BiMap<V, Integer>() {
@Nullable
@Override
public Integer put(@Nullable V key, @Nullable Integer value) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public Integer forcePut(@Nullable V key, @Nullable Integer value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends V, ? extends Integer> map) {
throw new UnsupportedOperationException();
}
@Override
public Set<Integer> values() {
throw new UnsupportedOperationException();
}
@Override
public BiMap<Integer, V> inverse() {
throw new UnsupportedOperationException();
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public Integer get(Object key) {
RegistryValueData pair = infoByValue.get(key);
if(pair == null)
return null;
return pair.id == -1 ? null : pair.id;
}
@Override
public Integer remove(Object key) {
RegistryValueData pair = infoByValue.get(key);
if(pair == null)
return null;
int id = pair.id;
if(id != -1)
valuesById.set(id, null);
updateInfoPairAndClearIfNull((V)key, p -> p.id = -1);
return id == -1 ? null : id;
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<V> keySet() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<Entry<V, Integer>> entrySet() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int size() {
return infoByValue.size();
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public V get(Object key) {
int id = (Integer)key;
if(id < 0 || id >= valuesById.size())
return null;
else
return valuesById.get(id);
}
@Override
public V remove(Object key) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
valuesById.clear();
infoByValue.values().removeIf(pair -> {
pair.id = -1;
return pair.isEmpty();
});
}
@NotNull
@Override
public Set<Integer> keySet() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<Entry<Integer, V>> entrySet() {
throw new UnsupportedOperationException();
}
@Override
public void forEach(BiConsumer<? super Integer, ? super V> action) {
for(int i = 0 ; i < valuesById.size(); i++) {
V val = valuesById.get(i);
if(val != null)
action.accept(i, val);
}
}
};
}
public void optimize() {
this.keys.optimize();
this.owners.optimize();
this.names.optimize();
this.infoByValue.trim();
}
public BiMap<Integer, V> getIds() {
return ids;
}
public BiMap<ResourceKey<V>, V> getKeys() {
return keys;
}
public BiMap<ResourceLocation, V> getNames() {
return names;
}
public DataFieldBiMap<?> getOwners() {
return owners;
}
/**
* Custom BiMap implementation that uses one internal hash map for the K->V direction, and the shared global
* information hash map for the V->K direction.
*/
class DataFieldBiMap<K> implements BiMap<K, V> {
public final Object2ObjectOpenHashMap<K, V> valuesByKey = new Object2ObjectOpenHashMap<>();
private final Function<RegistryValueData, K> getter;
private final BiConsumer<RegistryValueData, K> setter;
public DataFieldBiMap(Function<RegistryValueData, K> getter, BiConsumer<RegistryValueData, K> setter) {
this.getter = getter;
this.setter = setter;
}
public void optimize() {
this.valuesByKey.trim();
}
public void clearUnsafe() {
this.valuesByKey.clear();
}
private V forcePut(@Nullable K key, @Nullable V value, boolean throwOnExisting) {
if(throwOnExisting) {
RegistryValueData dataForValue = infoByValue.get(value);
// null check could be wrong if null is a valid value but doesn't matter in Forge's use
if(dataForValue != null && getter.apply(dataForValue) != null && !Objects.equals(getter.apply(dataForValue), key)) {
throw new IllegalArgumentException("Existing mapping for key " + key + " value " + value);
}
}
V oldValue = valuesByKey.put(key, value);
if(oldValue != null) {
updateInfoPairAndClearIfNull(oldValue, p -> setter.accept(p, null));
}
updateInfoPairAndClearIfNull(value, p -> setter.accept(p, key));
return oldValue;
}
@Nullable
@Override
public V put(@Nullable K key, @Nullable V value) {
return forcePut(key, value, true);
}
@Nullable
@Override
public V forcePut(@Nullable K key, @Nullable V value) {
return forcePut(key, value, false);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
map.forEach(this::put);
}
@Override
public Set<V> values() {
return Collections.unmodifiableSet(infoByValue.keySet());
}
private DataFieldBiMapInverse<K> inverse = null;
@Override
public BiMap<V, K> inverse() {
if(inverse == null)
inverse = new DataFieldBiMapInverse<>(this);
return inverse;
}
@Override
public int size() {
return valuesByKey.size();
}
@Override
public boolean isEmpty() {
return valuesByKey.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return valuesByKey.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return infoByValue.containsKey(value);
}
@Override
public V get(Object key) {
return valuesByKey.get(key);
}
@Override
public V remove(Object key) {
V value = get(key);
if(value == null)
return null;
inverse().remove(value);
return value;
}
@Override
public void clear() {
valuesByKey.values().forEach(v -> updateInfoPairAndClearIfNull(v, p -> p.key = null));
valuesByKey.clear();
}
@NotNull
@Override
public Set<K> keySet() {
return Collections.unmodifiableSet(valuesByKey.keySet());
}
@NotNull
@Override
public Set<Map.Entry<K, V>> entrySet() {
return Collections.unmodifiableSet(valuesByKey.entrySet());
}
}
class DataFieldBiMapInverse<K> implements BiMap<V, K> {
private final DataFieldBiMap<K> forward;
public DataFieldBiMapInverse(DataFieldBiMap<K> forward) {
this.forward = forward;
}
@Nullable
@Override
public K put(@Nullable V key, @Nullable K value) {
throw new UnsupportedOperationException();
}
@Nullable
@Override
public K forcePut(@Nullable V key, @Nullable K value) {
throw new UnsupportedOperationException();
}
@Override
public void putAll(Map<? extends V, ? extends K> map) {
throw new UnsupportedOperationException();
}
@Override
public Set<K> values() {
throw new UnsupportedOperationException();
}
@Override
public BiMap<K, V> inverse() {
return forward;
}
@Override
public int size() {
throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
throw new UnsupportedOperationException();
}
@Override
public boolean containsKey(Object key) {
return infoByValue.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return forward.valuesByKey.containsKey(value);
}
@Override
public K get(Object key) {
RegistryValueData pair = infoByValue.get(key);
if(pair == null)
return null;
else
return forward.getter.apply(pair);
}
@Override
public K remove(Object key) {
RegistryValueData pair = infoByValue.get(key);
if(pair == null)
return null;
else {
K rk = forward.getter.apply(pair);
forward.valuesByKey.remove(rk);
updateInfoPairAndClearIfNull((V)key, p -> forward.setter.accept(p, null));
return rk;
}
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<V> keySet() {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public Set<Entry<V, K>> entrySet() {
throw new UnsupportedOperationException();
}
}
class FastForgeRegistryLocationSet implements Set<ResourceLocation> {
private final Set<ResourceKey<V>> backingSet;
public FastForgeRegistryLocationSet(Set<ResourceKey<V>> backingSet) {
this.backingSet = backingSet;
}
@Override
public int size() {
return backingSet.size();
}
@Override
public boolean isEmpty() {
return backingSet.isEmpty();
}
@Override
public boolean contains(Object o) {
return backingSet.contains(ResourceKey.create(FastForgeRegistry.this.registryKey, (ResourceLocation)o));
}
@NotNull
@Override
public Iterator<ResourceLocation> iterator() {
return Iterators.transform(backingSet.iterator(), ResourceKey::location);
}
@NotNull
@Override
public Object[] toArray() {
Object[] keyArray = backingSet.toArray();
for(int i = 0; i < keyArray.length; i++) {
keyArray[i] = ((ResourceKey<V>)keyArray[i]).location();
}
return keyArray;
}
@NotNull
@Override
public <T> T[] toArray(@NotNull T[] a) {
Object[] keyArray = backingSet.toArray();
T[] finalArray = Arrays.copyOf(a, keyArray.length);
for(int i = 0; i < keyArray.length; i++) {
finalArray[i] = (T)((ResourceKey<V>)keyArray[i]).location();
}
return finalArray;
}
@Override
public boolean add(ResourceLocation resourceLocation) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
for(Object o : c) {
if(!contains(o))
return false;
}
return true;
}
@Override
public boolean addAll(@NotNull Collection<? extends ResourceLocation> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
throw new UnsupportedOperationException();
}
}
static class RegistryValueData {
public ResourceKey<?> key;
public ResourceLocation location;
public int id = -1;
public Object overrideOwner;
boolean isEmpty() {
return key == null && location == null && id == -1 && overrideOwner == null;
}
}
}