浏览代码

Initial commit
(100% equivalent to the code used in the screenshots in FC)

malte0811 4 年之前
当前提交
5d05ee81d3

+ 28 - 0
.gitignore

@@ -0,0 +1,28 @@
+# eclipse
+bin
+*.launch
+.settings
+.metadata
+.classpath
+.project
+
+# idea
+out
+*.ipr
+*.iws
+*.iml
+.idea
+
+# gradle
+build
+.gradle
+
+# other
+eclipse
+run
+classes
+
+# Files from Forge MDK
+forge*changelog.txt
+
+custom.gradle

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 malte0811
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 121 - 0
build.gradle

@@ -0,0 +1,121 @@
+buildscript {
+    repositories {
+        maven { url = 'https://files.minecraftforge.net/maven' }
+        jcenter()
+        mavenCentral()
+        maven {url='https://dist.creeper.host/Sponge/maven'}
+
+    }
+    dependencies {
+        classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
+        classpath 'org.spongepowered:mixingradle:0.7-SNAPSHOT'
+    }
+}
+apply plugin: 'net.minecraftforge.gradle'
+// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
+apply plugin: 'eclipse'
+apply plugin: 'maven-publish'
+
+version = "${mod_version}"
+group = "malte0811.${modid}" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
+archivesBaseName = "${modid}"
+
+sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
+
+minecraft {
+    // The mappings can be changed at any time, and must be in the following format.
+    // snapshot_YYYYMMDD   Snapshot are built nightly.
+    // stable_#            Stables are built at the discretion of the MCP team.
+    // Use non-default mappings at your own risk. they may not always work.
+    // Simply re-run your setup task after changing the mappings to update your workspace.
+    mappings channel: "snapshot", version: "${mapping}-${mcp_version}"
+    // makeObfSourceJar = false // an Srg named sources jar is made by default. uncomment this to disable.
+
+    //accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
+
+    // Default run configurations.
+    // These can be tweaked, removed, or duplicated as needed.
+    runs {
+        client {
+            workingDirectory project.file('run')
+            arg "-mixin.config="+archivesBaseName+".mixins.json"
+
+
+            // Recommended logging level for the console
+            property 'forge.logging.console.level', 'debug'
+
+            mods {
+                examplemod {
+                    source sourceSets.main
+                }
+            }
+        }
+
+        server {
+            workingDirectory project.file('run')
+            arg "-mixin.config="+archivesBaseName+".mixins.json"
+            arg "-nogui"
+
+
+            // Recommended logging level for the console
+            property 'forge.logging.console.level', 'debug'
+
+            mods {
+                examplemod {
+                    source sourceSets.main
+                }
+            }
+        }
+    }
+}
+
+def customGradle = rootProject.file('custom.gradle');
+if (customGradle.exists()) {
+    apply from: customGradle;
+}
+
+repositories{
+    maven {
+        // location of the maven that hosts JEI files
+        name = "Progwml6 maven"
+        url = "https://dvs1.progwml6.com/files/maven/"
+    }
+    maven {
+        // location of a maven mirror for JEI files, as a fallback
+        name = "ModMaven"
+        url = "https://modmaven.k-4u.nl"
+    }
+    maven {
+        name = "BlameJared"
+        url = "https://maven.blamejared.com/"
+    }
+    jcenter()
+    mavenCentral()
+
+}
+
+dependencies {
+    minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
+    compile fg.deobf("blusunrize.immersiveengineering:ImmersiveEngineering:1.16.3-+")
+    runtimeOnly fg.deobf("mezz.jei:jei-1.16.3:7.6.0.49")
+}
+
+// Example for how to get properties into the manifest for reading by the runtime..
+jar {
+    manifest {
+        attributes([
+                "Specification-Title": "${modid}",
+                "Specification-Version": "1", // We are version 1 of ourselves
+                "Implementation-Title": project.name,
+                "Implementation-Version": "${version}",
+                "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
+                "MixinConfigs": "${modid}.mixins.json"
+        ])
+    }
+}
+
+apply plugin: 'org.spongepowered.mixin'
+
+mixin {
+    add sourceSets.main, "${modid}.refmap.json"
+}

+ 10 - 0
gradle.properties

@@ -0,0 +1,10 @@
+# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
+# This is required to provide enough memory for the Minecraft decompilation process.
+org.gradle.jvmargs=-Xmx3G
+org.gradle.daemon=false
+mod_version=1.0
+modid=ferritecore
+mc_version=1.16.3
+forge_version=34.1.25
+mapping=20200916
+mcp_version=1.16.2

二进制
gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.9-bin.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 3 - 0
src/generated/resources/.cache/cache

@@ -0,0 +1,3 @@
+872b1aa7aa2acb6db0869633fba963e9ae3e1e18 assets/mixinplayground/blockstates/demo.json
+80b00714d08c01c04891057e12f7746d4da696b1 assets/mixinplayground/models/block/demo.json
+7f40dd86d5b21331e8eb875e0d6271f9d487f47c assets/mixinplayground/models/item/demo.json

+ 7 - 0
src/generated/resources/assets/mixinplayground/blockstates/demo.json

@@ -0,0 +1,7 @@
+{
+  "variants": {
+    "": {
+      "model": "mixinplayground:block/demo"
+    }
+  }
+}

+ 6 - 0
src/generated/resources/assets/mixinplayground/models/block/demo.json

@@ -0,0 +1,6 @@
+{
+  "parent": "minecraft:block/cube_all",
+  "textures": {
+    "all": "mixinplayground:block/demo"
+  }
+}

+ 3 - 0
src/generated/resources/assets/mixinplayground/models/item/demo.json

@@ -0,0 +1,3 @@
+{
+  "parent": "mixinplayground:block/demo"
+}

+ 127 - 0
src/main/java/malte0811/ferritecore/FastMap.java

@@ -0,0 +1,127 @@
+package malte0811.ferritecore;
+
+import net.minecraft.state.Property;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+public class FastMap<Value> {
+    private final List<Key<?>> keys;
+    private final List<Property<?>> rawKeys;
+    private final List<Value> values;
+
+    public FastMap(Collection<Property<?>> properties, Map<Map<Property<?>, Comparable<?>>, Value> valuesMap) {
+        List<Key<?>> keys = new ArrayList<>(properties.size());
+        int factorUpTo = 1;
+        for (Property<?> prop : properties) {
+            keys.add(new Key<>(prop, factorUpTo));
+            factorUpTo *= prop.getAllowedValues().size();
+        }
+        this.keys = keys;
+        List<Value> valuesList = new ArrayList<>(factorUpTo);
+        for (int i = 0; i < factorUpTo; ++i) {
+            valuesList.add(null);
+        }
+        for (Map.Entry<Map<Property<?>, Comparable<?>>, Value> state : valuesMap.entrySet()) {
+            valuesList.set(indexOf(state.getKey()), state.getValue());
+        }
+        this.values = valuesList;
+        this.rawKeys = new ArrayList<>(properties);
+    }
+
+    @Nullable
+    public <T extends Comparable<T>>
+    Value with(int last, Property<T> prop, T value) {
+        final Key<T> keyToChange = indexOf(prop);
+        if (keyToChange == null) {
+            return null;
+        }
+        int newIndex = keyToChange.replaceIn(last, value);
+        if (newIndex < 0) {
+            return null;
+        }
+        return values.get(newIndex);
+    }
+
+    @Nullable
+    private <T extends Comparable<T>>
+    Key<T> indexOf(Property<T> prop) {
+        for (Key<?> key : keys) {
+            if (key.getProperty() == prop) {
+                return (Key<T>) key;
+            }
+        }
+        return null;
+    }
+
+    public int indexOf(Map<Property<?>, Comparable<?>> state) {
+        int id = 0;
+        for (Key<?> k : keys) {
+            id += k.toPartialMapIndex(state.get(k.getProperty()));
+        }
+        return id;
+    }
+
+    @Nullable
+    public <T extends Comparable<T>>
+    T getValue(int stateIndex, Property<T> property) {
+        final Key<T> propId = indexOf(property);
+        if (propId == null) {
+            return null;
+        }
+        return propId.getValue(stateIndex);
+    }
+
+    public Collection<Property<?>> getProperties() {
+        return rawKeys;
+    }
+
+    private static class Key<T extends Comparable<T>> {
+        private final Property<T> property;
+        private final List<T> values;
+        private final int mapFactor;
+
+        private Key(Property<T> property, int mapFactor) {
+            this.property = property;
+            this.values = new ArrayList<>(property.getAllowedValues());
+            this.mapFactor = mapFactor;
+        }
+
+        public int toPartialMapIndex(Comparable<?> value) {
+            return mapFactor * getInternalIndex(value);
+        }
+
+        private int getInternalIndex(Comparable<?> value) {
+            int result = values.indexOf(value);
+            if (result >= 0) {
+                return result;
+            } else {
+                throw new IllegalStateException("Unknown value: "+value+" in "+property);
+            }
+        }
+
+        public T getValue(int mapIndex) {
+            int index = (mapIndex / mapFactor) % values.size();
+            return values.get(index);
+        }
+
+        public int replaceIn(int mapIndex, T newValue) {
+            final int lowerData = mapIndex % mapFactor;
+            final int upperFactor = mapFactor * values.size();
+            final int upperData = mapIndex - mapIndex % upperFactor;
+            int internalIndex = getInternalIndex(newValue);
+            if (internalIndex < 0) {
+                return -1;
+            } else {
+                return lowerData + mapFactor * internalIndex + upperData;
+            }
+        }
+
+        public Property<T> getProperty() {
+            return property;
+        }
+    }
+}

+ 11 - 0
src/main/java/malte0811/ferritecore/HackyGlobalState.java

@@ -0,0 +1,11 @@
+package malte0811.ferritecore;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.state.Property;
+
+import java.util.Map;
+
+public class HackyGlobalState {
+    public static Map<Map<Property<?>, Comparable<?>>, ?> lastStateMap;
+    public static FastMap<?> lastFastStateMap;
+}

+ 9 - 0
src/main/java/malte0811/ferritecore/ModMain.java

@@ -0,0 +1,9 @@
+package malte0811.ferritecore;
+
+import net.minecraftforge.fml.common.Mod;
+
+@Mod(ModMain.MODID)
+@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD)
+public class ModMain {
+    public static final String MODID = "ferritecore";
+}

+ 129 - 0
src/main/java/malte0811/ferritecore/mixin/StateholderMixin.java

@@ -0,0 +1,129 @@
+package malte0811.ferritecore.mixin;
+
+import com.google.common.collect.ImmutableMap;
+import malte0811.ferritecore.FastMap;
+import malte0811.ferritecore.HackyGlobalState;
+import net.minecraft.state.Property;
+import net.minecraft.state.StateHolder;
+import org.spongepowered.asm.mixin.*;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+
+@Mixin(StateHolder.class)
+public abstract class StateholderMixin<O, S> {
+    @Mutable
+    @Shadow
+    @Final
+    private ImmutableMap<Property<?>, Comparable<?>> properties;
+    @Shadow
+    @Final
+    protected O instance;
+    @Shadow public abstract Collection<Property<?>> getProperties();
+    @Shadow public abstract <T extends Comparable<T>> T get(Property<T> property);
+    private int globalTableIndex;
+    private FastMap<S> globalTable;
+
+    /**
+     * @reason This Mixin completely replaces the data structures initialized by this method, as the original ones waste
+     * a lot of memory
+     * @author malte0811
+     */
+    @Overwrite
+    public void func_235899_a_(Map<Map<Property<?>, Comparable<?>>, S> states) {
+        if (globalTable != null) {
+            throw new IllegalStateException();
+        } else if (states == HackyGlobalState.lastStateMap) {
+            // Use "hacky global state" to use the same fast map for all states of one block
+            this.globalTable = (FastMap<S>) HackyGlobalState.lastFastStateMap;
+        } else {
+            HackyGlobalState.lastStateMap = states;
+            this.globalTable = new FastMap<>(getProperties(), states);
+            HackyGlobalState.lastFastStateMap = this.globalTable;
+        }
+        this.globalTableIndex = this.globalTable.indexOf(properties);
+        properties = null;
+    }
+
+    /**
+     * @reason The original implementation relies on the vanilla neighbor data, which is never present with these Mixins
+     * @author malte0811
+     */
+    @Overwrite
+    public <T extends Comparable<T>, V extends T> S with(Property<T> property, V value) {
+        Comparable<?> comparable = get(property);
+        if (comparable == value) {
+            return (S) this;
+        } else {
+            S s = this.globalTable.with(this.globalTableIndex, property, value);
+            if (s == null) {
+                throw new IllegalArgumentException("Cannot set property " + property + " to " + value + " on " + this.instance + ", it is not an allowed value");
+            } else {
+                return s;
+            }
+        }
+    }
+
+    // All other Mixins: If the new data structures are initialized, use those. Otherwise (if populateNeighbors wasn't
+    // run yet) use the vanilla code using `properties`
+    @Inject(method = "get", at = @At("HEAD"), cancellable = true)
+    public <T extends Comparable<T>>
+    void get(Property<T> property, CallbackInfoReturnable<T> cir) {
+        if (globalTable != null) {
+            T result = globalTable.getValue(globalTableIndex, property);
+            if (result == null) {
+                throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.instance);
+            }
+            cir.setReturnValue(result);
+            cir.cancel();
+        }
+    }
+
+    @Inject(method = "func_235903_d_", at = @At("HEAD"), cancellable = true)
+    public <T extends Comparable<T>>
+    void getOptionalHead(Property<T> property, CallbackInfoReturnable<Optional<T>> cir) {
+        if (globalTable != null) {
+            T result = globalTable.getValue(globalTableIndex, property);
+            if (result == null) {
+                cir.setReturnValue(Optional.empty());
+            } else {
+                cir.setReturnValue(Optional.of(result));
+            }
+            cir.cancel();
+        }
+    }
+
+    //TODO speed up in some way?
+    @Inject(method = "getValues", at = @At("HEAD"), cancellable = true)
+    public void getValuesHead(CallbackInfoReturnable<ImmutableMap<Property<?>, Comparable<?>>> cir) {
+        if (globalTable != null) {
+            ImmutableMap.Builder<Property<?>, Comparable<?>> result = ImmutableMap.builder();
+            for (Property<?> p : globalTable.getProperties()) {
+                result.put(p, get(p));
+            }
+            cir.setReturnValue(result.build());
+            cir.cancel();
+        }
+    }
+
+    @Inject(method = "hasProperty", at = @At("HEAD"), cancellable = true)
+    public <T extends Comparable<T>>
+    void hasPropertyHead(Property<T> property, CallbackInfoReturnable<Boolean> cir) {
+        if (globalTable != null) {
+            cir.setReturnValue(globalTable.getProperties().contains(property));
+            cir.cancel();
+        }
+    }
+
+    @Inject(method = "getProperties", at = @At("HEAD"), cancellable = true)
+    public void getPropertiesHead(CallbackInfoReturnable<Collection<Property<?>>> cir) {
+        if (globalTable != null) {
+            cir.setReturnValue(globalTable.getProperties());
+            cir.cancel();
+        }
+    }
+}

+ 24 - 0
src/main/resources/META-INF/mods.toml

@@ -0,0 +1,24 @@
+modLoader="javafml"
+loaderVersion="[28,)"
+
+license="MIT"
+[[mods]]
+modId="ferritecore"
+version="${file.jarVersion}"
+displayName="Recipe Buffers"
+authors="malte0811"
+description='''
+Reduces memory usage. These changes will probably be PRd to Forge soon.
+'''
+[[dependencies.ferritecore]]
+    modId="forge"
+    mandatory=true
+    versionRange="[35.1.0,)"
+    ordering="NONE"
+    side="BOTH"
+[[dependencies.ferritecore]]
+    modId="minecraft"
+    mandatory=true
+    versionRange="[1.16.4,)"
+    ordering="NONE"
+    side="BOTH"

+ 13 - 0
src/main/resources/ferritecore.mixins.json

@@ -0,0 +1,13 @@
+{
+  "required": true,
+  "package": "malte0811.ferritecore.mixin",
+  "compatibilityLevel": "JAVA_8",
+  "refmap": "ferritecore.refmap.json",
+  "mixins": [
+    "StateholderMixin"
+  ],
+  "injectors": {
+    "defaultRequire": 1
+  },
+  "minVersion": "0.8"
+}

+ 6 - 0
src/main/resources/pack.mcmeta

@@ -0,0 +1,6 @@
+{
+    "pack": {
+        "description": "ferritecore resources",
+        "pack_format": 4
+    }
+}

+ 8 - 0
stats.md

@@ -0,0 +1,8 @@
+| State                  | Bytes |
+|:-----------------------|------:|
+|Base                    |209521 |
+|Batch same serializers  |168782 |
+|Recurring RL namespaces |141893 |
+|ItemStack optimizations |124605 |
+|Recurring ingredients   |102810 |
+|Global ingredients      | 96890 |