Skip to content

Commit 4946445

Browse files
committed
Make state_definition_construct degrade gracefully if map is used like a hashmap
Related: #452
1 parent c0eaf29 commit 4946445

File tree

2 files changed

+29
-15
lines changed

2 files changed

+29
-15
lines changed

common/src/main/java/org/embeddedt/modernfix/blockstate/FakeStateMap.java

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.embeddedt.modernfix.blockstate;
22

3+
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
34
import net.minecraft.world.level.block.state.properties.Property;
45
import org.jetbrains.annotations.NotNull;
56
import org.jetbrains.annotations.Nullable;
@@ -14,6 +15,7 @@
1415
*/
1516
public class FakeStateMap<S> implements Map<Map<Property<?>, Comparable<?>>, S> {
1617
private final Map<Property<?>, Comparable<?>>[] keys;
18+
private Map<Map<Property<?>, Comparable<?>>, S> fastLookup;
1719
private final Object[] values;
1820
private int usedSlots;
1921
public FakeStateMap(int numStates) {
@@ -34,22 +36,39 @@ public boolean isEmpty() {
3436

3537
@Override
3638
public boolean containsKey(Object o) {
37-
throw new UnsupportedOperationException();
39+
return getFastLookup().containsKey(o);
3840
}
3941

4042
@Override
4143
public boolean containsValue(Object o) {
42-
throw new UnsupportedOperationException();
44+
return getFastLookup().containsValue(o);
45+
}
46+
47+
@SuppressWarnings("unchecked")
48+
private Map<Map<Property<?>, Comparable<?>>, S> getFastLookup() {
49+
if(fastLookup == null) {
50+
var map = new Object2ObjectOpenHashMap<Map<Property<?>, Comparable<?>>, S>(usedSlots);
51+
Map<Property<?>, Comparable<?>>[] keys = this.keys;
52+
Object[] values = this.values;
53+
for(int i = 0; i < usedSlots; i++) {
54+
map.put(keys[i], (S)values[i]);
55+
}
56+
fastLookup = map;
57+
}
58+
return fastLookup;
4359
}
4460

4561
@Override
4662
public S get(Object o) {
47-
throw new UnsupportedOperationException();
63+
return getFastLookup().get(o);
4864
}
4965

5066
@Nullable
5167
@Override
5268
public S put(Map<Property<?>, Comparable<?>> propertyComparableMap, S s) {
69+
if(fastLookup != null) {
70+
throw new IllegalStateException("Cannot populate map after fast lookup is built");
71+
}
5372
keys[usedSlots] = propertyComparableMap;
5473
values[usedSlots] = s;
5574
usedSlots++;
@@ -70,7 +89,7 @@ public void putAll(@NotNull Map<? extends Map<Property<?>, Comparable<?>>, ? ext
7089

7190
@Override
7291
public void clear() {
73-
for(int i = 0; i < this.keys.length; i++) {
92+
for(int i = 0; i < usedSlots; i++) {
7493
this.keys[i] = null;
7594
this.values[i] = null;
7695
}

common/src/main/java/org/embeddedt/modernfix/common/mixin/perf/state_definition_construct/StateDefinitionMixin.java

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,25 @@
66
import net.minecraft.world.level.block.state.properties.Property;
77
import org.embeddedt.modernfix.annotation.RequiresMod;
88
import org.embeddedt.modernfix.blockstate.FakeStateMap;
9-
import org.embeddedt.modernfix.blockstate.FerriteCorePostProcess;
10-
import org.embeddedt.modernfix.platform.ModernFixPlatformHooks;
119
import org.spongepowered.asm.mixin.Final;
1210
import org.spongepowered.asm.mixin.Mixin;
1311
import org.spongepowered.asm.mixin.Shadow;
1412
import org.spongepowered.asm.mixin.injection.At;
15-
import org.spongepowered.asm.mixin.injection.Inject;
1613
import org.spongepowered.asm.mixin.injection.ModifyVariable;
17-
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
1814

1915
import java.util.Map;
2016

17+
// This optimization requires FerriteCore to be worthwhile, otherwise the FakeStateMap degrades to hash internally
2118
@Mixin(StateDefinition.class)
2219
@RequiresMod("ferritecore")
2320
public class StateDefinitionMixin<O, S extends StateHolder<O, S>> {
2421
@Shadow @Final private ImmutableSortedMap<String, Property<?>> propertiesByName;
2522

23+
/**
24+
* @author embeddedt
25+
* @reason write states into a custom array map for fast iteration by FerriteCore, no need to waste time hashing
26+
* and growing
27+
*/
2628
@ModifyVariable(method = "<init>", at = @At(value = "STORE", ordinal = 0), ordinal = 1, index = 8)
2729
private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>, Comparable<?>>, S> in) {
2830
int numStates = 1;
@@ -31,11 +33,4 @@ private Map<Map<Property<?>, Comparable<?>>, S> useArrayMap(Map<Map<Property<?>,
3133
}
3234
return new FakeStateMap<>(numStates);
3335
}
34-
35-
@Inject(method = "<init>", at = @At("TAIL"))
36-
private void postProcess(CallbackInfo ci) {
37-
// keep in dev only until upstream FC releases
38-
if(ModernFixPlatformHooks.INSTANCE.isDevEnv())
39-
FerriteCorePostProcess.postProcess((StateDefinition<O, S>)(Object)this);
40-
}
4136
}

0 commit comments

Comments
 (0)