Merge 1.18 into 1.19.2

This commit is contained in:
embeddedt 2024-02-28 20:10:06 -05:00
commit a1ece7da38
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
12 changed files with 0 additions and 1270 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,11 +1,9 @@
package org.embeddedt.modernfix.common.mixin.perf.compact_mojang_registries;
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.registry.LifecycleMap;
import org.spongepowered.asm.mixin.Final;
@ -26,8 +24,6 @@ public abstract class MappedRegistryMixin<T> extends Registry<T> {
@Mutable
private Map<T, Lifecycle> lifecycles;
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);
}
@ -35,12 +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

@ -162,9 +162,7 @@ public class ModernFixEarlyConfig {
private static final ImmutableMap<String, Boolean> DEFAULT_SETTING_OVERRIDES = new DefaultSettingMapBuilder()
.put("mixin.perf.dynamic_resources", 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.perf.deduplicate_climate_parameters", false)
.put("mixin.bugfix.packet_leak", 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,64 +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 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> {
@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,601 +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 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> {
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;
}
}
}