浏览代码

Add specialized object<->index converters for "well-known" properties

malte0811 4 年之前
父节点
当前提交
0174f5721c

+ 7 - 20
common/src/main/java/malte0811/ferritecore/fastmap/FastMapKey.java

@@ -8,30 +8,22 @@ import java.util.List;
 import java.util.Map;
 
 class FastMapKey<T extends Comparable<T>> {
-    private final Property<T> property;
-    private final List<T> values;
     private final int mapFactor;
-    private final Map<Comparable<?>, Integer> toValueIndex;
+    private final PropertyIndexer<T> indexer;
 
     FastMapKey(Property<T> property, int mapFactor) {
-        this.property = property;
-        this.values = ImmutableList.copyOf(property.getAllowedValues());
+        this.indexer = PropertyIndexer.makeIndexer(property);
         this.mapFactor = mapFactor;
-        ImmutableMap.Builder<Comparable<?>, Integer> toValueIndex = ImmutableMap.builder();
-        for (int i = 0; i < this.values.size(); i++) {
-            toValueIndex.put(this.values.get(i), i);
-        }
-        this.toValueIndex = toValueIndex.build();
     }
 
     T getValue(int mapIndex) {
-        int index = (mapIndex / mapFactor) % values.size();
-        return values.get(index);
+        int index = (mapIndex / mapFactor) % indexer.size();
+        return indexer.byIndex(index);
     }
 
     int replaceIn(int mapIndex, T newValue) {
         final int lowerData = mapIndex % mapFactor;
-        final int upperFactor = mapFactor * values.size();
+        final int upperFactor = mapFactor * indexer.size();
         final int upperData = mapIndex - mapIndex % upperFactor;
         int internalIndex = getInternalIndex(newValue);
         if (internalIndex < 0) {
@@ -42,7 +34,7 @@ class FastMapKey<T extends Comparable<T>> {
     }
 
     Property<T> getProperty() {
-        return property;
+        return indexer.getProperty();
     }
 
     int toPartialMapIndex(Comparable<?> value) {
@@ -50,11 +42,6 @@ class FastMapKey<T extends Comparable<T>> {
     }
 
     private int getInternalIndex(Comparable<?> value) {
-        Integer result = toValueIndex.get(value);
-        if (result != null) {
-            return result;
-        } else {
-            throw new IllegalStateException("Unknown value: " + value + " in " + property);
-        }
+        return indexer.toIndex((T) value);
     }
 }

+ 200 - 0
common/src/main/java/malte0811/ferritecore/fastmap/PropertyIndexer.java

@@ -0,0 +1,200 @@
+package malte0811.ferritecore.fastmap;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import net.minecraft.state.BooleanProperty;
+import net.minecraft.state.EnumProperty;
+import net.minecraft.state.IntegerProperty;
+import net.minecraft.state.Property;
+import net.minecraft.state.properties.BlockStateProperties;
+import net.minecraft.util.Direction;
+import net.minecraft.util.IStringSerializable;
+import org.apache.logging.log4j.LogManager;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+
+public abstract class PropertyIndexer<T extends Comparable<T>> {
+    private final Property<T> property;
+
+    public static <T extends Comparable<T>> PropertyIndexer<T> makeIndexer(Property<T> prop) {
+        PropertyIndexer<?> result = null;
+        if (prop instanceof BooleanProperty) {
+            result = new BoolIndexer((BooleanProperty) prop);
+        } else if (prop instanceof IntegerProperty) {
+            result = new IntIndexer((IntegerProperty) prop);
+        } else if (prop == BlockStateProperties.FACING) {
+            result = new WeirdVanillaDirectionIndexer();
+        } else if (prop instanceof EnumProperty<?>) {
+            result = new EnumIndexer<>((EnumProperty<?>) prop);
+        }
+        if (result == null || !result.isValid()) {
+            return new GenericIndexer<>(prop);
+        } else {
+            return (PropertyIndexer<T>) result;
+        }
+    }
+
+    protected PropertyIndexer(Property<T> property) {
+        this.property = property;
+    }
+
+    public Property<T> getProperty() {
+        return property;
+    }
+
+    public int size() {
+        return property.getAllowedValues().size();
+    }
+
+    public abstract T byIndex(int index);
+
+    public abstract int toIndex(T value);
+
+    protected boolean isValid() {
+        Collection<T> allowed = getProperty().getAllowedValues();
+        int index = 0;
+        for (T val : allowed) {
+            if (toIndex(val) != index || !val.equals(byIndex(index))) {
+                //TODO remove
+                LogManager.getLogger().info(
+                        "Property {} will use generic indexer, specialized is inconsistent at {}, {}",
+                        getProperty(), val, index
+                );
+                return false;
+            }
+            ++index;
+        }
+        return true;
+    }
+
+    private static class BoolIndexer extends PropertyIndexer<Boolean> {
+
+        protected BoolIndexer(BooleanProperty property) {
+            super(property);
+        }
+
+        @Override
+        public Boolean byIndex(int index) {
+            return index == 1 ? Boolean.FALSE : Boolean.TRUE;
+        }
+
+        @Override
+        public int toIndex(Boolean value) {
+            return value ? 0 : 1;
+        }
+    }
+
+    private static class IntIndexer extends PropertyIndexer<Integer> {
+        private final int min;
+
+        protected IntIndexer(IntegerProperty property) {
+            super(property);
+            this.min = property.getAllowedValues().stream().min(Comparator.naturalOrder()).orElse(0);
+        }
+
+        @Override
+        public Integer byIndex(int index) {
+            return index + min;
+        }
+
+        @Override
+        public int toIndex(Integer value) {
+            return value - min;
+        }
+    }
+
+    private static class EnumIndexer<E extends Enum<E> & IStringSerializable>
+            extends PropertyIndexer<E> {
+        private final int ordinalOffset;
+        private final E[] enumValues;
+
+        protected EnumIndexer(EnumProperty<E> property) {
+            super(property);
+            this.ordinalOffset = property.getAllowedValues()
+                    .stream()
+                    .mapToInt(Enum::ordinal)
+                    .min()
+                    .orElse(0);
+            this.enumValues = getProperty().getValueClass().getEnumConstants();
+        }
+
+        @Override
+        public E byIndex(int index) {
+            return enumValues[index + ordinalOffset];
+        }
+
+        @Override
+        public int toIndex(E value) {
+            return value.ordinal() - ordinalOffset;
+        }
+    }
+
+    /**
+     * This is a kind of hack for a vanilla quirk: BlockStateProperties.FACING (which is used everywhere) has the order
+     * NORTH, EAST, SOUTH, WEST, UP, DOWN
+     * instead of the "canonical" order given by the enum
+     */
+    private static class WeirdVanillaDirectionIndexer extends PropertyIndexer<Direction> {
+        private static final Direction[] ORDER = {
+                Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN
+        };
+
+        public WeirdVanillaDirectionIndexer() {
+            super(BlockStateProperties.FACING);
+            Preconditions.checkState(isValid());
+        }
+
+        @Override
+        public Direction byIndex(int index) {
+            return ORDER[index];
+        }
+
+        @Override
+        public int toIndex(Direction value) {
+            switch (value) {
+                case NORTH:
+                    return 0;
+                case EAST:
+                    return 1;
+                case SOUTH:
+                    return 2;
+                case WEST:
+                    return 3;
+                case UP:
+                    return 4;
+                case DOWN:
+                    return 5;
+            }
+            throw new IllegalArgumentException("Invalid direction: "+value);
+        }
+    }
+
+    private static class GenericIndexer<T extends Comparable<T>> extends PropertyIndexer<T> {
+        private final Map<Comparable<?>, Integer> toValueIndex;
+        private final List<T> values;
+
+        protected GenericIndexer(Property<T> property) {
+            super(property);
+            this.values = ImmutableList.copyOf(property.getAllowedValues());
+            ImmutableMap.Builder<Comparable<?>, Integer> toValueIndex = ImmutableMap.builder();
+            for (int i = 0; i < this.values.size(); i++) {
+                toValueIndex.put(this.values.get(i), i);
+            }
+            this.toValueIndex = toValueIndex.build();
+        }
+
+        @Override
+        public T byIndex(int index) {
+            return values.get(index);
+        }
+
+        @Override
+        public int toIndex(T value) {
+            return toValueIndex.get(value);
+        }
+    }
+}