From 8b698452fdb59cf076968d84f494603572c31860 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Thu, 10 Aug 2023 22:42:30 -0400 Subject: [PATCH] Compact Mojang registries (not available yet) --- .../DirectObjectMixin.java | 22 ++ .../MappedRegistryMixin.java | 52 ++++ .../registry/DirectStorageBiMap.java | 183 ++++++++++++++ .../registry/DirectStorageRegistryObject.java | 8 + .../modernfix/registry/LifecycleMap.java | 20 ++ .../modernfix/registry/RegistryStorage.java | 34 +++ .../modernfix/registry/TransformingBiMap.java | 224 ++++++++++++++++++ 7 files changed, 543 insertions(+) create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageBiMap.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java create mode 100644 common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java new file mode 100644 index 00000000..45d8d621 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/DirectObjectMixin.java @@ -0,0 +1,22 @@ +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.registry.DirectStorageRegistryObject; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin({ Block.class, Item.class }) +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; + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java new file mode 100644 index 00000000..c5e9c813 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/compact_mojang_registries/MappedRegistryMixin.java @@ -0,0 +1,52 @@ +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; +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; + +@Mixin(MappedRegistry.class) +@IgnoreOutsideDev +public abstract class MappedRegistryMixin extends Registry { + @Shadow + @Final + @Mutable + private Map lifecycles; + @Shadow @Final @Mutable + private BiMap storage; + @Shadow @Final @Mutable + private BiMap, T> keyStorage; + + private static final ImmutableSet MFIX$NEW_STORAGE_KEYS = ImmutableSet.of(new ResourceLocation("block"), new ResourceLocation("item")); + + protected MappedRegistryMixin(ResourceKey> resourceKey, Lifecycle lifecycle) { + super(resourceKey, lifecycle); + } + + @Inject(method = "", 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) RegistryStorage.createStorage(); + this.keyStorage = (BiMap, T>)RegistryStorage.createKeyStorage(this.key(), (BiMap)this.storage); + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageBiMap.java b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageBiMap.java new file mode 100644 index 00000000..68717081 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageBiMap.java @@ -0,0 +1,183 @@ +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; + +public class DirectStorageBiMap implements BiMap { + private final Function keyGetter; + private final BiConsumer keySetter; + private final Map forwardMap; + + public DirectStorageBiMap(Function keyGetter, BiConsumer 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 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 keySet() { + return this.forwardMap.keySet(); + } + + @Override + public Set values() { + return new HashSet<>(this.forwardMap.values()); + } + + @NotNull + @Override + public Set> entrySet() { + return this.forwardMap.entrySet(); + } + + @Override + public BiMap inverse() { + return new Reverse(); + } + + class Reverse implements BiMap { + @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 map) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @NotNull + @Override + public Set keySet() { + return DirectStorageBiMap.this.values(); + } + + @Override + public Set values() { + return DirectStorageBiMap.this.keySet(); + } + + @NotNull + @Override + public Set> entrySet() { + return DirectStorageBiMap.this.entrySet().stream() + .map(entry -> new AbstractMap.SimpleImmutableEntry<>(entry.getValue(), entry.getKey())) + .collect(Collectors.toSet()); + } + + @Override + public BiMap inverse() { + return DirectStorageBiMap.this; + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java new file mode 100644 index 00000000..c18ac699 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/DirectStorageRegistryObject.java @@ -0,0 +1,8 @@ +package org.embeddedt.modernfix.registry; + +import net.minecraft.resources.ResourceLocation; + +public interface DirectStorageRegistryObject { + ResourceLocation mfix$getResourceKey(); + void mfix$setResourceKey(ResourceLocation key); +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java b/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java new file mode 100644 index 00000000..76285abd --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/LifecycleMap.java @@ -0,0 +1,20 @@ +package org.embeddedt.modernfix.registry; + +import com.mojang.serialization.Lifecycle; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; + +public class LifecycleMap extends Reference2ReferenceOpenHashMap { + public LifecycleMap() { + this.defaultReturnValue(Lifecycle.stable()); + } + + @Override + public Lifecycle put(T t, Lifecycle lifecycle) { + if(lifecycle != defRetValue) + return super.put(t, lifecycle); + else { + // need the duplicate containsKey/get logic here to override the default return value + return super.containsKey(t) ? super.get(t) : null; + } + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java b/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java new file mode 100644 index 00000000..252676d2 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/RegistryStorage.java @@ -0,0 +1,34 @@ +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 createStorage() { + return new DirectStorageBiMap<>(DirectStorageRegistryObject::mfix$getResourceKey, DirectStorageRegistryObject::mfix$setResourceKey); + } + + public static BiMap, DirectStorageRegistryObject> createKeyStorage(ResourceKey> registryKey, BiMap storage) { + if(storage instanceof DirectStorageBiMap) { + DirectStorageBiMap directStorageBiMap = (DirectStorageBiMap)storage; + // silently ignore put/putAll calls on this map + return new TransformingBiMap, DirectStorageRegistryObject>(directStorageBiMap, loc -> ResourceKey.create(registryKey, loc), ResourceKey::location, Function.identity(), Function.identity()) { + @Override + public DirectStorageRegistryObject put(ResourceKey key, DirectStorageRegistryObject value) { + return null; + } + + @Override + public void putAll(Map, ? extends DirectStorageRegistryObject> map) { + + } + }; + } else + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java b/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java new file mode 100644 index 00000000..2fb1beb1 --- /dev/null +++ b/common/src/main/java/org/embeddedt/modernfix/registry/TransformingBiMap.java @@ -0,0 +1,224 @@ +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 implements BiMap { + private final BiMap delegate; + private final Function keyFwd; + private final Function keyBack; + private final Function valueFwd; + private final Function valueBack; + + public TransformingBiMap(BiMap map, Function keyFwd, Function keyBack, Function valueFwd, Function 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 map) { + map.forEach((key, value) -> { + this.delegate.put(keyBack(key), valueBack(value)); + }); + } + + @Override + public void clear() { + this.delegate.clear(); + } + + @NotNull + @Override + public Set keySet() { + return new TransformingSet<>(this.delegate.keySet(), this.keyFwd, this.keyBack); + } + + @Override + public Set values() { + return new TransformingSet<>(this.delegate.values(), this.valueFwd, this.valueBack); + } + + @NotNull + @Override + public Set> 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 inverse() { + return new TransformingBiMap<>(this.delegate.inverse(), this.valueFwd, this.valueBack, this.keyFwd, this.keyBack); + } + + static class TransformingSet implements Set { + private final Set delegate; + private final Function forward; + private final Function reverse; + + public TransformingSet(Set set, Function forward, Function 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 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[] 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 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 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(); + } + } +}