浏览代码

Merge branch 'main' into 1.17

malte0811 4 年之前
父节点
当前提交
5bcd474c6d

+ 2 - 2
.github/workflows/build.yaml

@@ -7,10 +7,10 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
-      - name: Set up JDK 8
+      - name: Set up JDK 11
         uses: actions/setup-java@v1
         with:
-          java-version: 8
+          java-version: 11
       - uses: actions/cache@v2
         with:
           path: |

+ 1 - 16
.github/workflows/test.yaml

@@ -1,27 +1,12 @@
-name: Run tests on Java 8 and Java 16-ea
+name: Run tests on Java 16
 on:
   push:
 
 jobs:
   run:
     runs-on: ubuntu-latest
-    # TODO this should probably be a matrix build, but the J16 test needs to use a different gradle version
     steps:
       - uses: actions/checkout@v2
-      - name: Set up JDK 8
-        uses: actions/setup-java@v1
-        with:
-          java-version: 8
-      - uses: actions/cache@v2
-        with:
-          path: |
-            ~/.gradle/caches
-            ~/.gradle/wrapper
-          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
-          restore-keys: |
-            ${{ runner.os }}-gradle-
-      - name: Test on Java 8
-        run: ./gradlew common:test
       - name: Set up JDK 16
         uses: actions/setup-java@v1
         with:

+ 2 - 2
build.gradle

@@ -2,7 +2,7 @@ import java.util.stream.Collectors
 
 plugins {
     id "architectury-plugin" version "3.1-SNAPSHOT"
-    id "forgified-fabric-loom" version "0.6.78" apply false
+    id "dev.architectury.loom" version "0.7.2-SNAPSHOT" apply false
 }
 
 architectury {
@@ -10,7 +10,7 @@ architectury {
 }
 
 subprojects {
-    apply plugin: "forgified-fabric-loom"
+    apply plugin: "dev.architectury.loom"
 
     def fcMixinConfigs = [
             "predicates",

+ 5 - 0
common/src/main/java/malte0811/ferritecore/ducks/FastMapStateHolder.java

@@ -1,6 +1,7 @@
 package malte0811.ferritecore.ducks;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Table;
 import malte0811.ferritecore.fastmap.FastMap;
 import net.minecraft.world.level.block.state.properties.Property;
 
@@ -16,4 +17,8 @@ public interface FastMapStateHolder<S> {
     ImmutableMap<Property<?>, Comparable<?>> getVanillaPropertyMap();
 
     void replacePropertyMap(ImmutableMap<Property<?>, Comparable<?>> newMap);
+
+    void setNeighborTable(Table<Property<?>, Comparable<?>, S> table);
+
+    Table<Property<?>, Comparable<?>, S> getNeighborTable();
 }

+ 5 - 1
common/src/main/java/malte0811/ferritecore/fastmap/FastMap.java

@@ -131,7 +131,7 @@ public class FastMap<Value> {
         return keys.size();
     }
 
-    FastMapKey<?> getKey(int keyIndex) {
+    public FastMapKey<?> getKey(int keyIndex) {
         return keys.get(keyIndex);
     }
 
@@ -145,4 +145,8 @@ public class FastMap<Value> {
             return (FastMapKey<T>) getKey(index);
         }
     }
+
+    public boolean isSingleState() {
+        return valueMatrix.size() == 1;
+    }
 }

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

@@ -41,11 +41,11 @@ public abstract class FastMapKey<T extends Comparable<T>> {
      */
     abstract int getFactorToNext();
 
-    protected final int numValues() {
+    public final int numValues() {
         return indexer.numValues();
     }
 
-    protected final Property<T> getProperty() {
+    public final Property<T> getProperty() {
         return indexer.getProperty();
     }
 

+ 106 - 0
common/src/main/java/malte0811/ferritecore/fastmap/table/CrashNeighborTable.java

@@ -0,0 +1,106 @@
+package malte0811.ferritecore.fastmap.table;
+
+import net.minecraft.state.Property;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Singleton, which is assigned as the neighbor table for all blockstates by default. This makes it clear who is to be
+ * blamed for any crashes, and also how to work around them.
+ */
+public class CrashNeighborTable<S> extends NeighborTableBase<S> {
+    private static final CrashNeighborTable<?> INSTANCE = new CrashNeighborTable<>();
+
+    @SuppressWarnings("unchecked")
+    public static <S> CrashNeighborTable<S> getInstance() {
+        return (CrashNeighborTable<S>) INSTANCE;
+    }
+
+    private CrashNeighborTable() {}
+
+    @Override
+    public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public boolean containsRow(@Nullable Object rowKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public boolean containsColumn(@Nullable Object columnKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public boolean containsValue(@Nullable Object value) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public S get(@Nullable Object rowKey, @Nullable Object columnKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public int size() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Map<Comparable<?>, S> row(@NotNull Property<?> rowKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Map<Property<?>, S> column(@NotNull Comparable<?> columnKey) {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Set<Cell<Property<?>, Comparable<?>, S>> cellSet() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Set<Property<?>> rowKeySet() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Set<Comparable<?>> columnKeySet() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Collection<S> values() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Map<Property<?>, Map<Comparable<?>, S>> rowMap() {
+        return crashOnAccess();
+    }
+
+    @Override
+    public Map<Comparable<?>, Map<Property<?>, S>> columnMap() {
+        return crashOnAccess();
+    }
+
+    private static <T> T crashOnAccess() {
+        throw new UnsupportedOperationException(
+                "A mod tried to access the state neighbor table directly. Please report this at " + ISSUES_URL +
+                        ". As a temporary workaround you can enable \"populateNeighborTable\" in the FerriteCore config"
+        );
+    }
+}

+ 220 - 0
common/src/main/java/malte0811/ferritecore/fastmap/table/FastmapNeighborTable.java

@@ -0,0 +1,220 @@
+package malte0811.ferritecore.fastmap.table;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Tables;
+import malte0811.ferritecore.ducks.FastMapStateHolder;
+import malte0811.ferritecore.fastmap.FastMap;
+import net.minecraft.state.Property;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.*;
+
+/**
+ * This is mostly untested, and is only used when mods are present that are known to access the neighbor table directly.
+ */
+public class FastmapNeighborTable<S> extends NeighborTableBase<S> {
+    private final FastMapStateHolder<S> owner;
+
+    public FastmapNeighborTable(FastMapStateHolder<S> owner) {
+        this.owner = owner;
+    }
+
+    @Override
+    public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
+        if (!(columnKey instanceof Comparable<?>) || !(rowKey instanceof Property<?>)) {
+            return false;
+        }
+        Comparable<?> valueInState = owner.getStateMap().getValue(owner.getStateIndex(), (Property<?>) rowKey);
+        if (valueInState == null || valueInState.equals(columnKey)) {
+            // Not contained in state, or the current value (which isn't added to the table)
+            return false;
+        } else {
+            // We contain the row, and we only ever contain non-null row keys
+            Preconditions.checkNotNull(rowKey);
+            // Is value allowed for property?
+            return ((Property<?>) rowKey).getAllowedValues().contains(columnKey);
+        }
+    }
+
+    @Override
+    public boolean containsRow(@Nullable Object rowKey) {
+        if (!(rowKey instanceof Property<?>)) {
+            return false;
+        } else {
+            // Property is not in state
+            return owner.getStateMap().getValue(owner.getStateIndex(), (Property<?>) rowKey) != null;
+        }
+    }
+
+    @Override
+    public boolean containsColumn(@Nullable Object columnKey) {
+        FastMap<S> map = owner.getStateMap();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            Map.Entry<Property<?>, Comparable<?>> entry = map.getEntry(i, owner.getStateIndex());
+            if (!entry.getValue().equals(columnKey) && entry.getKey().getAllowedValues().contains(columnKey)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean containsValue(@Nullable Object value) {
+        if (value == null) {
+            return false;
+        }
+        final FastMap<S> map = owner.getStateMap();
+        for (int propIndex = 0; propIndex < map.numProperties(); ++propIndex) {
+            if (isNeighbor(map.getKey(propIndex).getProperty(), value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private <T extends Comparable<T>> boolean isNeighbor(Property<T> prop, Object potentialNeighbor) {
+        final FastMap<S> map = owner.getStateMap();
+        final T valueInState = map.getValue(owner.getStateIndex(), prop);
+        for (final T neighborValue : prop.getAllowedValues()) {
+            if (neighborValue.equals(valueInState)) {
+                continue;
+            }
+            final S neighbor = map.with(owner.getStateIndex(), prop, valueInState);
+            if (potentialNeighbor.equals(neighbor)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public S get(@Nullable Object rowKey, @Nullable Object columnKey) {
+        if (!(rowKey instanceof Property)) {
+            return null;
+        }
+        return owner.getStateMap().withUnsafe(owner.getStateIndex(), (Property<?>) rowKey, columnKey);
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return owner.getStateMap().isSingleState();
+    }
+
+    @Override
+    public int size() {
+        int numNeighbors = 0;
+        for (int i = 0; i < owner.getStateMap().numProperties(); ++i) {
+            numNeighbors += owner.getStateMap().getKey(i).numValues();
+        }
+        return numNeighbors;
+    }
+
+    @Override
+    public Map<Comparable<?>, S> row(@NotNull Property<?> rowKey) {
+        final Map<Comparable<?>, S> rowMap = new HashMap<>();
+        final Comparable<?> contained = owner.getStateMap().getValue(owner.getStateIndex(), rowKey);
+        for (Comparable<?> val : rowKey.getAllowedValues()) {
+            if (!val.equals(contained)) {
+                rowMap.put(val, owner.getStateMap().withUnsafe(owner.getStateIndex(), rowKey, val));
+            }
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Map<Property<?>, S> column(@NotNull Comparable<?> columnKey) {
+        final FastMap<S> map = owner.getStateMap();
+        final int index = owner.getStateIndex();
+        final Map<Property<?>, S> rowMap = new HashMap<>();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            final Property<?> rowKey = map.getKey(i).getProperty();
+            final Comparable<?> contained = map.getValue(index, rowKey);
+            for (Comparable<?> val : rowKey.getAllowedValues()) {
+                if (!val.equals(contained) && val.equals(columnKey)) {
+                    rowMap.put(rowKey, map.withUnsafe(index, rowKey, val));
+                }
+            }
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Set<Cell<Property<?>, Comparable<?>, S>> cellSet() {
+        final FastMap<S> map = owner.getStateMap();
+        final int index = owner.getStateIndex();
+        final Set<Cell<Property<?>, Comparable<?>, S>> rowMap = new HashSet<>();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            final Property<?> rowKey = map.getKey(i).getProperty();
+            final Comparable<?> contained = map.getValue(index, rowKey);
+            for (Comparable<?> val : rowKey.getAllowedValues()) {
+                if (!val.equals(contained)) {
+                    rowMap.add(Tables.immutableCell(rowKey, val, map.withUnsafe(index, rowKey, val)));
+                }
+            }
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Set<Property<?>> rowKeySet() {
+        return owner.getVanillaPropertyMap().keySet();
+    }
+
+    @Override
+    public Set<Comparable<?>> columnKeySet() {
+        final FastMap<S> map = owner.getStateMap();
+        final Set<Comparable<?>> rowMap = new HashSet<>();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            final Property<?> rowKey = map.getKey(i).getProperty();
+            final Comparable<?> contained = map.getValue(owner.getStateIndex(), rowKey);
+            for (Comparable<?> val : rowKey.getAllowedValues()) {
+                if (!val.equals(contained)) {
+                    rowMap.add(val);
+                }
+            }
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Collection<S> values() {
+        final FastMap<S> map = owner.getStateMap();
+        final int index = owner.getStateIndex();
+        final Set<S> rowMap = new HashSet<>();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            final Property<?> rowKey = map.getKey(i).getProperty();
+            final Comparable<?> contained = map.getValue(index, rowKey);
+            for (Comparable<?> val : rowKey.getAllowedValues()) {
+                if (!val.equals(contained)) {
+                    rowMap.add(map.withUnsafe(index, rowKey, val));
+                }
+            }
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Map<Property<?>, Map<Comparable<?>, S>> rowMap() {
+        final FastMap<S> map = owner.getStateMap();
+        final Map<Property<?>, Map<Comparable<?>, S>> rowMap = new HashMap<>();
+        for (int i = 0; i < map.numProperties(); ++i) {
+            final Property<?> rowKey = map.getKey(i).getProperty();
+            rowMap.put(rowKey, row(rowKey));
+        }
+        return rowMap;
+    }
+
+    @Override
+    public Map<Comparable<?>, Map<Property<?>, S>> columnMap() {
+        final Map<Property<?>, Map<Comparable<?>, S>> rowMap = rowMap();
+        Map<Comparable<?>, Map<Property<?>, S>> colMap = new HashMap<>();
+        for (Map.Entry<Property<?>, Map<Comparable<?>, S>> entry : rowMap.entrySet()) {
+            for (Map.Entry<Comparable<?>, S> innerEntry : entry.getValue().entrySet()) {
+                colMap.computeIfAbsent(innerEntry.getKey(), $ -> new HashMap<>())
+                        .put(entry.getKey(), innerEntry.getValue());
+            }
+        }
+        return colMap;
+    }
+}

+ 36 - 0
common/src/main/java/malte0811/ferritecore/fastmap/table/NeighborTableBase.java

@@ -0,0 +1,36 @@
+package malte0811.ferritecore.fastmap.table;
+
+import com.google.common.collect.Table;
+import net.minecraft.state.Property;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class NeighborTableBase<S> implements Table<Property<?>, Comparable<?>, S> {
+    protected static final String ISSUES_URL = "https://github.com/malte0811/FerriteCore/issues";
+
+    @Override
+    public void clear() {
+        crashOnModify();
+    }
+
+    @Override
+    public final S put(@NotNull Property<?> rowKey, @NotNull Comparable<?> columnKey, @NotNull S value) {
+        return crashOnModify();
+    }
+
+    @Override
+    public final void putAll(@NotNull Table<? extends Property<?>, ? extends Comparable<?>, ? extends S> table) {
+        crashOnModify();
+    }
+
+    @Override
+    public final S remove(@Nullable Object rowKey, @Nullable Object columnKey) {
+        return crashOnModify();
+    }
+
+    private static <T> T crashOnModify() {
+        throw new UnsupportedOperationException(
+                "A mod tried to modify the state neighbor table directly. Please report this at " + ISSUES_URL
+        );
+    }
+}

+ 10 - 1
common/src/main/java/malte0811/ferritecore/impl/StateHolderImpl.java

@@ -3,6 +3,8 @@ package malte0811.ferritecore.impl;
 import malte0811.ferritecore.classloading.FastImmutableMapDefiner;
 import malte0811.ferritecore.ducks.FastMapStateHolder;
 import malte0811.ferritecore.fastmap.FastMap;
+import malte0811.ferritecore.fastmap.table.CrashNeighborTable;
+import malte0811.ferritecore.fastmap.table.FastmapNeighborTable;
 import malte0811.ferritecore.mixin.config.FerriteConfig;
 import net.minecraft.world.level.block.state.properties.Property;
 import java.util.Map;
@@ -18,7 +20,9 @@ public class StateHolderImpl {
      */
     public static <S>
     void populateNeighbors(Map<Map<Property<?>, Comparable<?>>, S> states, FastMapStateHolder<S> holder) {
-        if (states == LAST_STATE_MAP.get()) {
+        if (holder.getNeighborTable() != null) {
+            throw new IllegalStateException();
+        } else if (states == LAST_STATE_MAP.get()) {
             // Use threadlocal state to use the same fast map for all states of one block
             holder.setStateMap((FastMap<S>) LAST_FAST_STATE_MAP.get());
         } else {
@@ -34,5 +38,10 @@ public class StateHolderImpl {
         if (FerriteConfig.PROPERTY_MAP.isEnabled()) {
             holder.replacePropertyMap(FastImmutableMapDefiner.makeMap(holder));
         }
+        if (FerriteConfig.POPULATE_NEIGHBOR_TABLE.isEnabled()) {
+            holder.setNeighborTable(new FastmapNeighborTable<>(holder));
+        } else {
+            holder.setNeighborTable(CrashNeighborTable.getInstance());
+        }
     }
 }

+ 6 - 0
common/src/main/java/malte0811/ferritecore/mixin/config/FerriteConfig.java

@@ -17,6 +17,7 @@ public class FerriteConfig {
     public static final Option DEDUP_BLOCKSTATE_CACHE;
     public static final Option DEDUP_QUADS;
     public static final Option COMPACT_FAST_MAP;
+    public static final Option POPULATE_NEIGHBOR_TABLE;
 
     static {
         ConfigBuilder builder = new ConfigBuilder();
@@ -53,6 +54,11 @@ public class FerriteConfig {
                 "compactFastMap",
                 "Use a slightly more compact, but also slightly slower representation for block states"
         );
+        POPULATE_NEIGHBOR_TABLE = builder.createOptInOption(
+                "populateNeighborTable",
+                "Populate the neighbor table used by vanilla. Enabling this slightly increases memory usage, but" +
+                        " can help with issues in the rare case where mods access it directly."
+        );
         builder.finish();
     }
 

+ 12 - 0
common/src/main/java/malte0811/ferritecore/mixin/fastmap/FastMapStateHolderMixin.java

@@ -19,6 +19,8 @@ public abstract class FastMapStateHolderMixin<O, S> implements FastMapStateHolde
     @Shadow
     @Final
     private ImmutableMap<Property<?>, Comparable<?>> values;
+    @Shadow
+    private Table<Property<?>, Comparable<?>, S> propertyToStateTable;
 
     private int globalTableIndex;
     private FastMap<S> globalTable;
@@ -74,4 +76,14 @@ public abstract class FastMapStateHolderMixin<O, S> implements FastMapStateHolde
     public void setStateIndex(int newValue) {
         globalTableIndex = newValue;
     }
+
+    @Override
+    public void setNeighborTable(Table<Property<?>, Comparable<?>, S> table) {
+        propertyToStateTable = table;
+    }
+
+    @Override
+    public Table<Property<?>, Comparable<?>, S> getNeighborTable() {
+        return propertyToStateTable;
+    }
 }

+ 11 - 0
common/src/test/java/malte0811/ferritecore/fastmap/MockFMStateHolder.java

@@ -1,6 +1,7 @@
 package malte0811.ferritecore.fastmap;
 
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Table;
 import malte0811.ferritecore.ducks.FastMapStateHolder;
 import net.minecraft.world.level.block.state.properties.Property;
 
@@ -42,4 +43,14 @@ public class MockFMStateHolder<T> implements FastMapStateHolder<T> {
     public void replacePropertyMap(ImmutableMap<Property<?>, Comparable<?>> newMap) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public void setNeighborTable(Table<Property<?>, Comparable<?>, T> table) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Table<Property<?>, Comparable<?>, T> getNeighborTable() {
+        return null;
+    }
 }

+ 10 - 6
fabric/build.gradle

@@ -3,7 +3,7 @@ plugins {
 }
 
 configurations {
-    shadow
+    shadowCommon
 }
 
 architectury {
@@ -21,7 +21,7 @@ dependencies {
     developmentFabric(project(path: ":common")) {
         transitive = false
     }
-    shadow(project(path: ":common", configuration: "transformProductionFabric")) {
+    shadowCommon(project(path: ":common", configuration: "transformProductionFabric")) {
         transitive = false
     }
 }
@@ -35,16 +35,20 @@ processResources {
 }
 
 shadowJar {
-    configurations = [project.configurations.shadow]
-    classifier "shadow"
+    configurations = [project.configurations.shadowCommon]
+    classifier "dev-shadow"
 }
 
 remapJar {
-    dependsOn(shadowJar)
-    input.set(shadowJar.archivePath)
+    input.set shadowJar.archiveFile
+    dependsOn shadowJar
     classifier "fabric"
 }
 
+jar {
+    classifier "dev"
+}
+
 loom {
     mixinConfigs += "ferritecore.fabric.mixin.json"
 }

+ 1 - 1
forge/gradle.properties

@@ -1 +1 @@
-loom.forge=true
+loom.platform=forge

+ 1 - 1
gradle.properties

@@ -5,7 +5,7 @@ minecraft_version=1.16.5
 mcp_version=20210309-1.16.5
 
 archives_base_name=ferritecore
-mod_version=2.0.4
+mod_version=2.0.5
 maven_group=malte0811.ferritecore
 
 fabric_loader_version=0.11.1