Make state_definition_construct degrade gracefully if map is used like a hashmap

Related: #452
This commit is contained in:
embeddedt 2024-08-09 18:02:39 -04:00
parent c0eaf29cb5
commit 49464451dd
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
2 changed files with 29 additions and 15 deletions

View File

@ -1,5 +1,6 @@
package org.embeddedt.modernfix.blockstate;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.world.level.block.state.properties.Property;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -14,6 +15,7 @@ import java.util.*;
*/
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
private final Map<Property<?>, Comparable<?>>[] keys;
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
private final Object[] values;
private int usedSlots;
public FakeStateMap(int numStates) {
@ -34,22 +36,39 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
@Override
public boolean containsKey(Object o) {
throw new UnsupportedOperationException();
return getFastLookup().containsKey(o);
}
@Override
public boolean containsValue(Object o) {
throw new UnsupportedOperationException();
return getFastLookup().containsValue(o);
}
@SuppressWarnings("unchecked")
private Map<Map<Property<?>, Comparable<?>>, S> getFastLookup() {
if(fastLookup == null) {
var map = new Object2ObjectOpenHashMap<Map<Property<?>, Comparable<?>>, S>(usedSlots);
Map<Property<?>, Comparable<?>>[] keys = this.keys;
Object[] values = this.values;
for(int i = 0; i < usedSlots; i++) {
map.put(keys[i], (S)values[i]);
}
fastLookup = map;
}
return fastLookup;
}
@Override
public S get(Object o) {
throw new UnsupportedOperationException();
return getFastLookup().get(o);
}
@Nullable
@Override
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
if(fastLookup != null) {
throw new IllegalStateException("Cannot populate map after fast lookup is built");
}
keys[usedSlots] = propertyComparableMap;
values[usedSlots] = s;
usedSlots++;
@ -70,7 +89,7 @@ public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S>
@Override
public void clear() {
for(int i = 0; i < this.keys.length; i++) {
for(int i = 0; i < usedSlots; i++) {
this.keys[i] = null;
this.values[i] = null;
}

View File

@ -6,23 +6,25 @@ import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.block.state.properties.Property;
import org.embeddedt.modernfix.annotation.RequiresMod;
import org.embeddedt.modernfix.blockstate.FakeStateMap;
import org.embeddedt.modernfix.blockstate.FerriteCorePostProcess;
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
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.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Map;
// This optimization requires FerriteCore to be worthwhile, otherwise the FakeStateMap degrades to hash internally
@Mixin(StateDefinition.class)
@RequiresMod("ferritecore")
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
@Shadow @Final private ImmutableSortedMap<String, Property<?>> propertiesByName;
/**
* @author embeddedt
* @reason write states into a custom array map for fast iteration by FerriteCore, no need to waste time hashing
* and growing
*/
@ModifyVariable(method = "<init>", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8)
private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) {
int numStates = 1;
@ -31,11 +33,4 @@ public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
}
return new FakeStateMap<>(numStates);
}
@Inject(method = "<init>", at = @At("TAIL"))
private void postProcess(CallbackInfo ci) {
// keep in dev only until upstream FC releases
if(ModernFixPlatformHooks.INSTANCE.isDevEnv())
FerriteCorePostProcess.postProcess((StateDefinition<O, S>)(Object)this);
}
}