Compare commits

..

8 Commits

Author SHA1 Message Date
Ran 0c8b3f3cda Merge remote-tracking branch 'origin/java_omega' into java_omega
# Conflicts:
#	build.gradle
#	coreSubProjects
#	fabric/build.gradle
#	forge/build.gradle
2024-07-26 00:21:34 +10:00
Ran a9a6a19307 Attempt to migrate most of the codebase to newer versions of Java 2024-07-26 00:17:15 +10:00
Cutiepie 208fc69eb7 Rebase 2024-07-25 23:00:43 +10:00
Cutiepie eb1a062934 Rebase 2024-07-25 22:59:16 +10:00
Cutiepie 55d5bca76d Downgrade to the minimum Java version required by the selected Minecraft version 2024-05-22 22:17:59 +10:00
Cutiepie f81026c707 Bump JVM Downgrader 2024-05-22 21:51:46 +10:00
Cutiepie e86af817e0 Java Ω & cleanup core's code 2024-05-22 21:06:36 +10:00
Cutiepie e0fd2b2f7a Java Ω & cleanup core's code 2024-05-22 21:03:38 +10:00
73 changed files with 1177 additions and 1300 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ build:
stage: build stage: build
parallel: parallel:
matrix: matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1"] - MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21"]
script: script:
# this both runs the unit tests and assembles the code # this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
+27 -54
View File
@@ -12,6 +12,8 @@ plugins {
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
id 'xyz.wagyourtail.jvmdowngrader' version '0.9.1' apply true
} }
@@ -102,16 +104,13 @@ subprojects { p ->
// Apply plugins // Apply plugins
apply plugin: "java" apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow" apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "xyz.wagyourtail.jvmdowngrader"
if (isMinecraftSubProject) if (isMinecraftSubProject)
apply plugin: "systems.manifold.manifold-gradle-plugin" apply plugin: "systems.manifold.manifold-gradle-plugin"
// Apply forge's loom // Apply forge's loom
if ((findProject(":forge") && p == project(":forge")) || if ( (findProject(":forge") && p == project(":forge")) || (findProject(":neoforge") && p == project(":neoforge")) )
(findProject(":neoforge") && p == project(":neoforge"))
)
{
apply plugin: "dev.architectury.loom" apply plugin: "dev.architectury.loom"
}
// Set the manifold version (may not be required tough) // Set the manifold version (may not be required tough)
@@ -311,8 +310,21 @@ subprojects { p ->
mergeServiceFiles() mergeServiceFiles()
} }
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
jar.dependsOn(shadowJar) // For downgrading the Java version of our project to match the minimum Java version required by the selected Minecraft version.
task jarDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar) {
inputFile = tasks.shadowJar.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}"
}
task apiDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.ShadeJar) {
inputFile = jarDowngrade.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}-shaded-java-api"
}
// We're using a custom downgrade task so we disable the original downgrade tasks
downgradeJar.enabled = false
shadeDowngradedApi.enabled = false
// Put stuff from gradle.properties into the mod info // Put stuff from gradle.properties into the mod info
@@ -431,11 +443,9 @@ subprojects { p ->
jar { jar {
from "LICENSE.txt" from "LICENSE.txt"
manifest { manifest {
attributes( attributes 'Implementation-Title': rootProject.mod_name,
'Implementation-Title': rootProject.mod_name, 'Implementation-Version': rootProject.mod_version,
'Implementation-Version': rootProject.mod_version, 'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
)
} }
} }
@@ -557,44 +567,6 @@ allprojects { p ->
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod? // TODO: If neoforged is ever needed, should we use that, or call it a forge mod?
} }
// Adds some dependencies that are in vanilla but not in core
if (p == project(":core")) {
OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem;
// Set the OS lwjgl is using to the current os
project.ext.lwjglNatives = "natives-" + os.toFamilyName()
dependencies { // All of these dependencies are in Vanilla Minecraft, but we need to depend on it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
implementation "org.lwjgl:lwjgl-openal"
implementation "org.lwjgl:lwjgl-opengl"
implementation "org.lwjgl:lwjgl-stb"
implementation "org.lwjgl:lwjgl-tinyfd"
runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives"
runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives"
implementation "org.joml:joml:${rootProject.joml_version}"
// Some other dependencies
implementation("org.jetbrains:annotations:16.0.2")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("com.google.common:google-collect:0.5")
implementation("com.google.guava:guava:31.1-jre")
}
}
task copyCommonLoaderResources(type: Copy) { task copyCommonLoaderResources(type: Copy) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main"))) into(file(p.file("build/resources/main")))
@@ -616,16 +588,17 @@ allprojects { p ->
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
if (isMinecraftSubProject) { if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer options.release = rootProject.java_version as Integer // Neoforge complains without this
options.compilerArgs += ["-Xplugin:Manifold"] options.compilerArgs += ["-Xplugin:Manifold"]
} else {
options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
} }
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
// Sets the project's actual Java version (it's recommended to use this over the `options.release` method above)
java { java {
sourceCompatibility = rootProject.java_version
targetCompatibility = rootProject.java_version
withSourcesJar() withSourcesJar()
} }
} }
@@ -103,7 +103,7 @@ public abstract class AbstractModInitializer
LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandDispatcher = dispatcher; }); this.subscribeRegisterCommandsEvent(dispatcher -> this.commandDispatcher = dispatcher);
this.subscribeServerStartingEvent(server -> this.subscribeServerStartingEvent(server ->
{ {
@@ -1,85 +0,0 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class MixinChunkMapCommon
{
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{
// is this position already being updated?
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
{
return;
}
// is this chunk being saved to disk?
boolean savingChunkToDisk = ci.getReturnValue();
// true means a chunk was saved to disk
if (!savingChunkToDisk)
{
return;
}
// TODO are the following validations necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
// corrupt/incomplete chunk validation //
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
// biome validation //
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
// submit the update event
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, level, ServerLevelWrapper.getWrapper(level)),
ServerLevelWrapper.getWrapper(level)
);
}
}
@@ -26,6 +26,6 @@ public class DependencySetupDoneCheck
// TODO move to DependencySetup // TODO move to DependencySetup
public static boolean isDone = false; public static boolean isDone = false;
// TODO why is this here and what is its purpose? // TODO why is this here and what is its purpose?
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; }); public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> false);
} }
@@ -102,33 +102,17 @@ public class McObjectConverter
lodDirections = new EDhDirection[lodDirs.length]; lodDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs) for (EDhDirection lodDir : lodDirs)
{ {
Direction dir; Direction dir = switch (lodDir.name().toUpperCase()) {
switch (lodDir.name().toUpperCase()) case "DOWN" -> Direction.DOWN;
{ case "UP" -> Direction.UP;
case "DOWN": case "NORTH" -> Direction.NORTH;
dir = Direction.DOWN; case "SOUTH" -> Direction.SOUTH;
break; case "WEST" -> Direction.WEST;
case "UP": case "EAST" -> Direction.EAST;
dir = Direction.UP; default -> null;
break; };
case "NORTH":
dir = Direction.NORTH;
break;
case "SOUTH":
dir = Direction.SOUTH;
break;
case "WEST":
dir = Direction.WEST;
break;
case "EAST":
dir = Direction.EAST;
break;
default:
dir = null;
break;
}
if (dir == null) if (dir == null)
{ {
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir); throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
} }
@@ -155,25 +155,23 @@ public class WrapperFactory implements IWrapperFactory
} }
} }
//#if MC_VER <= MC_1_XX_X #if MC_VER <= MC_1_21
else if (objectArray.length == 2) else if (objectArray.length == 2)
{ {
// correct number of parameters from the API // correct number of parameters from the API
// chunk // chunk
if (!(objectArray[0] instanceof ChunkAccess)) if (!(objectArray[0] instanceof ChunkAccess chunk))
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// level / light source // level / light source
if (!(objectArray[1] instanceof Level)) if (!(objectArray[1] instanceof Level level))
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
// the level is needed for the DH level wrapper... // the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting // ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level; LevelReader lightSource = level;
@@ -201,7 +199,16 @@ public class WrapperFactory implements IWrapperFactory
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
//#endif #else
// Intentional compiler error to bring attention to the missing wrapper function.
// If you need to work on an unimplemented version but don't have the ability to implement this yet
// you can comment it out, but please don't commit it. Someone will have to implement it.
// After implementing the new version please read this method's javadocs for instructions
// on what other locations also need to be updated, the DhAPI specifically needs to
// be updated to state which objects this method accepts.
not implemented for this version of Minecraft!
#endif
} }
/** /**
* Note: when this is updated for different MC versions, * Note: when this is updated for different MC versions,
@@ -211,13 +218,16 @@ public class WrapperFactory implements IWrapperFactory
{ {
String[] expectedClassNames; String[] expectedClassNames;
//#if MC_VER <= MC_1_XX_X #if MC_VER <= MC_1_21
expectedClassNames = new String[] expectedClassNames = new String[]
{ {
ChunkAccess.class.getName(), ChunkAccess.class.getName(),
ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName() ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName()
}; };
//#endif #else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
} }
@@ -233,11 +243,10 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{ {
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{ {
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"]."); throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
@@ -256,7 +265,7 @@ public class WrapperFactory implements IWrapperFactory
Biome biome = (Biome) objectArray[0]; Biome biome = (Biome) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper); return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
#else #elif MC_VER <= MC_1_21
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome)) if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
{ {
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray)); throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
@@ -264,6 +273,9 @@ public class WrapperFactory implements IWrapperFactory
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0]; Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper); return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif #endif
} }
/** /**
@@ -276,8 +288,11 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
expectedClassNames = new String[] { Biome.class.getName() }; expectedClassNames = new String[] { Biome.class.getName() };
#else #elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" }; expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif #endif
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
@@ -286,27 +301,28 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{ {
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{ {
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"]."); throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
//#if MC_VER <= MC_1_XX_X #if MC_VER <= MC_1_21
if (objectArray.length != 1) if (objectArray.length != 1)
{ {
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray)); throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
} }
if (!(objectArray[0] instanceof BlockState)) if (!(objectArray[0] instanceof BlockState blockState))
{ {
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray)); throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
} }
BlockState blockState = (BlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper); return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
//#endif #else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
} }
/** /**
* Note: when this is updated for different MC versions, * Note: when this is updated for different MC versions,
@@ -318,8 +334,11 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() }; expectedClassNames = new String[] { Biome.class.getName() };
#else #elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" }; expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif #endif
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray); return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
@@ -266,7 +266,7 @@ public class BiomeWrapper implements IBiomeWrapper
} }
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals("")) else if (resourceLocationString.trim().isEmpty() || resourceLocationString.isEmpty())
{ {
LOGGER.warn("Null biome string deserialized."); LOGGER.warn("Null biome string deserialized.");
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
@@ -293,7 +293,7 @@ public class BiomeWrapper implements IBiomeWrapper
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
try try
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else #else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
@@ -34,6 +34,7 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.awt.*; import java.awt.*;
@@ -233,7 +234,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
String ignoreBlockCsv = config.get(); String ignoreBlockCsv = config.get();
if (ignoreBlockCsv != null) if (ignoreBlockCsv != null)
{ {
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(","))); blockStringList.addAll(List.of(ignoreBlockCsv.split(",")));
} }
return getBlockWrappers(blockStringList, levelWrapper); return getBlockWrappers(blockStringList, levelWrapper);
@@ -461,7 +462,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// we need the final string for the concurrent hash map later // we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString; final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.isEmpty()) // the empty string shouldn't normally happen, but just in case
{ {
return AIR; return AIR;
} }
@@ -498,7 +499,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
ResourceLocation resourceLocation; ResourceLocation resourceLocation;
try try
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else #else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1)); resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
@@ -602,7 +603,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// alphabetically sort the list so they are always in the same order // alphabetically sort the list so they are always in the same order
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection); List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName())); sortedBlockPropteryList.sort(Comparator.comparing(Property::getName));
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
@@ -0,0 +1,48 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
import java.util.concurrent.ConcurrentHashMap;
public class ClientBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, pos));
}
public void clear() { blockCache.clear(); }
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos)
{
return getBlockStateData(state, pos).getAndResolveFaceColor(biome, pos);
}
}
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper; import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper;
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@@ -47,70 +48,73 @@ import org.apache.logging.log4j.Logger;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* This stores and calculates the colors * @version 2022-9-16
* the given {@link BlockState} should have based
* on the given {@link IClientLevelWrapper}.
*
* @see ColorUtil
*/ */
public class ClientBlockStateColorCache public class ClientBlockStateCache
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>(); private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>(); private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
/**
* Methods using MC's "RandomSource" object aren't thread safe <br>
* so we need to put locks around that logic. <br>
* specifically:
* <code>
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
* </code>
*/
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
/** This is the order each direction on a block is processed when attempting to get the texture/color */
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
private static final int FLOWER_COLOR_SCALE = 5;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
private static final Random RANDOM = new Random(0); public static final Random random = new Random(0);
#else #else
/** Note: this object isn't thread safe and must be put in a lock */ public static final RandomSource random = RandomSource.create();
private static final RandomSource RANDOM = RandomSource.create();
#endif #endif
private final IClientLevelWrapper levelWrapper; public final IClientLevelWrapper levelWrapper;
private final BlockState blockState; public final BlockState blockState;
private final LevelReader level; public final LevelReader level;
public final BlockPos pos;
private boolean isColorResolved = false;
private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false;
private int tintIndex = 0;
//===========// public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
// constants // {
//===========// this.blockState = blockState;
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.pos = McObjectConverter.Convert(samplingPos);
this.resolveColors();
//LOGGER.info("ClientBlocKCache created for {}", blockState);
}
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13) boolean isColorResolved = false;
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON int baseColor = 0; //TODO: Impl per-face color
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS); boolean needShade = true;
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS); boolean needPostTinting = false;
int tintIndex = 0;
private static final int[] linearToSrgbTable = new int[]
public static final int FLOWER_COLOR_SCALE = 5;
enum ColorMode
{
Default,
Flower,
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block b)
{ {
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
if (b.toString().contains("glass")) return Glass;
if (b.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
//Way to efficiently do this was suggested by IMS from sodium. This is where those numbers and support code was lifted from.
private static final int MIN_BITS = 0x39000000; // 2^(-13)
private static final int MAX_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_BOUND = Float.intBitsToFloat(MIN_BITS);
private static final float MAX_BOUND = Float.intBitsToFloat(MAX_BITS);
private static final int[] linearToSrgbTable = new int[] {
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
@@ -124,139 +128,81 @@ public class ClientBlockStateColorCache
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
}; };
private static final float[] srgbToLinearTable = new float[] private static final float[] srgbToLinearTable = new float[] {
{ 0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f, 0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f, 0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f, 0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f, 0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f, 0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f, 0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f, 0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f, 0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f, 0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f, 0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f, 0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f, 0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f, 0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f, 0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f, 0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f, 0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f, 0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f, 0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f, 0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f, 0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f, 0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f, 0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f, 0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f, 0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f, 0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f, 0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f, 0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f, 0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f, 0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f, 0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f, 0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f };
};
private static int linearToSrgb(float c) {
if (!(c > MIN_BOUND)) {
c = MIN_BOUND;
}
if (c > MAX_BOUND) {
c = MAX_BOUND;
}
int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_BITS) >> 20)];
//=============// int bias = (entry >>> 16) << 9;
// constructor // int scale = entry & 0xffff;
//=============// int t = (inputBits >>> 12) & 0xff;
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel) return (bias + (scale * t)) >>> 16;
}
//////////////
private static int getWidth(TextureAtlasSprite texture)
{ {
this.blockState = blockState; #if MC_VER < MC_1_19_4
this.levelWrapper = samplingLevel; return texture.getWidth();
this.level = (LevelReader) samplingLevel.getWrappedMcObject(); #else
this.resolveColors(); return texture.contents().width();
#endif
}
private static int getHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
} }
//===================//
// color calculation //
//===================//
private void resolveColors()
{
if (this.isColorResolved)
{
return;
}
try
{
// getQuads() isn't thread safe so we need to put this logic in a lock
RESOLVE_LOCK.lock();
if (this.blockState.getFluidState().isEmpty())
{
// look for the first non-empty direction
List<BakedQuad> quads = null;
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
if (quads != null && !quads.isEmpty()
&& !(
this.blockState.getBlock() instanceof RotatedPillarBlock
&& direction == Direction.UP
)
)
{
break;
}
}
if (quads == null || quads.isEmpty())
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
}
if (quads != null && !quads.isEmpty())
{
this.needPostTinting = quads.get(0).isTinted();
this.needShade = quads.get(0).isShade();
this.tintIndex = quads.get(0).getTintIndex();
this.baseColor = calculateColorFromTexture(
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
ColorMode.getColorMode(this.blockState.getBlock()));
}
else
{
// Backup method.
this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
}
else
{
// Liquid Block
this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
this.isColorResolved = true;
}
finally
{
RESOLVE_LOCK.unlock();
}
}
//TODO: Perhaps make this not just use the first frame? //TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{ {
@@ -266,15 +212,13 @@ public class ClientBlockStateColorCache
double green = 0; double green = 0;
double blue = 0; double blue = 0;
int tempColor; int tempColor;
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
// don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel) if (colorMode != ColorMode.Chisel)
{ {
// textures normally use u and v instead of x and y // textures normally use u and v instead of x and y
for (int v = 0; v < getTextureHeight(texture); v++) for (int v = 0; v < getHeight(texture); v++)
{ {
for (int u = 0; u < getTextureWidth(texture); u++) for (int u = 0; u < getWidth(texture); u++)
{ {
//note: Minecraft color format is: 0xAA BB GG RR //note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB //________ DH mod color format is: 0xAA RR GG BB
@@ -326,10 +270,8 @@ public class ClientBlockStateColorCache
} }
if (count == 0) if (count == 0)
{
// this block is entirely transparent // this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255); tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
else else
{ {
// determine the average color // determine the average color
@@ -339,7 +281,6 @@ public class ClientBlockStateColorCache
linearToSrgb((float) (green / (double) alpha)), linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha))); linearToSrgb((float) (blue / (double) alpha)));
} }
//check if not missing texture //check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182)) if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{ {
@@ -348,53 +289,61 @@ public class ClientBlockStateColorCache
} }
return tempColor; return tempColor;
} }
private static int getTextureWidth(TextureAtlasSprite texture) private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
private void resolveColors()
{ {
#if MC_VER < MC_1_19_4 if (isColorResolved) return;
return texture.getWidth(); if (blockState.getFluidState().isEmpty())
#else {
return texture.contents().width(); List<BakedQuad> quads = null;
#endif for (Direction direction : DIRECTION_ORDER)
} {
private static int getTextureHeight(TextureAtlasSprite texture) quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
{ getBlockModel(blockState).getQuads(blockState, direction, random); // TODO getQuads sometimes throws a "makeThreadingException", is there anything we can do about that? Note: this isn't a critical issue, it just prints an ugly error and the render data will need to be regenered.
#if MC_VER < MC_1_19_4 if (quads != null && !quads.isEmpty() &&
return texture.getHeight(); !(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
#else break;
return texture.contents().height(); } ;
#endif if (quads == null || quads.isEmpty())
} {
/** quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
* This method was suggested by IMS from the Iris/Sodium team. getBlockModel(blockState).getQuads(blockState, null, random);
* That's where the numbers and code are based. }
*/ if (quads != null && !quads.isEmpty())
private static int linearToSrgb(float c) {
{ needPostTinting = quads.getFirst().isTinted();
if (!(c > MIN_SRGB_BOUND)) { needShade = quads.getFirst().isShade();
c = MIN_SRGB_BOUND; tintIndex = quads.getFirst().getTintIndex();
baseColor = calculateColorFromTexture(
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.getFirst().getSprite(), #endif
ColorMode.getColorMode(blockState.getBlock()));
}
else
{ // Backup method.
needPostTinting = false;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
}
} }
else
if (c > MAX_SRGB_BOUND) { { // Liquid Block
c = MAX_SRGB_BOUND; needPostTinting = true;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
} }
int inputBits = Float.floatToRawIntBits(c); isColorResolved = true;
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
} }
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
//===============//
// public getter //
//===============//
public int getColor(BiomeWrapper biome, DhBlockPos pos)
{ {
// FIXME: impl per-face colors
// only get the tint if the block needs to be tinted // only get the tint if the block needs to be tinted
if (!this.needPostTinting) if (!this.needPostTinting)
{ {
@@ -460,30 +409,4 @@ public class ClientBlockStateColorCache
} }
} }
//================//
// helper classes //
//================//
enum ColorMode
{
Default,
Flower,
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower;
if (block.toString().contains("glass")) return Glass;
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
} }
@@ -0,0 +1,43 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
public class ServerBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ServerLevelWrapper level;
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0, 0, 0)));
}
public void clear() { blockCache.clear(); }
}
@@ -0,0 +1,104 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
/**
* @version 2022-9-16
*/
public class ServerBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final BlockState state;
public final LevelReader level;
public final BlockPos pos;
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos)
{
state = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject();
pos = McObjectConverter.Convert(samplingPos);
resolveShapes();
//LOGGER.info("ServerBlockState created for {}", blockState);
}
boolean noCollision = false;
boolean[] occludeFaces = null;
boolean[] fullFaces = null;
boolean isShapeResolved = false;
public void resolveShapes()
{
if (isShapeResolved) return;
if (state.getFluidState().isEmpty())
{
noCollision = state.getCollisionShape(level, pos).isEmpty();
occludeFaces = new boolean[6];
if (state.canOcclude())
{
for (Direction dir : Direction.values())
{
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
}
}
VoxelShape voxelShape = state.getShape(level, pos);
fullFaces = new boolean[6];
if (!voxelShape.isEmpty())
{
for (Direction dir : Direction.values())
{
VoxelShape faceShape = voxelShape.getFaceShape(dir);
AABB aabb = faceShape.bounds();
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
(xFull || dir.getAxis().equals(Direction.Axis.X))
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
}
}
}
else
{ // Liquid Block. Treat as full block
occludeFaces = new boolean[6];
Arrays.fill(occludeFaces, true);
fullFaces = new boolean[6];
Arrays.fill(fullFaces, true);
}
}
}
@@ -83,7 +83,7 @@ public class ChunkWrapper implements IChunkWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos()); private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(BlockPos.MutableBlockPos::new);
private final ChunkAccess chunk; private final ChunkAccess chunk;
@@ -305,7 +305,7 @@ public class ChunkWrapper implements IChunkWrapper
public ChunkStatus getStatus() { return getStatus(this.getChunk()); } public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
public static ChunkStatus getStatus(ChunkAccess chunk) public static ChunkStatus getStatus(ChunkAccess chunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
return chunk.getStatus(); return chunk.getStatus();
#else #else
return chunk.getPersistedStatus(); return chunk.getPersistedStatus();
@@ -345,9 +345,8 @@ public class ChunkWrapper implements IChunkWrapper
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17 return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
#else #else
if (this.chunk instanceof LevelChunk) if (this.chunk instanceof LevelChunk levelChunk)
{ {
LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel) if (levelChunk.getLevel() instanceof ClientLevel)
{ {
// connected to a server // connected to a server
@@ -390,8 +389,6 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightStorage; return this.blockLightStorage;
} }
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; } public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
@Override
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
@Override @Override
@@ -406,8 +403,6 @@ public class ChunkWrapper implements IChunkWrapper
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ); this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
this.getSkyLightStorage().set(relX, y, relZ, lightValue); this.getSkyLightStorage().set(relX, y, relZ, lightValue);
} }
@Override
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
private ChunkLightStorage getSkyLightStorage() private ChunkLightStorage getSkyLightStorage()
{ {
@@ -478,9 +473,7 @@ public class ChunkWrapper implements IChunkWrapper
}); });
#else #else
this.chunk.findBlockLightSources((blockPos, blockState) -> this.chunk.findBlockLightSources((blockPos, blockState) ->
{ this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ())));
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
#endif #endif
} }
@@ -511,9 +504,8 @@ public class ChunkWrapper implements IChunkWrapper
/** Should be called after client light updates are triggered. */ /** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect() private void updateIsClientLightingCorrect()
{ {
if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel) if (this.chunk instanceof LevelChunk levelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{ {
LevelChunk levelChunk = (LevelChunk) this.chunk;
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource(); ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null && this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_VER <= MC_1_17_1 #if MC_VER <= MC_1_17_1
@@ -145,9 +145,9 @@ public class ClassicConfigGUI
case 0: case 0:
((EntryInfo) info.guiValue).error = null; break; ((EntryInfo) info.guiValue).error = null; break;
case -1: case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry<?>) info).getMin())); break;
case 1: case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry<?>) info).getMax())); break;
case 2: case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break;
} }
@@ -235,7 +235,7 @@ public class ClassicConfigGUI
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile(); ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent); Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
} }
@Override @Override
@@ -259,7 +259,7 @@ public class ClassicConfigGUI
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -319,7 +319,7 @@ public class ClassicConfigGUI
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
} }
@@ -332,7 +332,7 @@ public class ClassicConfigGUI
if (ConfigEntry.class.isAssignableFrom(info.getClass())) if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{ {
Button.OnPress btnAction = button -> { Button.OnPress btnAction = button -> {
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry) info).getDefaultValue()); ((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry<?>) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0; ((EntryInfo) info.guiValue).index = 0;
this.reload = true; this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this); Objects.requireNonNull(minecraft).setScreen(this);
@@ -376,9 +376,7 @@ public class ClassicConfigGUI
} }
if (ConfigUIButton.class.isAssignableFrom(info.getClass())) if (ConfigUIButton.class.isAssignableFrom(info.getClass()))
{ {
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> { Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> ((ConfigUIButton) info).runAction()));
((ConfigUIButton) info).runAction();
}));
this.list.addButton(widget, null, null, null); this.list.addButton(widget, null, null, null);
return; return;
} }
@@ -25,19 +25,18 @@ public class GetConfigScreen
// This shouldn't be here, but I need a way to test it after Minecraft inits its assets // This shouldn't be here, but I need a way to test it after Minecraft inits its assets
//System.out.println(ConfigBase.INSTANCE.generateLang(false, true)); //System.out.println(ConfigBase.INSTANCE.generateLang(false, true));
switch (useScreen) return switch (useScreen)
{ {
case Classic: case Classic -> ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client"); case OpenGL ->
case OpenGL: {
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title"); MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
return null; yield null;
}
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title"); // case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX: case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title"); default -> throw new IllegalArgumentException("No config screen implementation defined for [" + useScreen + "].");
default: };
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
}
} }
} }
@@ -134,9 +134,7 @@ public class ChangelogScreen extends DhScreen
this.addBtn( // Close this.addBtn( // Close
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> { MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> this.onClose())
this.onClose();
})
); );
@@ -76,7 +76,7 @@ public class UpdateModScreen extends DhScreen
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "logo.png"), new ResourceLocation(ModInfo.ID, "logo.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
@@ -107,7 +107,7 @@ public class UpdateModScreen extends DhScreen
0, 0, 0, 0,
// Some textuary stuff // Some textuary stuff
0, 0,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -135,9 +135,7 @@ public class UpdateModScreen extends DhScreen
}) })
); );
this.addBtn( // Later (not now) this.addBtn( // Later (not now)
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> { MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> this.onClose())
this.onClose();
})
); );
this.addBtn( // Never this.addBtn( // Never
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> { MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
@@ -127,20 +127,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
case ENABLED: case ENABLED:
switch (lodDirection) return switch (lodDirection) {
{ case DOWN -> 0.5F;
case DOWN: default -> 1.0F;
return 0.5F; case NORTH, SOUTH -> 0.8F;
default: case WEST, EAST -> 0.6F;
case UP: };
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case DISABLED: case DISABLED:
return 1.0F; return 1.0F;
@@ -250,7 +242,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override @Override
public ArrayList<ILevelWrapper> getAllServerWorlds() public ArrayList<ILevelWrapper> getAllServerWorlds()
{ {
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>(); ArrayList<ILevelWrapper> worlds = new ArrayList<>();
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds) for (ServerLevel world : serverWorlds)
@@ -21,7 +21,7 @@ public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!"); throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
} }
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
return this.dedicatedServer.getServerDirectory(); return this.dedicatedServer.getServerDirectory();
#else #else
return this.dedicatedServer.getServerDirectory().toFile(); return this.dedicatedServer.getServerDirectory().toFile();
@@ -120,6 +120,14 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z()); return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
} }
@Override
public DhBlockPos getCameraBlockPosition()
{
Camera camera = MC.gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override @Override
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */ /** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect() public boolean playerHasBlindingEffect()
@@ -140,6 +148,43 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3d(projectedView.x, projectedView.y, projectedView.z); return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
} }
@Override
public Mat4f getWorldViewMatrix()
{
Camera camera = MC.gameRenderer.getMainCamera();
Vector3f cameraVec3 = new Vector3f(
(float)camera.getPosition().x,
(float)camera.getPosition().y,
(float)camera.getPosition().z);
cameraVec3 = cameraVec3.negate();
Matrix4f matWorldView = new Matrix4f()
.rotateX((float)Math.toRadians(camera.getXRot()))
.rotateY((float)Math.toRadians(camera.getYRot() + 180f))
.translate(cameraVec3);
return new Mat4f(matWorldView);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
#if MC_VER < MC_1_17_1
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
#else
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
#endif
}
@Override
public double getGamma()
{
#if MC_VER < MC_1_19_2
return MC.options.gamma;
#else
return MC.options.gamma().get();
#endif
}
@Override @Override
public Color getFogColor(float partialTicks) public Color getFogColor(float partialTicks)
{ {
@@ -165,7 +210,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
if (MC.level.dimensionType().hasSkyLight()) if (MC.level.dimensionType().hasSkyLight())
{ {
float frameTime; float frameTime;
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
frameTime = MC.getFrameTime(); frameTime = MC.getFrameTime();
#else #else
frameTime = MC.getTimer().getRealtimeDeltaTicks(); frameTime = MC.getTimer().getRealtimeDeltaTicks();
@@ -269,6 +314,77 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return getRenderTarget().viewHeight; return getRenderTarget().viewHeight;
} }
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
*/
public boolean usingBackupGetVanillaRenderedChunks = false;
@Override
public HashSet<DhChunkPos> getVanillaRenderedChunks()
{
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
if (sodium != null)
{
return sodium.getNormalRenderedChunks();
}
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
if (optifine != null)
{
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
if (pos == null)
pos = getMaximumRenderedChunks();
return pos;
}
if (!usingBackupGetVanillaRenderedChunks)
{
try
{
#if MC_VER < MC_1_20_2
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if MC_VER < MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox =
#if MC_VER < MC_1_18_2 chunk.chunk.bb;
#else chunk.chunk.getBoundingBox(); #endif
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#else
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox = chunk.getBoundingBox();
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#endif
}
catch (LinkageError e)
{
try
{
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
+ " Using Backup Method.");
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7eOverdraw prevention will be worse than normal.");
}
catch (Exception e2)
{
}
LOGGER.error("getVanillaRenderedChunks Error: ", e);
usingBackupGetVanillaRenderedChunks = true;
}
}
return getMaximumRenderedChunks();
}
@Override @Override
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); } public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@@ -5,15 +5,14 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache; import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.*; import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -23,7 +22,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -48,7 +46,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level; private final ClientLevel level;
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>(); private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
private BlockStateWrapper dirtBlockWrapper; private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper; private BiomeWrapper plainsBiomeWrapper;
@@ -113,7 +111,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.error("Failed to get server side wrapper for client level: " + this.level); LOGGER.error("Failed to get server side wrapper for client level: " + level);
return null; return null;
} }
} }
@@ -125,13 +123,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper) public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState)
{ {
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent( return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
((BlockStateWrapper) blockWrapper).blockState,
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, pos);
} }
@Override @Override
@@ -151,12 +145,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
} }
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper); return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO);
} }
@Override
public void clearBlockColorCache() { this.blockCache.clear(); }
@Override @Override
public IBiomeWrapper getPlainsBiomeWrapper() public IBiomeWrapper getPlainsBiomeWrapper()
{ {
@@ -27,7 +27,9 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos;
@@ -35,6 +37,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -147,7 +147,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (index == -1) continue; if (index == -1) continue;
times.get(index).add(e.timeNs); times.get(index).add(e.timeNs);
} }
times.get(0).add(event.getTotalTimeNs()); times.getFirst().add(event.getTotalTimeNs());
} }
public String toString() public String toString()
@@ -224,8 +224,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// constructors // // constructors //
//==============// //==============//
public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP; public static ImmutableMap<EDhApiWorldGenerationStep, Integer> BorderNeeded;
public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED; public static int MaxBorderNeeded;
static static
{ {
@@ -253,13 +253,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0); builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.FEATURES, 0); builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
builder.put(EDhApiWorldGenerationStep.LIGHT, 0); builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build(); BorderNeeded = builder.build();
MaxBorderNeeded = BorderNeeded.values().stream().mapToInt(Integer::intValue).max().getAsInt();
// TODO this is a test to see if the additional boarder is actually necessary or not.
// If world generators end up having infinite loops or other unexplained issues,
// this should be set back to the commented out logic below
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
//MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.values().stream().mapToInt(Integer::intValue).max().getAsInt();
} }
public BatchGenerationEnvironment(IDhServerLevel serverlevel) public BatchGenerationEnvironment(IDhServerLevel serverlevel)
@@ -388,177 +383,171 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{ {
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos); EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
// Minecraft's generation events expect odd chunk width areas (3x3, 7x7, or 11x11), ArrayGridList<ChunkWrapper> chunkWrapperList;
// but DH submits square generation events (4x4). DhLitWorldGenRegion region;
// We handle this later, although that handling would need to change if the gen size ever changed. DummyLightEngine dummyLightEngine;
LodUtil.assertTrue(genEvent.size % 2 == 0, "Generation events are expected to be an evan number of chunks wide."); LightGetterAdaptor adaptor;
int borderSize = MaxBorderNeeded;
int refSize = genEvent.size + borderSize * 2;
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
int refSize = (genEvent.size - 1) + (borderSize * 2);
int refPosX = genEvent.minPos.x - borderSize; int refPosX = genEvent.minPos.x - borderSize;
int refPosZ = genEvent.minPos.z - borderSize; int refPosZ = genEvent.minPos.z - borderSize;
LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.params.level); try
DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor);
//====================================//
// offset and generate odd width area //
//====================================//
// reused data between each offset
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = new HashMap<>();
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
// while still submitting odd numbers to MC's internal generators
for (int xOffset = 0; xOffset < 2; xOffset++)
{ {
// final is so the offset can be used in lambdas ArrayGridList<ChunkAccess> totalChunks;
final int xOffsetFinal = xOffset;
for (int zOffset = 0; zOffset < 2; zOffset++) adaptor = new LightGetterAdaptor(this.params.level);
dummyLightEngine = new DummyLightEngine(adaptor);
//=============================//
// try getting existing chunks //
//=============================//
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
IEmptyChunkGeneratorFunc emptyChunkGeneratorFunc = (int x, int z) ->
{ {
final int zOffsetFinal = zOffset; ChunkPos chunkPos = new ChunkPos(x, z);
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
ChunkAccess newChunk = null;
try
//================//
// variable setup //
//================//
int radius = refSize / 2;
int centerX = refPosX + radius + xOffset;
int centerZ = refPosZ + radius + zOffset;
// get/create the list of chunks we're going to generate
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
refSize,
(x, z) -> this.generateEmptyChunk(
x + refPosX + xOffsetFinal,
z + refPosZ + zOffsetFinal,
chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos));
ChunkAccess centerChunk = regionChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, regionChunks,
ChunkStatus.STRUCTURE_STARTS, radius,
// this method shouldn't be necessary since we're passing in a pre-populated
// list of chunks, but just in case
(x, z) -> this.generateEmptyChunk(x, z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos)
);
lightGetterAdaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=========================//
// create chunk wrappers //
// and get existing chunks //
//=========================//
ArrayGridList<ChunkWrapper> chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize);
regionChunks.forEachPos((relX, relZ) ->
{ {
// ArrayGridList's use relative positions and don't have a center position // get the chunk
// so we need to use the offsetFinal to select the correct position CompoundTag chunkData = this.getChunkNbtData(chunkPos);
DhChunkPos chunkPos = new DhChunkPos(relX + xOffsetFinal, relZ + zOffsetFinal); newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
ChunkAccess chunk = regionChunks.get(relX, relZ);
if (chunkWrappersByDhPos.containsKey(chunkPos)) if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{ {
chunkWrapperList.set(relX, relZ, chunkWrappersByDhPos.get(chunkPos)); // attempt to get chunk lighting
} ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
else if (chunk != null) if (combinedLights != null)
{
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(relX, relZ, chunkWrapper);
// try setting the wrapper's lighting
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{ {
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos())); chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos())); chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
chunkWrapper.setUseDhLighting(true);
chunkWrapper.setIsDhLightCorrect(true);
} }
chunkWrappersByDhPos.put(chunkPos, chunkWrapper);
} }
else //if (chunk == null) }
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (newChunk == null)
{
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , this.params.level #endif
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
);
}
return newChunk;
};
totalChunks = new ArrayGridList<>(refSize, (x, z) -> emptyChunkGeneratorFunc.generate(x + refPosX, z + refPosZ));
int radius = refSize / 2;
int centerX = refPosX + radius;
int centerZ = refPosZ + radius;
ChunkAccess centerChunk = totalChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, totalChunks,
ChunkStatus.STRUCTURE_STARTS, radius, emptyChunkGeneratorFunc);
adaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=======================//
// create chunk wrappers //
//=======================//
chunkWrapperList = new ArrayGridList<>(totalChunks.gridSize);
totalChunks.forEachPos((x, z) ->
{
ChunkAccess chunk = totalChunks.get(x, z);
if (chunk != null)
{
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(x, z, chunkWrapper);
// try setting the wrapper's lighting
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{ {
LodUtil.assertNotReach("Programmer Error: No chunk found in grid list, position offset is likely wrong."); chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setUseDhLighting(true);
chunkWrapper.setIsDhLightCorrect(true);
} }
}); }
});
//=================// //=================//
// generate chunks // // generate chunks //
//=================// //=================//
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region); this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
genEvent.timer.nextEvent("cleanup");
genEvent.timer.nextEvent("cleanup"); }
} catch (StepStructureStart.StructStartCorruptedException f)
{
genEvent.threadedParam.markAsInvalid();
throw (RuntimeException) f.getCause();
} }
ArrayGridList<ChunkWrapper> finalGenChunks = GetCutoutFrom(chunkWrapperList, borderSize);
for (int offsetY = 0; offsetY < finalGenChunks.gridSize; offsetY++)
//=========================//
// submit generated chunks //
//=========================//
for (DhChunkPos dhChunkPos : chunkWrappersByDhPos.keySet())
{ {
ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhChunkPos); for (int offsetX = 0; offsetX < finalGenChunks.gridSize; offsetX++)
ChunkAccess target = wrappedChunk.getChunk();
if (target instanceof LevelChunk)
{ {
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 ChunkWrapper wrappedChunk = finalGenChunks.get(offsetX, offsetY);
((LevelChunk) target).setLoaded(true); ChunkAccess target = wrappedChunk.getChunk();
#else if (target instanceof LevelChunk)
((LevelChunk) target).loaded = true; {
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
((LevelChunk) target).setLoaded(true);
#else
((LevelChunk) target).loaded = true;
#endif
}
if (!wrappedChunk.isLightCorrect())
{
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
}
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
#if MC_VER >= MC_1_18_2
boolean isPartial = target.isOldNoiseGeneration();
#endif #endif
} if (isFull)
{
if (!wrappedChunk.isLightCorrect()) LOAD_LOGGER.info("Detected full existing chunk at {}", target.getPos());
{ genEvent.resultConsumer.accept(wrappedChunk);
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false"); }
} #if MC_VER >= MC_1_18_2
else if (isPartial)
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk; {
#if MC_VER >= MC_1_18_2 LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos());
boolean isPartial = target.isOldNoiseGeneration(); genEvent.resultConsumer.accept(wrappedChunk);
#endif }
if (isFull) #endif
{ else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
LOAD_LOGGER.debug("Detected full existing chunk at {}", target.getPos()); {
genEvent.resultConsumer.accept(wrappedChunk); genEvent.resultConsumer.accept(wrappedChunk);
} }
#if MC_VER >= MC_1_18_2 else
else if (isPartial) {
{ genEvent.resultConsumer.accept(wrappedChunk);
LOAD_LOGGER.debug("Detected old existing chunk at {}", target.getPos()); }
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
{
genEvent.resultConsumer.accept(wrappedChunk);
}
else
{
genEvent.resultConsumer.accept(wrappedChunk);
} }
} }
@@ -567,58 +556,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (PREF_LOGGER.canMaybeLog()) if (PREF_LOGGER.canMaybeLog())
{ {
genEvent.threadedParam.perf.recordEvent(genEvent.timer); genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.debugInc("{}", genEvent.timer); PREF_LOGGER.infoInc("{}", genEvent.timer);
} }
} }
private ChunkAccess generateEmptyChunk(
int x, int z,
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos)
{
ChunkPos chunkPos = new ChunkPos(x, z);
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
if (generatedChunkByDhPos.containsKey(dhChunkPos))
{
return generatedChunkByDhPos.get(dhChunkPos);
}
ChunkAccess newChunk = null;
try
{
// get the chunk
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
}
}
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (newChunk == null)
{
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , this.params.level #endif
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
);
}
generatedChunkByDhPos.put(dhChunkPos, newChunk);
return newChunk;
}
private CompoundTag getChunkNbtData(ChunkPos chunkPos) private CompoundTag getChunkNbtData(ChunkPos chunkPos)
{ {
ServerLevel level = this.params.level; ServerLevel level = this.params.level;
@@ -669,7 +609,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{ {
try try
{ {
LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk."); LOAD_LOGGER.info("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
return ChunkLoader.read(level, chunkPos, chunkData); return ChunkLoader.read(level, chunkPos, chunkData);
} }
catch (Exception e) catch (Exception e)
@@ -699,9 +639,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
); );
} }
public void generateDirect( public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border, GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException
@@ -716,9 +653,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunksToGenerate.forEach((chunkWrapper) -> chunksToGenerate.forEach((chunkWrapper) ->
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk instanceof ProtoChunk) if (chunk instanceof ProtoChunk protoChunk)
{ {
ProtoChunk protoChunk = ((ProtoChunk) chunk);
protoChunk.setLightEngine(region.getLightEngine()); protoChunk.setLightEngine(region.getLightEngine());
} }
@@ -833,8 +769,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
} }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); } private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
//private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); } private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step)); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
@Override @Override
@@ -43,7 +43,6 @@ public final class GenerationEvent
public final int id; public final int id;
public final ThreadedParameters threadedParam; public final ThreadedParameters threadedParam;
public final DhChunkPos minPos; public final DhChunkPos minPos;
/** the number of chunks wide this event is */
public final int size; public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep; public final EDhApiWorldGenerationStep targetGenerationStep;
public EventTimer timer = null; public EventTimer timer = null;
@@ -74,10 +73,10 @@ public final class GenerationEvent
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
//if (size % 2 == 0) if (size % 2 == 0)
//{ {
// size += 1; // size must be odd for vanilla world gen regions to work size += 1; // size must be odd for vanilla world gen regions to work
//} }
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer); GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
@@ -94,7 +93,9 @@ public final class GenerationEvent
//LOGGER.info("generating [{}]", event.minPos); //LOGGER.info("generating [{}]", event.minPos);
genEnvironment.generateLodFromList(generationEvent); genEnvironment.generateLodFromList(generationEvent);
} }
catch (InterruptedException ignored) { } catch (InterruptedException ignored)
{
}
finally finally
{ {
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); BatchGenerationEnvironment.isDistantGeneratorThread.remove();
@@ -125,6 +126,21 @@ public final class GenerationEvent
return this.future.isCancelled(); return this.future.isCancelled();
} }
public boolean tooClose(int minX, int minZ, int width)
{
int aMinX = this.minPos.x;
int aMinZ = this.minPos.z;
int aSize = this.size;
// Account for required empty chunks in the border
aSize += 1;
width += 1;
// Do a AABB to AABB intersection test
return (aMinX + aSize >= minX &&
aMinX <= minX + width &&
aMinZ + aSize >= minZ &&
aMinZ <= minZ + width);
}
public void refreshTimeout() public void refreshTimeout()
{ {
this.timeoutTime = System.nanoTime(); this.timeoutTime = System.nanoTime();
@@ -85,7 +85,7 @@ import net.minecraft.world.level.material.Fluids;
#if MC_VER == MC_1_20_6 #if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType; import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER >= MC_1_21_1 #elif MC_VER == MC_1_21
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType; import net.minecraft.world.level.chunk.status.ChunkType;
#endif #endif
@@ -294,7 +294,7 @@ public class ChunkLoader
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif #endif
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); : new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10) biomeContainer = tagSection.contains("biomes", 10)
@@ -309,7 +309,7 @@ public class ChunkLoader
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif #endif
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); : new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif #endif
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@@ -325,7 +325,7 @@ public class ChunkLoader
} }
private static private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21_1 ChunkType #elif MC_VER < MC_1_21 ChunkType
#else ChunkType #endif #else ChunkType #endif
readChunkType(CompoundTag tagLevel) readChunkType(CompoundTag tagLevel)
{ {
@@ -434,7 +434,7 @@ public class ChunkLoader
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++) for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{ {
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex); Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag)) if (!(chunkSectionTag instanceof CompoundTag chunkSectionCompoundTag))
{ {
if (!lightingSectionErrorLogged) if (!lightingSectionErrorLogged)
{ {
@@ -443,10 +443,9 @@ public class ChunkLoader
} }
return null; return null;
} }
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0 // if null all lights = 0
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight"); byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight"); byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
@@ -1,6 +1,6 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER >= MC_1_21_1 #if MC_VER >= MC_1_21
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
@@ -63,7 +63,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.*; import net.minecraft.world.level.chunk.status.*;
#endif #endif
#if MC_VER >= MC_1_21_1 #if MC_VER == MC_1_21
import net.minecraft.util.StaticCache2D; import net.minecraft.util.StaticCache2D;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.GenerationChunkHolder; import net.minecraft.server.level.GenerationChunkHolder;
@@ -84,7 +84,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
private final ChunkPos firstPos; private final ChunkPos firstPos;
private final List<ChunkAccess> cache; private final List<ChunkAccess> cache;
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>(); private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<>();
/** /**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe, * Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
@@ -127,7 +127,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
{ {
#if MC_VER == MC_1_16_5 #if MC_VER == MC_1_16_5
super(serverLevel, chunkList); super(serverLevel, chunkList);
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21
super(serverLevel, chunkList, chunkStatus, writeRadius); super(serverLevel, chunkList, chunkStatus, writeRadius);
#else #else
super(serverLevel, super(serverLevel,
@@ -142,7 +142,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
centerChunk); centerChunk);
#endif #endif
this.firstPos = chunkList.get(0).getPos(); this.firstPos = chunkList.getFirst().getPos();
this.generator = generator; this.generator = generator;
this.lightEngine = lightEngine; this.lightEngine = lightEngine;
this.writeRadius = writeRadius; this.writeRadius = writeRadius;
@@ -231,11 +231,9 @@ public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureF
Map<Structure, LongSet> map = chunk.getAllReferences(); Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder(); ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
while (var5.hasNext()) for (Map.Entry<Structure, LongSet> entry : map.entrySet())
{ {
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey(); Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature)) if (predicate.test(configuredStructureFeature))
{ {
@@ -68,7 +68,7 @@ public final class StepBiomes
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
@@ -89,7 +89,7 @@ public final class StepBiomes
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else #else
@@ -66,7 +66,7 @@ public final class StepFeatures
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
@@ -68,7 +68,7 @@ public final class StepNoise
continue; continue;
} }
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
@@ -87,7 +87,7 @@ public final class StepNoise
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState, chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else #else
@@ -54,7 +54,7 @@ public final class StepStructureReference
List<ChunkWrapper> chunkWrappers) List<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
@@ -66,7 +66,7 @@ public final class StepStructureReference
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
@@ -83,7 +83,7 @@ public final class StepStructureStart
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
@@ -65,7 +65,7 @@ public final class StepSurface
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS); ((ProtoChunk) chunk).setStatus(STATUS);
#else #else
((ProtoChunk) chunk).setPersistedStatus(STATUS); ((ProtoChunk) chunk).setPersistedStatus(STATUS);
+2 -2
View File
@@ -23,8 +23,8 @@ loom {
} }
remapJar { remapJar {
inputFile = shadowJar.archiveFile inputFile = shadeDowngradedApi.archiveFile
dependsOn shadowJar dependsOn shadeDowngradedApi
} }
@@ -106,7 +106,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// tick events // // tick events //
//=============// //=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); }); ClientTickEvents.START_CLIENT_TICK.register((client) -> ClientApi.INSTANCE.clientTickEvent());
@@ -117,11 +117,8 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// ClientChunkLoadEvent // ClientChunkLoadEvent
ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> ClientChunkEvents.CHUNK_LOAD.register((level, chunk) ->
{ {
if (MC.clientConnectedToDedicatedServer()) IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
{ SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
}
}); });
// (kinda) block break event // (kinda) block break event
@@ -203,6 +200,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
}); });
// Client Chunk Save
ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkUnloadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
});
//==============// //==============//
// render event // // render event //
@@ -222,7 +227,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()), this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
modelViewMatrix, modelViewMatrix,
projectionMatrix, projectionMatrix,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
renderContext.tickDelta() renderContext.tickDelta()
#else #else
renderContext.tickCounter().getGameTimeDeltaTicks() renderContext.tickCounter().getGameTimeDeltaTicks()
@@ -124,7 +124,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium")) if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2
#endif #endif
if (ConfigBase.INSTANCE == null) if (ConfigBase.INSTANCE == null)
@@ -1,7 +1,5 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper; import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
@@ -12,7 +10,6 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@@ -78,14 +75,6 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
// ServerTickEvent // ServerTickEvent
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent()); ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test world gen overrides without having to build a separate API project
if (false)
{
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
}
// ServerWorldLoadEvent // ServerWorldLoadEvent
//TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?) //TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?)
ServerLifecycleEvents.SERVER_STARTING.register((server) -> ServerLifecycleEvents.SERVER_STARTING.register((server) ->
@@ -116,7 +116,7 @@ public class MixinLevelRenderer
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level), ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level),
mcModelViewMatrix, mcModelViewMatrix,
mcProjectionMatrix, mcProjectionMatrix,
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
Minecraft.getInstance().getFrameTime() Minecraft.getInstance().getFrameTime()
#else #else
Minecraft.getInstance().getTimer().getRealtimeDeltaTicks() Minecraft.getInstance().getTimer().getRealtimeDeltaTicks()
@@ -98,7 +98,7 @@ public class MixinMinecraft
} }
else else
{ {
versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"); versionId = GitlabGetter.INSTANCE.projectPipelines.getFirst().get("sha");
} }
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
@@ -45,7 +45,7 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
#endif #endif
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
import net.minecraft.client.gui.screens.OptionsScreen; import net.minecraft.client.gui.screens.OptionsScreen;
#else #else
import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.options.OptionsScreen;
@@ -64,7 +64,7 @@ public class MixinOptionsScreen extends Screen
/** Texture used for the config opening button */ /** Texture used for the config opening button */
@Unique @Unique
private static final ResourceLocation ICON_TEXTURE = private static final ResourceLocation ICON_TEXTURE =
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/button.png"); new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png"); ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
@@ -104,14 +104,14 @@ public class MixinOptionsScreen extends Screen
// add the button to the correct location in the UI // add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements? // TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child; LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.getFirst().child;
// determine how wide the other option buttons are so we can put our botton to the left of them all // determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0); AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> { width.addAndGet(x.getWidth()); }); layout.visitChildren(x -> width.addAndGet(x.getWidth()));
width.addAndGet(-10); // padding between the DH button and the FOV slider width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); }); layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> settings.paddingLeft(width.get() * -1));
layout.arrangeElements(); layout.arrangeElements();
#endif #endif
@@ -1,6 +1,8 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import net.minecraft.server.level.ChunkMap; import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
@@ -30,6 +32,62 @@ public class MixinChunkMap
// don't need the chunk(s) before MC has finished saving them // don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE)) @Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci) private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); } {
// true means a chunk was saved to disk
if (ci.getReturnValue())
{
// TODO is this validation necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
//=====================================//
// corrupt/incomplete chunk validation //
//=====================================//
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
//==================//
// biome validation //
//==================//
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, this.level, ServerLevelWrapper.getWrapper(this.level)),
ServerLevelWrapper.getWrapper(this.level)
);
}
}
} }
@@ -1,28 +0,0 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger;
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
{
LOGGER.info("DH Level: ["+event.value.levelWrapper.getDimensionType()+"] loaded.");
// Note: whenever you use a wrapper method on a new Minecraft version it is recommended that you
// call wrapper.getClass() to determine which object the API will return before you try casting it.
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
// override the core DH world generator for this level
IDhApiWorldGenerator exampleWorldGen = new TestWorldGenerator(level);
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
}
}
@@ -1,114 +0,0 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratorReturnType;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.AbstractDhApiChunkWorldGenerator;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
public class TestWorldGenerator extends AbstractDhApiChunkWorldGenerator
{
private final ServerLevel level;
private final IDhApiLevelWrapper levelWrapper;
//=============//
// constructor //
//=============//
public TestWorldGenerator(ServerLevel level)
{
this.level = level;
this.levelWrapper = ServerLevelWrapper.getWrapper(level);
}
//============//
// properties //
//============//
@Override
public EDhApiWorldGeneratorReturnType getReturnType() { return EDhApiWorldGeneratorReturnType.API_CHUNKS; }
@Override
public boolean runApiChunkValidation() { return true; }
//==================//
// chunk generation //
//==================//
@Override
public Object[] generateChunk(int chunkX, int chunkZ, EDhApiDistantGeneratorMode eDhApiDistantGeneratorMode)
{
ChunkAccess chunk = this.level.getChunk(chunkX, chunkZ);
return new Object[] { chunk, this.level };
}
@Override
public DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode)
{
// this test is only validated for 1.18.2 and up
// (and it is only needed when testing world gen overrides/API chunks, so it isn't normally needed)
#if MC_VER >= MC_1_18_2
ChunkAccess chunk = this.level.getChunk(chunkPosX, chunkPosZ);
int minBuildHeight = this.level.getMinBuildHeight();
int maxBuildHeight = this.level.getMaxBuildHeight();
DhApiChunk apiChunk = DhApiChunk.create(chunkPosX, chunkPosZ, minBuildHeight, maxBuildHeight);
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
ArrayList<DhApiTerrainDataPoint> dataPoints = new ArrayList<>();
IDhApiBlockStateWrapper block = null;
IDhApiBiomeWrapper biome = null;
for (int y = minBuildHeight; y < maxBuildHeight; y++)
{
block = DhApi.Delayed.wrapperFactory.getBlockStateWrapper(new Object[]{chunk.getBlockState(new BlockPos(x, y, z))}, this.levelWrapper);
biome = DhApi.Delayed.wrapperFactory.getBiomeWrapper(new Object[]{chunk.getNoiseBiome(x, y, z)}, this.levelWrapper);
dataPoints.add(DhApiTerrainDataPoint.create((byte) 0, 0, 15, y, y + 1, block, biome));
}
apiChunk.setDataPoints(x, z, dataPoints);
}
}
return apiChunk;
#else
return null;
#endif
}
@Override
public void preGeneratorTaskStart() { /* do nothing */ }
//=========//
// cleanup //
//=========//
@Override
public void close() { /* do nothing */ }
}
@@ -32,7 +32,7 @@ public class ModMenuIntegration implements ModMenuApi
@Override @Override
public ConfigScreenFactory<?> getModConfigScreenFactory() public ConfigScreenFactory<?> getModConfigScreenFactory()
{ {
return parent -> GetConfigScreen.getScreen(parent); return GetConfigScreen::getScreen;
} }
} }
@@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IBCLibAcces
#elif MC_VER == MC_1_18_2 #elif MC_VER == MC_1_18_2
import ru.bclib.config.ClientConfig; import ru.bclib.config.ClientConfig;
import ru.bclib.config.Configs; import ru.bclib.config.Configs;
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21
import org.betterx.bclib.config.ClientConfig; import org.betterx.bclib.config.ClientConfig;
import org.betterx.bclib.config.Configs; import org.betterx.bclib.config.Configs;
#endif #endif
@@ -37,7 +37,7 @@ public class ModChecker implements IModChecker
@Override @Override
public File modLocation(String modid) public File modLocation(String modid)
{ {
return new File(FabricLoader.getInstance().getModContainer(modid).get().getOrigin().getPaths().get(0).toUri()); return new File(FabricLoader.getInstance().getModContainer(modid).get().getOrigin().getPaths().getFirst().toUri());
} }
} }
@@ -19,18 +19,20 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import java.lang.invoke.MethodHandle; import java.util.HashSet;
import java.lang.invoke.MethodHandles; import java.util.stream.Collectors;
import java.lang.invoke.MethodType;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
#if MC_VER < MC_1_20_1 import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
#endif import net.minecraft.client.Minecraft;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
@@ -39,99 +41,100 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
#else #else
import net.minecraft.world.level.LevelHeightAccessor;
#endif #endif
public class SodiumAccessor implements ISodiumAccessor public class SodiumAccessor implements ISodiumAccessor
{ {
#if MC_VER >= MC_1_20_1 private final IWrapperFactory factory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
private static MethodHandle setFogOcclusionMethod; private final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static Object sodiumPerformanceOptions;
public IClientLevelWrapper levelWrapper;
public Mat4f mcModelViewMatrix;
public Mat4f mcProjectionMatrix;
public float partialTicks;
@Override
public String getModName()
{
return "Sodium-Fabric";
}
#if MC_VER >= MC_1_17_1
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks()
{
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance();
LevelHeightAccessor height = Minecraft.getInstance().level;
#if MC_VER >= MC_1_20_1
// TODO: This is just a tmp solution, use a proper solution later
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15))).collect(Collectors.toCollection(HashSet::new));
#elif MC_VER >= MC_1_18_2
// 0b11 = Lighted chunk & loaded chunk
return renderer.getChunkTracker().getChunks(0b00).filter(
(long l) -> {
return true;
}).mapToObj(DhChunkPos::new).collect(Collectors.toCollection(HashSet::new));
#else
// TODO: Maybe use a mixin to make this more efficient, and maybe ignore changes behind the camera
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
return (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new));
#endif
}
#else
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks() {
SodiumWorldRenderer renderer = SodiumWorldRenderer.getInstance();
LevelAccessor height = Minecraft.getInstance().level;
// TODO: Maybe use a mixin to make this more efficient
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
FakeChunkEntity AABB = new FakeChunkEntity(chunk.x, chunk.z, height.getMaxBuildHeight());
return (renderer.isEntityVisible(AABB));
}).collect(Collectors.toCollection(HashSet::new));
}
private static class FakeChunkEntity extends Entity {
public int cx;
public int cz;
public int my;
public FakeChunkEntity(int chunkX, int chunkZ, int maxHeight) {
super(EntityType.AREA_EFFECT_CLOUD, null);
cx = chunkX;
cz = chunkZ;
my = maxHeight;
}
@Override
public AABB getBoundingBoxForCulling() {
return new AABB(cx*16+1, 1, cz*16+1,
cx*16+15, my-1, cz*16+15);
}
@Override
protected void defineSynchedData() {}
@Override
protected void readAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
protected void addAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
public Packet<?> getAddEntityPacket() {
throw new UnsupportedOperationException("This is a FAKE CHUNK ENTITY... For tricking the Sodium to check a AABB.");
}
}
#endif #endif
/** A temporary overwrite for a config in sodium 0.5 to fix their terrain from showing, will be removed once a proper fix is added */
// FIXME
//======================//
// mod accessor methods //
//======================//
@Override @Override
public String getModName() { return "Sodium-Fabric"; } public void setFogOcclusion(boolean b)
//================//
// sodium methods //
//================//
/** An overwrite for a config in sodium 0.5 to fix their terrain from showing */
@Override
public void setFogOcclusion(boolean occlusionEnabled)
{ {
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
try me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
{
if (sodiumPerformanceOptions == null)
{
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
if (!sodiumV6)
{
// sodium 0.5
Class<?> optionsClass = Class.forName("me.jellysquid.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("me.jellysquid.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"me.jellysquid.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
// alternate option if referencing Sodium 0.5 directly
//me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
}
else
{
// sodium 0.6
Class<?> optionsClass = Class.forName("net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("net.caffeinemc.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
}
}
setFogOcclusionMethod.invoke(sodiumPerformanceOptions, occlusionEnabled);
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
#endif #endif
} }
//================//
// helper methods //
//================//
private static boolean classPresent(String className)
{
try
{
Class.forName(className);
return true;
}
catch (ClassNotFoundException e)
{
return false;
}
}
} }
+2 -2
View File
@@ -51,8 +51,8 @@ loom {
} }
remapJar { remapJar {
inputFile = shadowJar.archiveFile inputFile = shadeDowngradedApi.archiveFile
dependsOn shadowJar dependsOn shadeDowngradedApi
} }
def addMod(path, enabled) { def addMod(path, enabled) {
@@ -176,59 +176,53 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent @SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{ {
if (MC.clientConnectedToDedicatedServer()) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) return;
}
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{ {
return; ChunkAccess chunk = level.getChunk(event.getPos());
} this.onBlockChangeEvent(level, chunk);
});
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
} }
} }
@SubscribeEvent @SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{ {
if (MC.clientConnectedToDedicatedServer()) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) return;
}
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{ {
return; ChunkAccess chunk = level.getChunk(event.getPos());
} this.onBlockChangeEvent(level, chunk);
});
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
#if MC_VER < MC_1_19_2
LevelAccessor level = event.getWorld();
#else
LevelAccessor level = event.getLevel();
#endif
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk);
});
}
} }
} }
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
@@ -237,15 +231,20 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel); SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
} }
@SubscribeEvent @SubscribeEvent
public void clientChunkLoadEvent(ChunkEvent.Load event) public void clientChunkLoadEvent(ChunkEvent.Load event)
{ {
if (MC.clientConnectedToDedicatedServer()) ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
{ IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event)); SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel); }
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel); @SubscribeEvent
} public void clientChunkUnloadEvent(ChunkEvent.Unload event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
} }
@@ -22,8 +22,6 @@ package com.seibel.distanthorizons.forge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -106,20 +104,6 @@ public class ForgeMain extends AbstractModInitializer
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
() -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent))); () -> new ConfigScreenHandler.ConfigScreenFactory((client, parent) -> GetConfigScreen.getScreen(parent)));
#endif #endif
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
{
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded("alexscaves"))
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
}
} }
@Override @Override
@@ -137,6 +137,14 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper); IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper); this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
} }
@SubscribeEvent
public void serverChunkSaveEvent(ChunkEvent.Unload event)
{
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
}
@@ -1,35 +0,0 @@
package com.seibel.distanthorizons.forge.mixins.server;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkMap.class)
public class MixinChunkMap
{
@Unique
private static final String CHUNK_SERIALIZER_WRITE
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
"Lnet/minecraft/nbt/CompoundTag;";
@Shadow
@Final
ServerLevel level;
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
// don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
}
@@ -5,8 +5,7 @@
"mixins": [ "mixins": [
"server.MixinUtilBackgroundThread", "server.MixinUtilBackgroundThread",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinTFChunkGenerator", "server.MixinTFChunkGenerator"
"server.MixinChunkMap"
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
+2 -2
View File
@@ -5,7 +5,7 @@ org.gradle.caching=true
# Mod Info # Mod Info
mod_name=DistantHorizons mod_name=DistantHorizons
mod_version=2.2.0-a mod_version=2.1.3-a-dev
api_version=3.0.0 api_version=3.0.0
maven_group=com.seibel.distanthorizons maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons mod_readable_name=Distant Horizons
@@ -49,7 +49,7 @@ versionStr=
# This defines what MC version Intellij will use for the preprocessor # This defines what MC version Intellij will use for the preprocessor
# and what version is used automatically by build and run commands # and what version is used automatically by build and run commands
mcVer=1.21.1 mcVer=1.21
# Defines the maximum amount of memory Minecraft is allowed when run in a development environment # Defines the maximum amount of memory Minecraft is allowed when run in a development environment
#minecraftMemoryJavaArg="-Xmx4G" #minecraftMemoryJavaArg="-Xmx4G"
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+9 -13
View File
@@ -31,7 +31,7 @@ loom {
setConfigName("NeoForge Client") setConfigName("NeoForge Client")
ideConfigGenerated(true) ideConfigGenerated(true)
runDir("../run") runDir("../run")
//vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg) // vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
} }
server { server {
server() server()
@@ -42,7 +42,13 @@ loom {
} }
} }
remapJar {
inputFile = apiDowngrade.archiveFile
dependsOn apiDowngrade
// classifier null
atAccessWideners.add("distanthorizons.accesswidener")
}
def addMod(path, enabled) { def addMod(path, enabled) {
if (enabled == "2") if (enabled == "2")
@@ -50,24 +56,21 @@ def addMod(path, enabled) {
else if (enabled == "1") else if (enabled == "1")
dependencies { modCompileOnly(path) } dependencies { modCompileOnly(path) }
} }
dependencies { dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() mappings loom.layered() {
{
// Mojmap mappings // Mojmap mappings
officialMojangMappings() officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc) // Parchment mappings (it adds parameter mappings & javadoc)
parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip")
} }
// Neoforge // Neoforge
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
} }
task deleteResources(type: Delete) { task deleteResources(type: Delete) {
delete file("build/resources/main") delete file("build/resources/main")
} }
@@ -86,13 +89,6 @@ tasks.named('runClient') {
finalizedBy(deleteResources) finalizedBy(deleteResources)
} }
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
atAccessWideners.add("distanthorizons.accesswidener")
}
sourcesJar { sourcesJar {
def commonSources = project(":common").sourcesJar def commonSources = project(":common").sourcesJar
dependsOn commonSources dependsOn commonSources
@@ -63,8 +63,6 @@ import org.lwjgl.opengl.GL32;
import net.neoforged.neoforge.event.TickEvent; import net.neoforged.neoforge.event.TickEvent;
#else #else
import net.neoforged.neoforge.client.event.ClientTickEvent; import net.neoforged.neoforge.client.event.ClientTickEvent;
import java.util.concurrent.ThreadPoolExecutor;
#endif #endif
@@ -131,12 +129,11 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("level load"); LOGGER.info("level load");
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
if (!(level instanceof ClientLevel)) if (!(level instanceof ClientLevel clientLevel))
{ {
return; return;
} }
ClientLevel clientLevel = (ClientLevel) level;
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel); IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
// TODO this causes a crash due to level being set to null somewhere // TODO this causes a crash due to level being set to null somewhere
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
@@ -147,12 +144,11 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("level unload"); LOGGER.info("level unload");
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
if (!(level instanceof ClientLevel)) if (!(level instanceof ClientLevel clientLevel))
{ {
return; return;
} }
ClientLevel clientLevel = (ClientLevel) level;
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel); IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper);
} }
@@ -166,55 +162,49 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent @SubscribeEvent
public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event) public void rightClickBlockEvent(PlayerInteractEvent.RightClickBlock event)
{ {
if (MC.clientConnectedToDedicatedServer()) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) return;
{ }
return;
}
// executor to prevent locking up the render/event thread // executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected // if the getChunk() takes longer than expected
// (which can be caused by certain mods) // (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor(); var executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null) if (executor != null)
{
executor.execute(() ->
{ {
executor.execute(() -> //LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
{
//LOGGER.trace("interact or block place event at blockPos: " + event.getPos());
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
}); });
}
} }
} }
@SubscribeEvent @SubscribeEvent
public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event) public void leftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event)
{ {
if (MC.clientConnectedToDedicatedServer()) if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ()))
{ {
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(event.getPos().getX(), event.getPos().getZ())) return;
{ }
return;
}
// executor to prevent locking up the render/event thread // executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected // if the getChunk() takes longer than expected
// (which can be caused by certain mods) // (which can be caused by certain mods)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor(); var executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null) if (executor != null)
{
executor.execute(() ->
{ {
executor.execute(() -> //LOGGER.trace("break or block attack at blockPos: " + event.getPos());
{
//LOGGER.trace("break or block attack at blockPos: " + event.getPos());
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
ChunkAccess chunk = level.getChunk(event.getPos()); ChunkAccess chunk = level.getChunk(event.getPos());
this.onBlockChangeEvent(level, chunk); this.onBlockChangeEvent(level, chunk);
}); });
}
} }
} }
private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk)
@@ -224,6 +214,22 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
} }
@SubscribeEvent
public void clientChunkLoadEvent(ChunkEvent.Load event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkLoadEvent(chunk, wrappedLevel);
}
@SubscribeEvent
public void clientChunkUnloadEvent(ChunkEvent.Unload event)
{
ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), wrappedLevel);
SharedApi.INSTANCE.chunkUnloadEvent(chunk, wrappedLevel);
}
//==============// //==============//
// key bindings // // key bindings //
@@ -22,8 +22,6 @@ package com.seibel.distanthorizons.neoforge;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen; import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
@@ -85,26 +83,12 @@ public class NeoforgeMain extends AbstractModInitializer
// TODO fix potential null pointer // TODO fix potential null pointer
() -> (client, parent) -> GetConfigScreen.getScreen(parent)); () -> (client, parent) -> GetConfigScreen.getScreen(parent));
#endif #endif
if (Config.Client.Advanced.Logging.showModCompatibilityWarningsOnStartup.get())
{
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
if (modChecker.isModLoaded("alexscaves"))
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
}
} }
@Override @Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{ {
NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); }); NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> eventHandler.accept(e.getDispatcher()));
} }
@Override @Override
@@ -116,7 +100,7 @@ public class NeoforgeMain extends AbstractModInitializer
@Override @Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler) protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{ {
NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> { eventHandler.accept(e.getServer()); }); NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> eventHandler.accept(e.getServer()));
} }
@Override @Override
@@ -122,6 +122,14 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper); IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkLoadEvent(chunk, levelWrapper); this.serverApi.serverChunkLoadEvent(chunk, levelWrapper);
} }
@SubscribeEvent
public void serverChunkSaveEvent(ChunkEvent.Unload event)
{
ILevelWrapper levelWrapper = ProxyUtil.getLevelWrapper(GetEventLevel(event));
IChunkWrapper chunk = new ChunkWrapper(event.getChunk(), GetEventLevel(event), levelWrapper);
this.serverApi.serverChunkSaveEvent(chunk, levelWrapper);
}
@@ -120,7 +120,7 @@ public class MixinLevelRenderer
float frameTime; float frameTime;
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
frameTime = Minecraft.getInstance().getFrameTime(); frameTime = Minecraft.getInstance().getFrameTime();
#else #else
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks(); frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
@@ -86,7 +86,7 @@ public class MixinMinecraft
} }
else else
{ {
versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"); versionId = GitlabGetter.INSTANCE.projectPipelines.getFirst().get("sha");
} }
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
@@ -45,7 +45,7 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
#endif #endif
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
import net.minecraft.client.gui.screens.OptionsScreen; import net.minecraft.client.gui.screens.OptionsScreen;
#else #else
import net.minecraft.client.gui.screens.options.OptionsScreen; import net.minecraft.client.gui.screens.options.OptionsScreen;
@@ -64,7 +64,7 @@ public class MixinOptionsScreen extends Screen
/** Texture used for the config opening button */ /** Texture used for the config opening button */
@Unique @Unique
private static final ResourceLocation ICON_TEXTURE = private static final ResourceLocation ICON_TEXTURE =
#if MC_VER < MC_1_21_1 #if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/button.png"); new ResourceLocation(ModInfo.ID, "textures/gui/button.png");
#else #else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png"); ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/button.png");
@@ -104,14 +104,14 @@ public class MixinOptionsScreen extends Screen
// add the button to the correct location in the UI // add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements? // TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child; LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.getFirst().child;
// determine how wide the other option buttons are so we can put our botton to the left of them all // determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0); AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> { width.addAndGet(x.getWidth()); }); layout.visitChildren(x -> width.addAndGet(x.getWidth()));
width.addAndGet(-10); // padding between the DH button and the FOV slider width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); }); layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> settings.paddingLeft(width.get() * -1));
layout.arrangeElements(); layout.arrangeElements();
#endif #endif
@@ -1,35 +0,0 @@
package com.seibel.distanthorizons.neoforge.mixins.server;
import com.seibel.distanthorizons.common.commonMixins.MixinChunkMapCommon;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ChunkMap.class)
public class MixinChunkMap
{
@Unique
private static final String CHUNK_SERIALIZER_WRITE
= "Lnet/minecraft/world/level/chunk/storage/ChunkSerializer;write(" +
"Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ChunkAccess;)" +
"Lnet/minecraft/nbt/CompoundTag;";
@Shadow
@Final
ServerLevel level;
// firing at INVOKE causes issues with C2ME and is probably unnecessary since we
// don't need the chunk(s) before MC has finished saving them
@Inject(method = "save", at = @At(value = "RETURN", target = CHUNK_SERIALIZER_WRITE))
private void onChunkSave(ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{ MixinChunkMapCommon.onChunkSave(this.level, chunk, ci); }
}
@@ -5,8 +5,7 @@
"mixins": [ "mixins": [
"server.MixinUtilBackgroundThread", "server.MixinUtilBackgroundThread",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinTFChunkGenerator", "server.MixinTFChunkGenerator"
"server.MixinChunkMap"
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
+6
View File
@@ -33,6 +33,12 @@ pluginManagement {
name "ParchmentMC" name "ParchmentMC"
url "https://maven.parchmentmc.org" url "https://maven.parchmentmc.org"
} }
maven { // Used for downgrading Java versions
url "https://maven.wagyourtail.xyz/releases"
}
maven {
url "https://maven.wagyourtail.xyz/snapshots"
}
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
+1 -1
View File
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.1
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" } fabric_incompatibility_list={ "iris": "<=1.6.20" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.2
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" } fabric_incompatibility_list={ "iris": "<=1.6.20" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.91.2+1.20.4
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" } fabric_incompatibility_list={ "iris": "<=1.6.20" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.97.8+1.20.6
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" } fabric_incompatibility_list={ "iris": "<=1.6.20" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
@@ -1,8 +1,8 @@
# 1.21.1 version # 1.21 version
java_version=21 java_version=21
minecraft_version=1.21.1 minecraft_version=1.21
parchment_version=1.20.6:2024.05.01 parchment_version=1.20.6:2024.05.01
compatible_minecraft_versions=["1.21", "1.21.1"] compatible_minecraft_versions=["1.21.0"]
accessWidenerVersion=1_20_6 accessWidenerVersion=1_20_6
builds_for=fabric,neoforge builds_for=fabric,neoforge
# forge is broken due to gradle/build script issues # forge is broken due to gradle/build script issues
@@ -21,7 +21,7 @@ fabric_api_version=0.100.1+1.21
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.7.4" } fabric_incompatibility_list={ "iris": "<=1.6.20" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
@@ -38,8 +38,8 @@ fabric_api_version=0.100.1+1.21
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # (Neo)Forge loader
forge_version= forge_version=50.0.19
neoforge_version=21.1.6 neoforge_version=21.0.4-beta
# (Neo)Forge mod versions # (Neo)Forge mod versions
starlight_version_forge= starlight_version_forge=
terraforged_version= terraforged_version=