diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..a190fb110
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,19 @@
+**/.git
+**/.gitlab
+**/.cache
+
+buildAllJars
+
+**/_Misc Files
+*.bat
+*.md
+*.sh
+*.txt
+
+coreSubProjects/*.md
+coreSubProjects/*.txt
+
+**/.gitignore
+**/.gitattributes
+**/.gitlab-cy.yml
+**/.gitmodules
diff --git a/.gitignore b/.gitignore
index 547998298..0fa2bd272 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@ run/
out/
*.iml
.gradle/
+.gradle-cache/
output/
bin/
libs/
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..8df81edfb
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,12 @@
+FROM eclipse-temurin:17-jdk
+
+WORKDIR /home/build/
+COPY ./gradlew .
+RUN chmod +x ./gradlew
+CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \
+ ./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
+ echo "\r========== [BUILD: $MC_VER] ==========" && \
+ ./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
+ echo "\r========== [MERGE: $MC_VER] ==========" && \
+ ./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \
+ echo "\r========== [DONE: $MC_VER] =========="
diff --git a/Readme.md b/Readme.md
index 737724030..2566023d5 100644
--- a/Readme.md
+++ b/Readme.md
@@ -109,7 +109,8 @@ If running in an IDE, to ensure the IDE noticed the version change, run any grad
>Note: There may be a `java.nio.file.FileSystemException` thrown when running the command after switching versions. To fix it, either restart your IDE (as your IDE is probably locking a file) or use a tool like LockHunter to unlock the linked file(s). (Generally it is a lib file under `common\build\lib`, `forge\build\lib`, or `fabric\build\lib`). \
> If anyone knows how to solve this issue please let us know here: \
> https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/233
-
+
+
## Compiling
@@ -138,7 +139,16 @@ Run tests with: `./gradlew test`
> Example: `./gradlew assemble -PmcVer=1.18.2`
-
+
+
+## Compiling with Docker
+
+`./compile `
+
+You can also locally compile the DH jars without a Java environment by using Docker. Where `` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values.
+
+
+
## Other commands
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
index a4cc12d80..9a7b76b2e 100644
--- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
+++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java
@@ -19,136 +19,146 @@
package com.seibel.distanthorizons.common.wrappers.block;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.Holder;
+import net.minecraft.core.Registry;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.biome.Biome;
+import net.minecraft.world.level.biome.Biomes;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
-
-import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
#if POST_MC_1_17
import net.minecraft.core.Holder;
import net.minecraft.resources.RegistryOps;
#endif
-
#if POST_MC_1_19_2
import net.minecraft.data.worldgen.biome.EndBiomes;
import net.minecraft.data.worldgen.biome.NetherBiomes;
#endif
-
-
#if MC_1_16_5 || MC_1_17_1
import net.minecraft.core.Registry;
#elif MC_1_18_2 || MC_1_19_2
-import net.minecraft.core.Holder;
-import net.minecraft.core.Registry;
#else
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
#endif
-import net.minecraft.resources.ResourceLocation;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.biome.Biome;
-#if !PRE_MC_1_18_2
-import net.minecraft.world.level.biome.Biomes;
-#endif
+/**
+ * This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
+ */
+public class BiomeWrapper implements IBiomeWrapper {
+ private static final Logger LOGGER = LogManager.getLogger();
-
-
-/** This class wraps the minecraft BlockPos.Mutable (and BlockPos) class */
-public class BiomeWrapper implements IBiomeWrapper
-{
- #if PRE_MC_1_18_2
- public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>();
- public final Biome biome;
- #else
- public static final ConcurrentMap, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
- public final Holder biome;
+ #if PRE_MC_1_18_2
+ public static final ConcurrentMap biomeWrapperMap = new ConcurrentHashMap<>();
+ public final Biome biome;
+ #else
+ public static final ConcurrentMap, BiomeWrapper> biomeWrapperMap = new ConcurrentHashMap<>();
+ public final Holder biome;
#endif
- static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome)
- {
- return biomeWrapperMap.computeIfAbsent(biome, BiomeWrapper::new);
- }
+ /**
+ * Cached so it can be quickly used as a semi-stable hashing method.
+ * This may also fix the issue where we can serialize and save after a level has been shut down.
+ */
+ private String serializationResult = null;
- private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome)
- {
- this.biome = biome;
- }
+ //==============//
+ // constructors //
+ //==============//
- @Override
- public String getName()
- {
+ static public IBiomeWrapper getBiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome) {
+ return biomeWrapperMap.computeIfAbsent(biome, BiomeWrapper::new);
+ }
+
+ private BiomeWrapper(#if PRE_MC_1_18_2 Biome #else Holder #endif biome) {
+ this.biome = biome;
+ }
+
+ //=========//
+ // methods //
+ //=========//
+
+ @Override
+ public String getName() {
#if PRE_MC_1_18_2
return biome.toString();
#else
- return biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
+ return this.biome.unwrapKey().orElse(Biomes.THE_VOID).registry().toString();
#endif
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- BiomeWrapper that = (BiomeWrapper) o;
- return Objects.equals(biome, that.biome);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(biome);
- }
-
- @Override
- public String serialize(ILevelWrapper levelWrapper)
- {
-
- net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
-
- ResourceLocation resourceLocation;
- #if MC_1_16_5 || MC_1_17_1
- resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
- #elif MC_1_18_2 || MC_1_19_2
- resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
- #else
- resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
- #endif
-
- if (resourceLocation == null)
- {
- // shouldn't normally happen, but just in case
- return "";
- }
- else
- {
- String resourceLocationString = resourceLocation.getNamespace()+":"+resourceLocation.getPath();
- return resourceLocationString;
- }
}
-
- public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException
- {
- if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
- {
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ } else if (obj == null || this.getClass() != obj.getClass()) {
+ return false;
+ }
+
+ BiomeWrapper that = (BiomeWrapper) obj;
+ // the serialized value is used so we can test the contents instead of the references
+ return Objects.equals(this.serialize(), that.serialize(Minecraft.getInstance().level));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.serialize());
+ }
+
+ @Override
+ public String serialize(ILevelWrapper levelWrapper) {
+ if (this.serializationResult == null) {
+ net.minecraft.core.RegistryAccess registryAccess = ((Level) levelWrapper.getWrappedMcObject()).registryAccess();
+
+ ResourceLocation resourceLocation;
+ #if MC_1_16_5 || MC_1_17_1
+ resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
+ #elif MC_1_18_2 || MC_1_19_2
+ resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
+ #else
+ resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
+ #endif
+
+ if (resourceLocation == null) {
+ LOGGER.warn("unable to serialize: " + this.biome.value());
+ // shouldn't normally happen, but just in case
+ this.serializationResult = "";
+ } else {
+ this.serializationResult = resourceLocation.getNamespace() + ":" + resourceLocation.getPath();
+ }
+ }
+
+ return this.serializationResult;
+ }
+
+ public static IBiomeWrapper deserialize(String resourceLocationString, ILevelWrapper levelWrapper) throws IOException {
+ if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals("")) {
+ LOGGER.warn("null biome string deserialized");
+
// shouldn't normally happen, but just in case
new ResourceLocation("minecraft", "the_void"); // just "void" in MC 1.12 through 1.9 (inclusive)
}
-
-
+
// parse the resource location
int separatorIndex = resourceLocationString.indexOf(":");
- if (separatorIndex == -1)
- {
- throw new IOException("Unable to parse resource location string: ["+resourceLocationString+"].");
+ if (separatorIndex == -1) {
+ throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
}
- ResourceLocation resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex+1));
-
-
- try
- {
- net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
+ ResourceLocation resourceLocation = new ResourceLocation(
+ resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
+
+ try {
+ net.minecraft.core.RegistryAccess registryAccess = ((Level) levelWrapper.getWrappedMcObject()).registryAccess();
#if MC_1_16_5 || MC_1_17_1
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
@@ -157,19 +167,24 @@ public class BiomeWrapper implements IBiomeWrapper
Holder biome = new Holder.Direct<>(unwrappedBiome);
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
+ if (unwrappedBiome == null)
+ {
+ LOGGER.warn("null biome string deserialized from string: "+resourceLocationString);
+ }
Holder biome = new Holder.Direct<>(unwrappedBiome);
#endif
-
+
return getBiomeWrapper(biome);
- }
- catch (Exception e)
- {
- throw new IOException("Failed to deserialize the string ["+resourceLocationString+"] into a BiomeWrapper: "+e.getMessage(), e);
+ } catch (Exception e) {
+ throw new IOException(
+ "Failed to deserialize the string [" + resourceLocationString + "] into a BiomeWrapper: " + e.getMessage(), e);
}
}
-
-
- @Override
- public Object getWrappedMcObject() { return this.biome; }
-
+
+ @Override
+ public Object getWrappedMcObject() {return this.biome;}
+
+ @Override
+ public String toString() {return this.serialize();}
+
}
diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
index dd3d66462..673d572fa 100644
--- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
+++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java
@@ -8,6 +8,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
+import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
@@ -19,8 +20,11 @@ import java.util.concurrent.ConcurrentHashMap;
#if MC_1_16_5 || MC_1_17_1
import net.minecraft.core.Registry;
#elif MC_1_18_2 || MC_1_19_2
+import net.minecraft.client.Minecraft;
+import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
#else
+import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.EmptyBlockGetter;
@@ -38,10 +42,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public static final BlockStateWrapper AIR = new BlockStateWrapper(null);
+ public static final ConcurrentHashMap cache = new ConcurrentHashMap<>();
-
- public static ConcurrentHashMap cache = new ConcurrentHashMap<>();
-
+ /**
+ * Cached so it can be quickly used as a semi-stable hashing method.
+ * This may also fix the issue where we can serialize and save after a level has been shut down.
+ */
+ private String serializationResult = null;
//==============//
@@ -94,25 +101,36 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public String serialize(ILevelWrapper levelWrapper)
{
- if (this.blockState == null)
+ // cache the serialization result so it can be quickly used as a semi-stable hashing method
+ if (this.serializationResult == null)
{
- return "AIR";
+ if (this.blockState == null)
+ {
+ return "AIR";
+ }
+
+ ResourceLocation resourceLocation;
+ #if MC_1_16_5 || MC_1_17_1
+ resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
+ #elif MC_1_18_2 || MC_1_19_2
+ net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
+ resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
+ #else
+ net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
+ resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
+ #endif
+
+ if (resourceLocation == null)
+ {
+ LOGGER.warn("unable to serialize: "+this.blockState);
+ }
+
+ this.serializationResult = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
+ + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
}
-
- ResourceLocation resourceLocation;
- #if MC_1_16_5 || MC_1_17_1
- resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
- #elif MC_1_18_2 || MC_1_19_2
- net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
- resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
- #else
- net.minecraft.core.RegistryAccess registryAccess = ((Level)levelWrapper.getWrappedMcObject()).registryAccess();
- resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
- #endif
-
- String resourceStateString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath()
- + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState);
- return resourceStateString;
+
+
+ return this.serializationResult;
}
public static BlockStateWrapper deserialize(String resourceStateString, ILevelWrapper levelWrapper) throws IOException
@@ -174,7 +192,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// use the default if no state was found
if (foundState == null)
{
- LOGGER.debug("Unable to find BlockState for Block ["+resourceLocation+"] with properties: ["+blockStatePropertiesString+"].");
+ LOGGER.warn("Unable to find BlockState for Block ["+resourceLocation+"] with properties: ["+blockStatePropertiesString+"].");
foundState = block.defaultBlockState();
}
return new BlockStateWrapper(foundState);
@@ -230,11 +248,12 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
BlockStateWrapper that = (BlockStateWrapper) obj;
- return Objects.equals(this.blockState, that.blockState);
+ // the serialized value is used so we can test the contents instead of the references
+ return Objects.equals(this.serialize(), that.serialize());
}
@Override
- public int hashCode() { return Objects.hash(this.blockState); }
+ public int hashCode() { return Objects.hash(this.serialize()); }
@Override
@@ -269,4 +288,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
#endif
}
+ @Override
+ public String toString() { return this.serialize(); }
+
}
diff --git a/compile.sh b/compile.sh
new file mode 100644
index 000000000..9cc9a628b
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+publish_version()
+{
+ if [[ "$2" == "all" || "$1" == "$2" ]]
+ then
+ docker run --name=dh-build-$1 --rm -v /${PWD}:/home/build -e MC_VER=$1 dh-eclipse-temurin
+ cp ./fabric/build/libs/*$1.jar ./buildAllJars/fabric/
+ cp ./forge/build/libs/*$1.jar ./buildAllJars/forge/
+ cp ./Merged/*.jar ./buildAllJars/merged/
+ fi
+}
+
+
+if [ -z "$1" ]
+then
+ echo "Build target is undefined! [all] [1.20.1] [1.19.4] [1.19.2] [1.18.2] [1.17.1] [1.16.5]"
+ exit 1
+fi
+
+docker build --tag=dh-eclipse-temurin -q .
+
+mkdir -p buildAllJars/fabric
+mkdir -p buildAllJars/forge
+mkdir -p buildAllJars/merged
+publish_version 1.20.1 $1
+publish_version 1.19.4 $1
+publish_version 1.19.2 $1
+publish_version 1.18.2 $1
+publish_version 1.17.1 $1
+publish_version 1.16.5 $1