Compare commits

..

82 Commits

Author SHA1 Message Date
James Seibel 759ec3676c Add logging/messaging for corrupted DB files 2025-12-09 07:12:38 -06:00
James Seibel e5ea86bf8f Fix handling bad heightmaps 2025-12-06 12:14:03 -06:00
James Seibel 533c6a7f93 Fix compiling MC 1.21.5 2025-12-06 12:04:35 -06:00
James Seibel ff41e070fd Fix some iris issues on Neoforge 2025-12-06 11:59:39 -06:00
James Seibel b3f607e132 clean up version property files 2025-12-06 11:07:41 -06:00
James Seibel 9ecbb9cc9f Fix old version compiling 2025-12-06 10:05:32 -06:00
James Seibel 7888de8200 Move world gen files into different namespaces 2025-12-06 09:44:32 -06:00
James Seibel d2327ae836 log incomplete world gen warnings 2025-12-06 09:35:39 -06:00
James Seibel ea92a8f922 revert internal server to FEATURES to improve speed 2025-12-06 09:18:55 -06:00
James Seibel 47e97630b4 Improve pre-existing chunk handling 2025-12-04 07:52:54 -06:00
James Seibel c250a7408e enable long file paths on windows for the DB 2025-12-02 07:07:31 -06:00
James Seibel 30da01f580 TEST 2025-11-29 09:59:38 -06:00
James Seibel 2c71c2bf76 maybe fix concurrency error during world gen shutdown 2025-11-28 16:31:23 -06:00
James Seibel 13a4505d7d Fix biome blending using underground biomes 2025-11-28 15:54:06 -06:00
James Seibel 7f0eeb9f15 ignore chunk updates during internal server generation 2025-11-28 10:48:55 -06:00
James Seibel d7eabcf3a6 don't render thick snow layers 2025-11-28 09:39:22 -06:00
James Seibel 7047d0afdf fix 1.21.10 compiling 2025-11-27 22:16:48 -06:00
James Seibel f318b52280 Fix compiling for older MC versions 2025-11-27 22:12:46 -06:00
James Seibel f50613e20c world gen var renaming 2025-11-27 18:56:58 -06:00
James Seibel af3a993042 massively speed up pre-existing world importing 2025-11-27 18:51:30 -06:00
James Seibel ace1aab42e refactor internal server world gen 2025-11-27 10:44:47 -06:00
James Seibel 350d72b6ec Update .editorconfig 2025-11-26 13:55:47 -06:00
James Seibel 986a6cdc19 Update coreSubProjects 2025-11-26 13:53:10 -06:00
James Seibel 2aa8d9f489 update neoforge reference 1.21.10 6 -> 55 2025-11-26 13:52:51 -06:00
James Seibel 5652a9328c add/replace ZStd stream with block compression 2025-11-24 14:43:56 -06:00
James Seibel b7253b6549 refactor chunk file handling 2025-11-22 12:23:54 -06:00
James Seibel e026cf104c remove unneeded lambda 2025-11-22 11:59:49 -06:00
James Seibel ab4a9cbb55 Remove world gen step duplicate code 2025-11-22 11:27:01 -06:00
James Seibel a709ab6071 move internal server into its own file 2025-11-22 11:02:13 -06:00
James Seibel 3c11a2dc33 Update .editorconfig 2025-11-22 10:29:00 -06:00
James Seibel ce2aa6602a remove unused items from world gen event 2025-11-22 09:48:35 -06:00
James Seibel 738aff8ec6 remove performance recording in batch gen 2025-11-22 09:30:13 -06:00
James Seibel 91da0bf252 replace and simplify WorldGenThreadCheck 2025-11-22 09:25:55 -06:00
James Seibel af8dea9d9f Start world gen refactoring 2025-11-22 08:17:01 -06:00
James Seibel 384933d351 re-add biome blending to API config options 2025-11-18 07:42:48 -06:00
James Seibel a5344d50c2 hopefully fix some downsampling holes 2025-11-18 07:33:33 -06:00
James Seibel 2b4c5b91a4 Fix biome fading after removing FullData MinY 2025-11-16 16:48:13 -06:00
James Seibel 3090544b85 Fix rendering when Iris isn't installed 2025-11-16 16:11:56 -06:00
James Seibel 75e1bbbe17 Fix biome blending gridlines 2025-11-15 19:09:42 -06:00
James Seibel 31b604b5c8 Fix shaders when far clip fading is active 2025-11-15 18:20:52 -06:00
James Seibel 70ba93abec Fix batch generator compiling 2025-11-15 15:36:18 -06:00
James Seibel f628a7f21e Fix WorldGen after restarting generation 2025-11-15 12:08:03 -06:00
James Seibel 254f51ea5e replace server tick/world gen tick with a timer 2025-11-15 09:50:05 -06:00
James Seibel d44152dc46 fix compiling 2025-11-14 07:48:33 -06:00
James Seibel 0ef979ee6c Merge branch 'adjData' 2025-11-14 07:46:46 -06:00
James Seibel f67fb1758d Speed up shutdown and reduce logging 2025-11-14 07:46:10 -06:00
James Seibel cf643372b6 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-11-13 07:22:30 -06:00
James Seibel dc1a117f6b revert adjData core changes 34412305 2025-11-13 07:22:25 -06:00
James Seibel 2545c7e76d Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-11-13 07:21:10 -06:00
James Seibel 506ba05b34 Don't duplicate adjacent data 2025-11-13 07:18:13 -06:00
James Seibel 70be3f9364 Add varint encoding for full data 2025-11-12 07:22:00 -06:00
s809 8d9b5f66fa Prevent auto-pause while pregen is running 2025-11-11 23:48:17 +05:00
James Seibel 3a01151137 re-add GPU upload config including "none" 2025-11-10 07:33:11 -06:00
James Seibel 197051747a put zstd version in main gradle file 2025-11-10 07:31:14 -06:00
James Seibel 45efeb96fa Optimize ColumnBox building 2025-11-08 18:08:10 -06:00
James Seibel e05dff3fb9 Optimize DhTintGetter 2025-11-08 17:51:50 -06:00
s809 aec28854a3 Clean up code a bit 2025-11-08 16:10:15 +05:00
James Seibel 106d97e0a1 Revert "minor AbstractDhTintGetter optimization"
This reverts commit 6a418de153.
2025-11-07 07:02:21 -06:00
Acuadragon100 63acd94fd4 Fix /dh command being deleted after reloading. 2025-11-06 16:19:53 +01:00
James Seibel 34412305d0 adjData core changes 2025-11-04 07:48:35 -06:00
James Seibel 6a418de153 minor AbstractDhTintGetter optimization 2025-10-29 21:38:57 -05:00
James Seibel 41c6b2936b Add experimental fast loading option 2025-10-28 07:47:14 -05:00
James Seibel 97130d1535 minor optimization to tint getting 2025-10-28 07:35:26 -05:00
James Seibel d61dcfaab6 optimize BiomeWrapper getting slightly 2025-10-28 07:26:44 -05:00
James Seibel 3c3f1ef41b Add far clip fading 2025-10-25 11:54:44 -05:00
James Seibel ed1d6396fd Fix iris not setting face culling in the MC state manager 2025-10-25 08:38:34 -05:00
James Seibel 518ec18362 framebuffer name consistency fix 2025-10-25 08:37:16 -05:00
James Seibel 5715cd9266 Fixes !82 (replay mod crash during shutdown) 2025-10-23 07:18:04 -05:00
James Seibel 56953efabc disallow writing to ImposterProtoChunks during world gen 2025-10-22 07:31:59 -05:00
James Seibel 351802de4c run occlusion culling whenever saving a LOD
Also run culling for every column in an LOD, which improves compression by about 20%
- Thanks Scaevolus
2025-10-22 07:25:08 -05:00
James Seibel f60e74c838 Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons 2025-10-19 16:08:02 -05:00
James Seibel 2007a6af24 Clean up LodRendering logic 2025-10-19 16:06:19 -05:00
s809 4bc199fe14 Merge branch 'feature/server-keys' 2025-10-19 22:58:48 +05:00
James Seibel bcd9a0da2c Make LodRenderer a singleton 2025-10-18 11:43:50 -05:00
James Seibel 50c97e3ca3 Fix Batch generator registries import 2025-10-18 11:43:00 -05:00
James Seibel 7539cb94d4 Fix internal server generation not loading chunks 2025-10-17 07:27:04 -05:00
James Seibel 3a20329096 Dh and level wrapper refactoring and commenting 2025-10-17 07:21:48 -05:00
James Seibel 702002c540 up api version 4.1.0 -> 5.0.0
due to logger enum changes
2025-10-15 17:39:03 -05:00
James Seibel 7f790e2c9c merge loggers and add logger builder 2025-10-15 17:38:43 -05:00
James Seibel b3f8b03fdf up version number 2.3.6 -> 2.3.7 2025-10-13 18:03:26 -05:00
s809 c84ee721e3 Bump protocol version 2025-08-16 21:01:49 +05:00
s809 c46c056980 Add a server keys feature 2025-08-16 20:59:34 +05:00
97 changed files with 3092 additions and 2819 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ insert_final_newline = false
max_line_length = 1000 max_line_length = 1000
tab_width = 4 tab_width = 4
trim_trailing_whitespace = false trim_trailing_whitespace = false
ij_continuation_indent_size = 8 ij_continuation_indent_size = 4
ij_formatter_off_tag = @formatter:off ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = true ij_formatter_tags_enabled = true
+1 -1
View File
@@ -248,7 +248,7 @@ subprojects { p ->
// We cannot relocate this library since we call some MC classes that reference it // We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}") implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
forgeShadowMe("com.github.luben:zstd-jni:1.5.7-4") forgeShadowMe("com.github.luben:zstd-jni:${rootProject.zstd_version}")
// Compression // Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4 forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -36,7 +36,7 @@ import java.util.function.Supplier;
*/ */
public abstract class AbstractModInitializer public abstract class AbstractModInitializer
{ {
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
private CommandInitializer commandInitializer; private CommandInitializer commandInitializer;
@@ -115,8 +115,8 @@ public abstract class AbstractModInitializer
this.initializeModCompat(); this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers..."); LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
this.commandInitializer = new CommandInitializer();
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); }); this.subscribeRegisterCommandsEvent(dispatcher -> { commandInitializer.initCommands(dispatcher); });
this.subscribeServerStartingEvent(server -> this.subscribeServerStartingEvent(server ->
{ {
@@ -124,7 +124,7 @@ public abstract class AbstractModInitializer
this.initConfig(); this.initConfig();
this.postInit(); this.postInit();
this.commandInitializer.initCommands(); this.commandInitializer.onServerReady();
this.checkForUpdates(); this.checkForUpdates();
@@ -172,6 +172,7 @@ public abstract class AbstractModInitializer
{ {
ConfigHandler.tryRunFirstTimeSetup(); ConfigHandler.tryRunFirstTimeSetup();
Config.completeDelayedSetup(); Config.completeDelayedSetup();
DhLogger.runDelayedConfigSetup();
} }
private void checkForUpdates() private void checkForUpdates()
@@ -1,8 +1,8 @@
package com.seibel.distanthorizons.common; package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent; import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent; import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
@@ -15,15 +15,15 @@ import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.LogManager;
import java.io.IOException; import java.io.IOException;
import java.util.Objects; import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{ {
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), private static final DhLogger LOGGER = new DhLoggerBuilder()
() -> Config.Common.Logging.logNetworkEvent.get()); .fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
.build();
#if MC_VER >= MC_1_21_1 #if MC_VER >= MC_1_21_1
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH); public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
@@ -3,6 +3,7 @@ package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.Nullable;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE; import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.literal; import static net.minecraft.commands.Commands.literal;
@@ -12,25 +13,42 @@ import static net.minecraft.commands.Commands.literal;
*/ */
public class CommandInitializer public class CommandInitializer
{ {
private final CommandDispatcher<CommandSourceStack> commandDispatcher; private boolean serverReady = false;
/** /**
* Constructs a new instance of this class. * A received command dispatcher, which is held until the server is ready to initialize the commands.
*
* @param commandDispatcher The dispatcher to use for registering commands.
*/ */
public CommandInitializer(CommandDispatcher<CommandSourceStack> commandDispatcher) @Nullable
private CommandDispatcher<CommandSourceStack> commandDispatcher;
/**
* Notify the command initializer that the game is ready to accept commands.
* If {@link CommandInitializer#initCommands(CommandDispatcher)} has been fired before it was ready, it will also initialize the commands.
*/
public void onServerReady()
{ {
this.commandDispatcher = commandDispatcher; this.serverReady = true;
if (this.commandDispatcher != null)
{
this.initCommands(this.commandDispatcher);
this.commandDispatcher = null;
}
} }
/** /**
* Initializes all available commands. * Initializes all available commands.
* If the game is not ready yet, it stores the dispatcher to initialize the commands later.
*
* @param commandDispatcher The command dispatcher to register commands to.
*/ */
public void initCommands() public void initCommands(CommandDispatcher<CommandSourceStack> commandDispatcher)
{ {
if (!this.serverReady)
{
this.commandDispatcher = commandDispatcher;
return;
}
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh") LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
.requires(source -> source.hasPermission(4)); .requires(source -> source.hasPermission(4));
@@ -43,7 +61,7 @@ public class CommandInitializer
builder.then(new CrashCommand().buildCommand()); builder.then(new CrashCommand().buildCommand());
} }
this.commandDispatcher.register(builder); commandDispatcher.register(builder);
} }
} }
@@ -17,9 +17,9 @@ public class CrashCommand extends AbstractCommand
.requires(this::isPlayerSource) .requires(this::isPlayerSource)
.then(literal("encode") .then(literal("encode")
.executes(c -> { .executes(c -> {
assert SharedApi.getIDhServerWorld() != null; assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager() ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c)); .getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null) if (serverPlayerState != null)
{ {
@@ -29,9 +29,9 @@ public class CrashCommand extends AbstractCommand
})) }))
.then(literal("decode") .then(literal("decode")
.executes(c -> { .executes(c -> {
assert SharedApi.getIDhServerWorld() != null; assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager() ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c)); .getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null) if (serverPlayerState != null)
{ {
@@ -56,7 +56,6 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE); SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE); SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
DependencySetupDoneCheck.isDone = true;
} }
public static void createServerBindings() public static void createServerBindings()
@@ -38,7 +38,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
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.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
#if MC_VER > MC_1_17_1 #if MC_VER > MC_1_17_1
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@@ -67,7 +67,7 @@ public class WrapperFactory implements IWrapperFactory
//==============// //==============//
@Override @Override
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel) public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
{ {
if (targetLevel instanceof IDhServerLevel) if (targetLevel instanceof IDhServerLevel)
{ {
@@ -1,6 +1,7 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.BlockBiomeWrapperPair;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.DhSectionPos;
@@ -9,6 +10,7 @@ import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList;
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.core.BlockPos; import net.minecraft.core.BlockPos;
@@ -18,8 +20,11 @@ import net.minecraft.world.level.biome.Biome;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
@@ -28,30 +33,41 @@ import net.minecraft.core.Holder;
public abstract class AbstractDhTintGetter implements BlockAndTintGetter public abstract class AbstractDhTintGetter implements BlockAndTintGetter
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
protected final BiomeWrapper biomeWrapper;
protected final int smoothingRadiusInBlocks;
protected final FullDataSourceV2 fullDataSource;
protected final IClientLevelWrapper clientLevelWrapper;
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
public static final ConcurrentMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#else #else
public static final ConcurrentMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#endif #endif
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
/** returned if the color cache is incomplete */
public static final int INVALID_COLOR = Integer.MIN_VALUE;
protected BiomeWrapper biomeWrapper;
protected BlockStateWrapper blockStateWrapper;
protected FullDataSourceV2 fullDataSource;
protected int smoothingRadiusInBlocks;
protected IClientLevelWrapper clientLevelWrapper;
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
public AbstractDhTintGetter(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper) public AbstractDhTintGetter() { }
/**
* Mutates this getter so we can access the necessary
* variables for tint getting.
*/
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{ {
this.biomeWrapper = biomeWrapper; this.biomeWrapper = biomeWrapper;
this.blockStateWrapper = blockStateWrapper;
this.fullDataSource = fullDataSource; this.fullDataSource = fullDataSource;
this.clientLevelWrapper = clientLevelWrapper; this.clientLevelWrapper = clientLevelWrapper;
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get(); this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
@@ -63,8 +79,27 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// shared methods // // shared methods //
//================// //================//
/** Called by MC's tint getter */
@Override @Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockPos.getX(), blockPos.getY(), blockPos.getZ());
return this.tryGetBlockTint(mutableBlockPos, colorResolver);
}
/**
* Can be called by DH directly, skipping some of MC's logic
* to speed up tint getting slightly.
*
* @return {@link AbstractDhTintGetter#INVALID_COLOR} if any of the biomes needed for this position
* were not cached. In that case calling {@link AbstractDhTintGetter#getBlockTint(BlockPos, ColorResolver)}
* will need to be called by MC's ColorResolver so we can
* populate the color cache.
*/
public int tryGetBlockTint(DhBlockPosMutable mutableBlockPos)
{ return this.tryGetBlockTint(mutableBlockPos, null); }
private int tryGetBlockTint(DhBlockPosMutable mutableBlockPos, @Nullable ColorResolver colorResolver)
{ {
// determine how wide this data source is so we can determine // determine how wide this data source is so we can determine
// if blending should be used // if blending should be used
@@ -78,7 +113,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
if (this.smoothingRadiusInBlocks == 0 if (this.smoothingRadiusInBlocks == 0
|| dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks) || dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks)
{ {
return colorResolver.getColor(unwrapClientBiome(this.biomeWrapper.biome, this.clientLevelWrapper), blockPos.getX(), blockPos.getZ()); return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
} }
@@ -88,13 +123,14 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
int rollingGreen = 0; int rollingGreen = 0;
int rollingBlue = 0; int rollingBlue = 0;
int xMin = blockPos.getX() - this.smoothingRadiusInBlocks; int xMin = mutableBlockPos.getX() - this.smoothingRadiusInBlocks;
int xMax = blockPos.getX() + this.smoothingRadiusInBlocks; int xMax = mutableBlockPos.getX() + this.smoothingRadiusInBlocks + 1; // +1 to account for the center block
int zMin = blockPos.getZ() - this.smoothingRadiusInBlocks; int zMin = mutableBlockPos.getZ() - this.smoothingRadiusInBlocks;
int zMax = blockPos.getZ() + this.smoothingRadiusInBlocks; int zMax = mutableBlockPos.getZ() + this.smoothingRadiusInBlocks + 1;
int levelMinY = this.clientLevelWrapper.getMinHeight();
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(0, blockPos.getY(), 0);
for (int x = xMin; x < xMax; x++) for (int x = xMin; x < xMax; x++)
{ {
for (int z = zMin; z < zMax; z++) for (int z = zMin; z < zMax; z++)
@@ -105,16 +141,21 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// this can return the same position/datapoint for larger LODs duplicating work, // this can return the same position/datapoint for larger LODs duplicating work,
// however for small smoothing ranges that isn't a big deal and for large LODs // however for small smoothing ranges that isn't a big deal and for large LODs
// we ignore smoothing anyway // we ignore smoothing anyway
long dataPoint = this.fullDataSource.getAtBlockPos(mutableBlockPos); long dataPoint = this.fullDataSource.getDataPointAtBlockPos(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ(), levelMinY);
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT) if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
{ {
continue; continue;
} }
// get the color for this nearby position // get the color for this nearby position
int id = FullDataPointUtil.getId(dataPoint); int id = FullDataPointUtil.getId(dataPoint);
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id); BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
int color = colorResolver.getColor(unwrapClientBiome(biomeWrapper.biome, this.clientLevelWrapper), mutableBlockPos.getX(), mutableBlockPos.getZ()); int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
if (color == INVALID_COLOR)
{
return INVALID_COLOR;
}
// rolling average // rolling average
@@ -131,7 +172,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// just use the default center's color // just use the default center's color
if (dataPointCount == 0) if (dataPointCount == 0)
{ {
return colorResolver.getColor(unwrapClientBiome(this.biomeWrapper.biome, this.clientLevelWrapper), blockPos.getX(), blockPos.getZ()); return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
} }
int colorInt = ColorUtil.argbToInt( int colorInt = ColorUtil.argbToInt(
@@ -142,14 +183,41 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
return colorInt; return colorInt;
} }
protected static Biome unwrapClientBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome, IClientLevelWrapper clientLevelWrapper) /**
* If given a ColorResolver this will always succeed. <Br>
* If not it will attempt to use the cached color.
*/
private int tryGetClientBiomeColor(@Nullable ColorResolver colorResolver, BiomeWrapper biomeWrapper)
{ {
BiomeWrapper biomeWrapper = (BiomeWrapper)BiomeWrapper.getBiomeWrapper(biome, clientLevelWrapper); BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper);
// use the cached color if possible
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations
if (cachedColor != null)
{
return cachedColor;
}
if (colorResolver == null)
{
// no color resolver is present,
// the cache needs to be populated before
// we can use the fast path
return INVALID_COLOR;
}
int color = colorResolver.getColor(unwrapClientBiome(biomeWrapper), 0, 0);
COLOR_BY_BLOCK_BIOME_PAIR.put(pair, color);
return color;
}
protected static Biome unwrapClientBiome(BiomeWrapper biomeWrapper)
{
String biomeString = biomeWrapper.getSerialString(); String biomeString = biomeWrapper.getSerialString();
if (biomeString == null if (biomeString == null
|| biomeString.isEmpty() || biomeString.isEmpty()
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING)) || biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
{ {
// default to "plains" for empty/invalid biomes // default to "plains" for empty/invalid biomes
biomeString = "minecraft:plains"; biomeString = "minecraft:plains";
@@ -203,6 +271,21 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
*/ */
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString) private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
{ {
#if MC_VER < MC_1_18_2
Biome biome;
#else
Holder<Biome> biome;
#endif
// calling get instead of compute is slightly faster for already
// computed values
biome = BIOME_BY_RESOURCE_STRING.get(biomeResourceString);
if (biome != null)
{
return biome;
}
// cache the client biomes so we don't have to re-parse the resource location every time // cache the client biomes so we don't have to re-parse the resource location every time
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString, return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
(resourceString, existingBiome) -> (resourceString, existingBiome) ->
@@ -26,10 +26,11 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -57,7 +58,7 @@ import net.minecraft.world.level.biome.Biomes;
public class BiomeWrapper implements IBiomeWrapper public class BiomeWrapper implements IBiomeWrapper
{ {
// must be defined before AIR, otherwise a null pointer will be thrown // must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = LogManager.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
@@ -103,8 +104,7 @@ public class BiomeWrapper implements IBiomeWrapper
// constructors // // constructors //
//==============// //==============//
// TODO why not just return BiomeWrapper? public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{ {
if (biome == null) if (biome == null)
{ {
@@ -112,9 +112,10 @@ public class BiomeWrapper implements IBiomeWrapper
} }
if (WRAPPER_BY_BIOME.containsKey(biome)) BiomeWrapper biomeWrapper = WRAPPER_BY_BIOME.get(biome);
if (biomeWrapper != null)
{ {
return WRAPPER_BY_BIOME.get(biome); return biomeWrapper;
} }
else else
{ {
@@ -300,7 +301,7 @@ public class BiomeWrapper implements IBiomeWrapper
} }
foundWrapper = (BiomeWrapper) getBiomeWrapper(deserializeResult.biome, levelWrapper); foundWrapper = getBiomeWrapper(deserializeResult.biome, levelWrapper);
return foundWrapper; return foundWrapper;
} }
catch (Exception e) catch (Exception e)
@@ -35,7 +35,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 org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.awt.*; import java.awt.*;
import java.io.IOException; import java.io.IOException;
@@ -72,7 +72,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// must be defined before AIR, otherwise a null pointer will be thrown // must be defined before AIR, otherwise a null pointer will be thrown
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>(); public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
@@ -274,7 +274,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>(); HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING); baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper); rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks; return rendererIgnoredBlocks;
} }
/** /**
@@ -291,7 +291,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>(); HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING); baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper); rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks; return rendererIgnoredCaveBlocks;
} }
@@ -302,7 +302,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// lod builder helpers // // lod builder helpers //
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper) private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
{ {
// get the base blocks // get the base blocks
HashSet<String> blockStringList = new HashSet<>(); HashSet<String> blockStringList = new HashSet<>();
@@ -318,9 +318,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(","))); blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
} }
return getBlockWrappers(blockStringList, levelWrapper); return getAllBlockWrappers(blockStringList, levelWrapper);
} }
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper) private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
{ {
// deserialize each of the given resource locations // deserialize each of the given resource locations
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>(); HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
@@ -587,7 +587,8 @@ 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.equals("")) // the empty string shouldn't normally happen, but just in case
{ {
return AIR; return AIR;
} }
@@ -23,13 +23,13 @@ import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
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.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock; import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock; import net.minecraft.world.level.block.LeavesBlock;
@@ -40,7 +40,7 @@ import net.minecraft.util.RandomSource;
import java.util.Random; import java.util.Random;
#endif #endif
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
@@ -62,10 +62,8 @@ import net.minecraft.client.renderer.block.model.BlockModelPart;
*/ */
public class ClientBlockStateColorCache public class ClientBlockStateColorCache
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
// TODO it isn't that we need the level, but that we need the adjacent data
// maybe we can pass in the full data source?
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<>();
@@ -95,14 +93,11 @@ public class ClientBlockStateColorCache
#endif #endif
private final IClientLevelWrapper clientLevelWrapper; private final IClientLevelWrapper clientLevelWrapper;
private final BlockStateWrapper blockStateWrapper;
private final BlockState blockState; private final BlockState blockState;
private final LevelReader level; private final BlockStateWrapper blockStateWrapper;
private boolean isColorResolved = false; private boolean isColorResolved = false;
private int baseColor = 0; private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false; private boolean needPostTinting = false;
private int tintIndex = 0; private int tintIndex = 0;
@@ -170,18 +165,20 @@ public class ClientBlockStateColorCache
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 final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider());
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride());
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel) public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
{ {
this.blockState = blockState; this.blockState = blockState;
this.clientLevelWrapper = samplingLevel; this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
this.level = (LevelReader) samplingLevel.getWrappedMcObject(); this.clientLevelWrapper = clientLevelWrapper;
this.blockStateWrapper = BlockStateWrapper.fromBlockState(this.blockState, this.clientLevelWrapper);
this.resolveColors(); this.resolveColors();
} }
@@ -235,32 +232,29 @@ public class ClientBlockStateColorCache
this.needPostTinting = firstQuad.isTinted(); this.needPostTinting = firstQuad.isTinted();
#if MC_VER <= MC_1_21_4 #if MC_VER <= MC_1_21_4
this.needShade = firstQuad.isShade();
this.tintIndex = firstQuad.getTintIndex(); this.tintIndex = firstQuad.getTintIndex();
#else #else
this.needShade = firstQuad.shade();
this.tintIndex = firstQuad.tintIndex(); this.tintIndex = firstQuad.tintIndex();
#endif #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite, firstQuad.sprite,
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER < MC_1_21_5 #elif MC_VER < MC_1_21_5
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(), firstQuad.getSprite(),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#else #else
this.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite(), firstQuad.sprite(),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
#endif #endif
} }
else else
{ {
// Backup method. // Backup method.
this.needPostTinting = false; this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = this.getParticleIconColor();
} }
@@ -269,19 +263,11 @@ public class ClientBlockStateColorCache
{ {
// Liquid Block // Liquid Block
this.needPostTinting = true; this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = this.getParticleIconColor();
} }
//String serialString = this.blockStateWrapper.getSerialString();
//if (serialString.contains("minecraft:water")
// || serialString.contains("grass"))
//{
// BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
//}
this.isColorResolved = true; this.isColorResolved = true;
} }
finally finally
@@ -319,7 +305,7 @@ public class ClientBlockStateColorCache
} }
//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, EColorMode colorMode)
{ {
int count = 0; int count = 0;
int alpha = 0; int alpha = 0;
@@ -329,8 +315,8 @@ public class ClientBlockStateColorCache
int tempColor; int tempColor;
// don't render Chiseled blocks. // don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once. // Since EColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel) if (colorMode != EColorMode.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 < getTextureHeight(texture); v++)
@@ -348,7 +334,7 @@ public class ClientBlockStateColorCache
int b = (tempColor & 0x00FF0000) >>> 16; int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24; int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1; int scale = 1;
if (colorMode == ColorMode.Leaves) if (colorMode == EColorMode.Leaves)
{ {
//switch (//FIXME add config option) //switch (//FIXME add config option)
// case BLACK: // case BLACK:
@@ -367,11 +353,11 @@ public class ClientBlockStateColorCache
// break; //do nothing, let it count towards transparency // break; //do nothing, let it count towards transparency
} }
else if (a == 0 && colorMode != ColorMode.Glass) else if (a == 0 && colorMode != EColorMode.Glass)
{ {
continue; continue;
} }
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r)) else if (colorMode == EColorMode.Flower && (g + 25 < b || g + 25 < r))
{ {
scale = FLOWER_COLOR_SCALE; scale = FLOWER_COLOR_SCALE;
} }
@@ -454,7 +440,7 @@ public class ClientBlockStateColorCache
{ {
return calculateColorFromTexture( return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState), Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock())); EColorMode.getColorMode(this.blockState.getBlock()));
} }
@@ -463,7 +449,7 @@ public class ClientBlockStateColorCache
// public getter // // public getter //
//===============// //===============//
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos pos) public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
{ {
// 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)
@@ -487,16 +473,27 @@ public class ClientBlockStateColorCache
{ {
try try
{ {
tintColor = Minecraft.getInstance().getBlockColors() TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
.getColor(this.blockState, tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
new TintWithoutLevelOverrider(biomeWrapper, fullDataSource, this.clientLevelWrapper), // TODO can this object be cached?
McObjectConverter.Convert(pos), // try using DH's cached tint values first if possible
this.tintIndex); tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
{
// one or more tint values weren't calculated,
// we need MC's color resolver
tintColor = Minecraft.getInstance()
.getBlockColors()
.getColor(this.blockState,
tintOverride,
McObjectConverter.Convert(blockPos),
this.tintIndex);
}
} }
catch (UnsupportedOperationException e) catch (UnsupportedOperationException e)
{ {
// this exception generally occurs if the tint requires other blocks besides itself // this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e); LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState); BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
} }
} }
@@ -504,13 +501,22 @@ public class ClientBlockStateColorCache
// use the level logic only if requested // use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{ {
// this logic can't be used all the time due to it breaking some blocks tinting // the level shouldn't be used all the time due to it breaking some blocks tinting
// specifically oceans don't render correctly // specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, TintGetterOverride tintOverride = TintOverrideGetter.get();
new TintGetterOverride(this.level, biomeWrapper, fullDataSource, this.clientLevelWrapper), // TODO can this object be cached? tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
McObjectConverter.Convert(pos),
this.tintIndex); tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
{
tintColor = Minecraft.getInstance()
.getBlockColors()
.getColor(this.blockState,
tintOverride,
McObjectConverter.Convert(blockPos),
this.tintIndex);
}
} }
} }
catch (Exception e) catch (Exception e)
@@ -518,7 +524,7 @@ public class ClientBlockStateColorCache
// only display the error once per block/biome type to reduce log spam // only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState)) if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{ {
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState); BROKEN_BLOCK_STATES.add(this.blockState);
} }
} }
@@ -542,7 +548,7 @@ public class ClientBlockStateColorCache
// helper classes // // helper classes //
//================// //================//
enum ColorMode private enum EColorMode
{ {
Default, Default,
Flower, Flower,
@@ -550,7 +556,7 @@ public class ClientBlockStateColorCache
Chisel, Chisel,
Glass; Glass;
static ColorMode getColorMode(Block block) static EColorMode getColorMode(Block block)
{ {
if (block instanceof LeavesBlock) return Leaves; if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower; if (block instanceof FlowerBlock) return Flower;
@@ -41,7 +41,7 @@ import java.util.stream.Stream;
public class TintGetterOverride extends AbstractDhTintGetter public class TintGetterOverride extends AbstractDhTintGetter
{ {
private final LevelReader parent; private LevelReader parent;
@@ -49,9 +49,11 @@ public class TintGetterOverride extends AbstractDhTintGetter
// constructor // // constructor //
//=============// //=============//
public TintGetterOverride(LevelReader parent, BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper) public TintGetterOverride() { }
{
super(biomeWrapper, fullDataSource, clientLevelWrapper); public void update(LevelReader parent, BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
this.parent = parent; this.parent = parent;
} }
@@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos; 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.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
@@ -36,8 +37,8 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter
// constructor // // constructor //
//=============// //=============//
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper) public TintWithoutLevelOverrider()
{ super(biomeWrapper, fullDataSource, clientLevelWrapper); } { }
@@ -21,7 +21,6 @@ package com.seibel.distanthorizons.common.wrappers.chunk;
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.misc.MutableBlockPosWrapper; import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
@@ -38,7 +37,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.*; import java.util.*;
@@ -75,7 +74,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
public class ChunkWrapper implements IChunkWrapper public class ChunkWrapper implements IChunkWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** 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(() -> new BlockPos.MutableBlockPos());
@@ -98,9 +97,9 @@ public class ChunkWrapper implements IChunkWrapper
private int maxNonEmptyHeight = Integer.MAX_VALUE; private int maxNonEmptyHeight = Integer.MAX_VALUE;
/** will be null if we are using MC heightmaps */ /** will be null if we are using MC heightmaps */
private final int[][] solidHeightMap; private int[][] solidHeightMap = null;
/** will be null if we are using MC heightmaps */ /** will be null if we are using MC heightmaps */
private final int[][] lightBlockingHeightMap; private int[][] lightBlockingHeightMap = null;
@@ -109,23 +108,18 @@ public class ChunkWrapper implements IChunkWrapper
//=============// //=============//
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel) public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
{
this(chunk, wrappedLevel, true);
}
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel, boolean recreateHeightmaps)
{ {
this.chunk = chunk; this.chunk = chunk;
this.wrappedLevel = wrappedLevel; this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z); this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
// use DH heightmaps if requested if (recreateHeightmaps)
if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get())
{ {
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH]; this.createDhHeightMaps();
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.recalculateDhHeightMapsIfNeeded();
}
else
{
this.solidHeightMap = null;
this.lightBlockingHeightMap = null;
} }
} }
@@ -248,56 +242,59 @@ public class ChunkWrapper implements IChunkWrapper
} }
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); } private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
/** Will only run if the config says the MC heightmaps shouldn't be trusted. */ public void createDhHeightMaps()
public void recalculateDhHeightMapsIfNeeded()
{ {
// re-calculate the min/max heights for consistency (during world gen these may be wrong) // re-calculate the min/max heights for consistency (during world gen these may be wrong)
this.minNonEmptyHeight = Integer.MIN_VALUE; this.minNonEmptyHeight = Integer.MIN_VALUE;
this.maxNonEmptyHeight = Integer.MAX_VALUE; this.maxNonEmptyHeight = Integer.MAX_VALUE;
// recalculate heightmaps if needed this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
if (this.solidHeightMap != null) this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{ {
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{ {
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
// if no blocks are found the height map will be at the bottom of the world
int solidHeight = minInclusiveBuildHeight;
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&&
(
solidHeight == minInclusiveBuildHeight
|| lightBlockingHeight == minInclusiveBuildHeight
)
)
{ {
int minInclusiveBuildHeight = this.getMinNonEmptyHeight(); // is this block solid?
// if no blocks are found the height map will be at the bottom of the world if (solidHeight == minInclusiveBuildHeight
int solidHeight = minInclusiveBuildHeight; && block.isSolid())
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&& (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight))
{ {
// is this block solid? solidHeight = y;
if (solidHeight == minInclusiveBuildHeight
&& block.isSolid())
{
solidHeight = y;
}
// is this block light blocking?
if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
} }
this.solidHeightMap[x][z] = solidHeight; // is this block light blocking?
this.lightBlockingHeightMap[x][z] = lightBlockingHeight; if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
} }
this.solidHeightMap[x][z] = solidHeight;
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
} }
} }
} }
@@ -22,7 +22,7 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosi
import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity; import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.SpamReducedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.AnnotationUtil; import com.seibel.distanthorizons.core.util.AnnotationUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
@@ -38,8 +38,7 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.LogManager; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -73,8 +72,10 @@ import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.Translata
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class ClassicConfigGUI public class ClassicConfigGUI
{ {
private static final Logger LOGGER = LogManager.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final SpamReducedLogger SPAM_LOGGER = new SpamReducedLogger(4); public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
.maxCountPerSecond(1)
.build();
public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface(); public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface();
@@ -1002,7 +1003,7 @@ public class ClassicConfigGUI
catch (Exception e) catch (Exception e)
{ {
// should prevent crashing the game if there's an issue // should prevent crashing the game if there's an issue
SPAM_LOGGER.error("Unexpected gui rendering issue: ["+e.getMessage()+"]", e); RATE_LIMITED_LOGGER.error("Unexpected gui rendering issue: ["+e.getMessage()+"]", e);
} }
} }
@@ -5,11 +5,11 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen; import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
public class GetConfigScreen public class GetConfigScreen
{ {
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static EType useScreen = EType.Classic; public static EType useScreen = EType.Classic;
@@ -14,7 +14,7 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.narration.NarratableEntry; import net.minecraft.client.gui.narration.NarratableEntry;
@@ -41,7 +41,7 @@ import java.util.*;
// TODO: Make this // TODO: Make this
public class ChangelogScreen extends DhScreen public class ChangelogScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent; private Screen parent;
@@ -16,7 +16,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
#endif #endif
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
@@ -31,7 +31,7 @@ import java.util.*;
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192 // and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends DhScreen public class UpdateModScreen extends DhScreen
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent; private Screen parent;
@@ -36,9 +36,9 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; } public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
@Override @Override
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey) public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey)
{ {
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey); IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper((ClientLevel) clientLevel.getWrappedMcObject(), serverKey, levelKey);
this.serverKeyedLevel = keyedLevel; this.serverKeyedLevel = keyedLevel;
this.enabled = true; this.enabled = true;
return keyedLevel; return keyedLevel;
@@ -2,24 +2,36 @@ package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements IServerKeyedClientLevel
{ {
/** Returns the folder name the server wants the client to use. */
private final String serverKey;
/** A unique identifier (generally the level's name) for differentiating multiverse levels */ /** A unique identifier (generally the level's name) for differentiating multiverse levels */
private final String serverLevelKey; private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey) //=============//
// constructor //
//=============//
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
{ {
super(level); super(level);
this.serverKey = serverKey;
this.serverLevelKey = serverLevelKey; this.serverLevelKey = serverLevelKey;
} }
@Override
public String getServerKey() { return this.serverKey; }
//======================//
// level identification //
//======================//
@Override @Override
public String getServerLevelKey() { return this.serverLevelKey; } public String getServerLevelKey() { return this.serverLevelKey; }
@@ -27,4 +39,6 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
@Override @Override
public String getDhIdentifier() { return this.getServerLevelKey(); } public String getDhIdentifier() { return this.getServerLevelKey(); }
} }
@@ -57,7 +57,7 @@ import net.minecraft.network.chat.TextComponent;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@@ -72,7 +72,7 @@ import net.minecraft.util.profiling.Profiler;
*/ */
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper(); public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
@@ -396,4 +396,10 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
} }
@Override
public void setPreventAutoPause(boolean preventAutoPause)
{
throw new UnsupportedOperationException();
}
} }
@@ -28,7 +28,7 @@ import com.mojang.blaze3d.opengl.GlStateManager;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
@@ -54,7 +54,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
{ {
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper(); public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -59,6 +59,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAc
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.effect.MobEffects; import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.phys.Vec3;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector4f;
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags; import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@@ -67,9 +73,6 @@ import org.lwjgl.opengl.GL15;
#else #else
import net.minecraft.world.level.material.FogType; import net.minecraft.world.level.material.FogType;
#endif #endif
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
#if MC_VER >= MC_1_21_5 #if MC_VER >= MC_1_21_5
import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.opengl.GlTexture;
@@ -84,7 +87,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{ {
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance(); private static final Minecraft MC = Minecraft.getInstance();
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class); private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
@@ -318,7 +321,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public int getTargetFrameBuffer() public int getTargetFramebuffer()
{ {
// used so we can access the framebuffer shaders end up rendering to // used so we can access the framebuffer shaders end up rendering to
if (AbstractOptifineAccessor.optifinePresent()) if (AbstractOptifineAccessor.optifinePresent())
@@ -399,7 +402,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public int getTargetFrameBufferViewportWidth() public int getTargetFramebufferViewportWidth()
{ {
#if MC_VER < MC_1_21_9 #if MC_VER < MC_1_21_9
return this.getRenderTarget().viewWidth; return this.getRenderTarget().viewWidth;
@@ -409,7 +412,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public int getTargetFrameBufferViewportHeight() public int getTargetFramebufferViewportHeight()
{ {
#if MC_VER < MC_1_21_9 #if MC_VER < MC_1_21_9
return this.getRenderTarget().viewHeight; return this.getRenderTarget().viewHeight;
@@ -419,7 +422,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
} }
@Override @Override
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); } public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@Override @Override
public boolean isFogStateSpecial() public boolean isFogStateSpecial()
@@ -11,6 +11,7 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper(); public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
public DedicatedServer dedicatedServer = null; public DedicatedServer dedicatedServer = null;
public boolean preventAutoPause = false;
//=============// //=============//
@@ -49,4 +50,10 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
return this.dedicatedServer.getPlayerCount(); return this.dedicatedServer.getPlayerCount();
} }
@Override
public void setPreventAutoPause(boolean preventAutoPause)
{
this.preventAutoPause = preventAutoPause;
}
} }
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -32,7 +32,7 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper public class LightMapWrapper implements ILightMapWrapper
{ {
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class); private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private int textureId = 0; private int textureId = 0;
@@ -2,7 +2,6 @@ package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
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.ClientBlockStateColorCache;
@@ -25,8 +24,7 @@ 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.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 com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -54,7 +52,7 @@ import com.seibel.distanthorizons.core.util.ColorUtil;
public class ClientLevelWrapper implements IClientLevelWrapper public class ClientLevelWrapper implements IClientLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** /**
* weak references are to prevent rare issues * weak references are to prevent rare issues
* where, upon world closure, some levels aren't shutdown/removed properly * where, upon world closure, some levels aren't shutdown/removed properly
@@ -66,17 +64,14 @@ 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 ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
/** cached method reference to reduce GC overhead */ /** cached method reference to reduce GC overhead */
private final Function<BlockState, ClientBlockStateColorCache> cachedBlockColorCacheFunction = (blockState) -> this.createBlockColorCache(blockState); private final Function<BlockState, ClientBlockStateColorCache> createCachedBlockColorCacheFunc = (blockState) -> new ClientBlockStateColorCache(blockState, this);
private BlockStateWrapper dirtBlockWrapper; private BlockStateWrapper dirtBlockWrapper;
private BlockStateWrapper waterBlockWrapper; private IDhLevel dhLevel;
private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -168,13 +163,17 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{ {
try try
{ {
// this method only makes sense if we are running a single-player server
if (MINECRAFT.getSingleplayerServer() == null)
{
return null;
}
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
// attempt to find the server level with the same dimension type // attempt to find the server level with the same dimension type
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension // Note: this assumes only one level per dimension type, multiverse servers may not behave correctly
ServerLevelWrapper foundLevelWrapper = null; ServerLevelWrapper foundLevelWrapper = null;
// TODO: Surely there is a more efficient way to write this code
for (ServerLevel serverLevel : serverLevels) for (ServerLevel serverLevel : serverLevels)
{ {
if (serverLevel.dimension() == this.level.dimension()) if (serverLevel.dimension() == this.level.dimension())
@@ -200,16 +199,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper) public int getBlockColor(DhBlockPos blockPos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
{ {
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent( ClientBlockStateColorCache blockColorCache = this.blockColorCacheByBlockState.get(((BlockStateWrapper) blockWrapper).blockState);
((BlockStateWrapper) blockWrapper).blockState, if (blockColorCache == null)
this.cachedBlockColorCacheFunction); {
blockColorCache = this.blockColorCacheByBlockState.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
this.createCachedBlockColorCacheFunc);
}
return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, pos); return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, blockPos);
} }
/** used by {@link ClientLevelWrapper#cachedBlockColorCacheFunction} */
private ClientBlockStateColorCache createBlockColorCache(BlockState block) { return new ClientBlockStateColorCache(block, this); }
@Override @Override
@@ -232,48 +233,8 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper); return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper);
} }
@Override
public int getWaterBlockColor()
{
if (this.waterBlockWrapper == null)
{
try
{
this.waterBlockWrapper = (BlockStateWrapper) BlockStateWrapper.deserialize(BlockStateWrapper.WATER_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get water color with resource location ["+BlockStateWrapper.WATER_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return -1;
}
}
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.waterBlockWrapper);
}
@Override @Override
public void clearBlockColorCache() { this.blockCache.clear(); } public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper()
{
if (this.plainsBiomeWrapper == null)
{
try
{
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get planes biome with resource location ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return null;
}
}
return this.plainsBiomeWrapper;
}
@Override @Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); } public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@@ -331,20 +292,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return new ChunkWrapper(chunk, this); return new ChunkWrapper(chunk, this);
} }
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ)
{
ChunkSource source = this.level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{ return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); }
@Override
public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); }
@Override @Override
public ClientLevel getWrappedMcObject() { return this.level; } public ClientLevel getWrappedMcObject() { return this.level; }
@@ -352,18 +299,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public void onUnload() public void onUnload()
{ {
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level); LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null; this.dhLevel = null;
} }
@Override @Override
public File getDhSaveFolder() public File getDhSaveFolder()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getSaveStructure().getSaveFolder(this); return this.dhLevel.getSaveStructure().getSaveFolder(this);
} }
@@ -374,17 +321,19 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//===================// //===================//
@Override @Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; } public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
@Override
public IDhLevel getDhLevel() { return this.dhLevel; }
@Override @Override
public IDhApiCustomRenderRegister getRenderRegister() public IDhApiCustomRenderRegister getRenderRegister()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getGenericRenderer(); return this.dhLevel.getGenericRenderer();
} }
@Override @Override
@@ -24,21 +24,18 @@ import java.lang.ref.WeakReference;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
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.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.world.EWorldEnvironment;
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.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -52,16 +49,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
#if MC_VER < MC_1_21_3 import com.seibel.distanthorizons.core.logging.DhLogger;
#else import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
#endif
import org.apache.logging.log4j.Logger;
public class ServerLevelWrapper implements IServerLevelWrapper public class ServerLevelWrapper implements IServerLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** /**
* weak references are to prevent rare issues * weak references are to prevent rare issues
* where, upon world closure, some levels aren't shutdown/removed properly * where, upon world closure, some levels aren't shutdown/removed properly
@@ -69,8 +62,13 @@ public class ServerLevelWrapper implements IServerLevelWrapper
private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>()); private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
private final ServerLevel level; private final ServerLevel level;
@Deprecated // TODO circular references are bad private IDhLevel dhLevel;
private IDhLevel parentDhLevel;
/**
* this name is cached to prevent issues during shutdown where
* the server variables needed may no longer be available.
*/
private final String KeyedLevelDimensionName;
@@ -95,7 +93,11 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}).get(); }).get();
} }
public ServerLevelWrapper(ServerLevel level) { this.level = level; } public ServerLevelWrapper(ServerLevel level)
{
this.level = level;
this.KeyedLevelDimensionName = this.createKeyedLevelDimensionName();
}
@@ -114,16 +116,60 @@ public class ServerLevelWrapper implements IServerLevelWrapper
} }
@Override @Override
public String getWorldFolderName() public String getKeyedLevelDimensionName() { return this.KeyedLevelDimensionName; }
private String createKeyedLevelDimensionName()
{ {
// Need specifically overworld since it's the only dimension that is stored in a server root folder String dimensionName = this.getDhIdentifier();
#if MC_VER >= MC_1_21_3 if (Config.Server.sendLevelKeys.get())
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString(); {
#else // <= 1.21.3 String levelKeyPrefix = Config.Server.levelKeyPrefix.get();
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
#endif if (SharedApi.getEnvironment() == EWorldEnvironment.CLIENT_SERVER)
{
String cleanWorldFolderName = this.getWorldFolderName()
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
.replaceAll(" ", "_");
levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName
+ "_" + this.getHashedSeedEncoded();
}
if (levelKeyPrefix.isEmpty())
{
levelKeyPrefix = this.getHashedSeedEncoded();
}
String mainPart = "@" + dimensionName;
return levelKeyPrefix.substring(0, Math.min(
LevelInitMessage.MAX_LENGTH - mainPart.length(),
levelKeyPrefix.length()
)) + mainPart;
}
return dimensionName;
} }
private String getWorldFolderName()
{
try
{
// We use the overworld since it's the only dimension that is stored in the server root folder
#if MC_VER >= MC_1_21_3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString();
#else // <= 1.21.3
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
#endif
}
catch (Exception e)
{
LOGGER.warn("Unable to get world folder name. LODs may not load or save correctly. Error: ["+e.getMessage()+"].", e);
return "unknown_world";
}
}
@Override @Override
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); } public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@@ -180,26 +226,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
return new ChunkWrapper(chunk, this); return new ChunkWrapper(chunk, this);
} }
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ)
{
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
ChunkSource source = this.level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos)
{
return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this);
}
@Override @Override
public ServerLevel getWrappedMcObject() { return this.level; } public ServerLevel getWrappedMcObject() { return this.level; }
@@ -208,28 +234,31 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override @Override
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; } public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
@Override
@Nullable
public IDhLevel getDhLevel() { return this.dhLevel; }
@Override @Override
public IDhApiCustomRenderRegister getRenderRegister() public IDhApiCustomRenderRegister getRenderRegister()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getGenericRenderer(); return this.dhLevel.getGenericRenderer();
} }
@Override @Override
public File getDhSaveFolder() public File getDhSaveFolder()
{ {
if (this.parentDhLevel == null) if (this.dhLevel == null)
{ {
return null; return null;
} }
return this.parentDhLevel.getSaveStructure().getSaveFolder(this); return this.dhLevel.getSaveStructure().getSaveFolder(this);
} }
@@ -0,0 +1,92 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import net.minecraft.world.level.ChunkPos;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class ChunkPosGenStream
{
public static Iterator<ChunkPos> getIterator(int genMinX, int genMinZ, int width, int extraRadius)
{ return getStream(genMinX, genMinZ, width, extraRadius).iterator(); }
/** @param extraRadius in both the positive and negative directions */
public static Stream<ChunkPos> getStream(int genMinX, int genMinZ, int width, int extraRadius)
{ return StreamSupport.stream(new InclusiveChunkPosIterator(genMinX, genMinZ, width, extraRadius), false); }
private static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
{
private final int minX;
private final int minZ;
private final int maxX;
private final int maxZ;
/** current X pos */
int x;
/** current Z pos */
private int z;
//=============//
// constructor //
//=============//
protected InclusiveChunkPosIterator(int genMinX, int genMinZ, int width, int extraRadius)
{
super(getCount(width, extraRadius), Spliterator.SIZED);
this.minX = genMinX - extraRadius;
this.minZ = genMinZ - extraRadius;
this.maxX = genMinX + (width - 1) + extraRadius;
this.maxZ = genMinZ + (width - 1) + extraRadius;
// X starts at 1 minus the minX so we can immediately re-add 1 in the tryAdvance() loop
this.x = this.minX - 1;
this.z = this.minZ;
}
private static int getCount(int width, int extraRadius)
{
int widthPlusExtra = width + (extraRadius * 2);
return widthPlusExtra * widthPlusExtra;
}
//=================//
// iterator method //
//=================//
@Override
public boolean tryAdvance(Consumer<? super ChunkPos> consumer)
{
if (this.x == this.maxX && this.z == this.maxZ)
{
// the last returned position was the final valid position
return false;
}
if (this.x == this.maxX)
{
// we reached the max X position, loop back around in the next Z row
this.x = this.minX;
this.z++;
}
else
{
this.x++;
}
consumer.accept(new ChunkPos(this.x, this.z));
return true;
}
}
}
@@ -19,114 +19,118 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration; package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer; import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.objects.EventTimer;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
public final class GenerationEvent public final class GenerationEvent
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();;
private static int generationFutureDebugIDs = 0;
private static final AtomicInteger DEBUG_ID_REF = new AtomicInteger(0);
/** can be used for troubleshooting */
public final int id; public final int id;
public final ThreadedParameters threadedParam;
public final ThreadWorldGenParams threadedParam;
public final DhChunkPos minPos; public final DhChunkPos minPos;
/** the number of chunks wide this event is */ public final int widthInChunks;
public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep; public final EDhApiWorldGenerationStep targetGenerationStep;
public final EDhApiDistantGeneratorMode generatorMode; public final EDhApiDistantGeneratorMode generatorMode;
public EventTimer timer = null; public final CompletableFuture<Void> future;
public long inQueueTime;
public long timeoutTime = -1;
public CompletableFuture<Void> future = null;
public final Consumer<IChunkWrapper> resultConsumer; public final Consumer<IChunkWrapper> resultConsumer;
public GenerationEvent( //=============//
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup, // constructor //
//=============//
private GenerationEvent(
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment generationGroup,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer) EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{ {
this.inQueueTime = System.nanoTime(); this.id = DEBUG_ID_REF.getAndIncrement();
this.id = generationFutureDebugIDs++;
this.minPos = minPos; this.minPos = minPos;
this.size = size; this.widthInChunks = widthInChunks;
this.generatorMode = generatorMode;
this.targetGenerationStep = targetGenerationStep; this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params); this.generatorMode = generatorMode;
this.threadedParam = ThreadWorldGenParams.getOrMake(generationGroup.globalParams);
this.future = new CompletableFuture<>();
this.resultConsumer = resultConsumer; this.resultConsumer = resultConsumer;
} }
public static GenerationEvent startEvent( //=======//
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment, // start //
//=======//
public static GenerationEvent start(
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment genEnvironment,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, generatorMode, target, resultConsumer); GenerationEvent genEvent = new GenerationEvent(minPos, widthInChunks, genEnvironment, generatorMode, target, resultConsumer);
generationEvent.future = CompletableFuture.supplyAsync(() ->
try
{ {
long runStartTime = System.nanoTime(); worldGeneratorThreadPool.execute(() ->
generationEvent.timeoutTime = runStartTime;
generationEvent.inQueueTime = runStartTime - generationEvent.inQueueTime;
generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{ {
genEnvironment.generateLodFromListAsync(generationEvent, (runnable) -> try
{ {
worldGeneratorThreadPool.execute(() -> BatchGenerationEnvironment.isDhWorldGenThreadRef.set(true);
if (genEvent.generatorMode == EDhApiDistantGeneratorMode.INTERNAL_SERVER)
{
genEnvironment.internalServerGenerator.generateChunksViaInternalServer(genEvent);
genEvent.future.complete(null);
}
else
{ {
boolean alreadyMarked = BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread();
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
}
try try
{ {
runnable.run(); genEnvironment.generateEvent(genEvent);
} }
catch (Throwable throwable) catch (Throwable throwable)
{ {
handleWorldGenThrowable(generationEvent, throwable); handleWorldGenThrowable(genEvent, throwable);
} }
finally finally
{ {
if (!alreadyMarked) genEvent.future.complete(null);
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(false);
}
} }
}); }
}); }
} catch (Throwable initialThrowable)
catch (Throwable initialThrowable) {
{ handleWorldGenThrowable(genEvent, initialThrowable);
handleWorldGenThrowable(generationEvent, initialThrowable); }
} finally
finally {
{ BatchGenerationEnvironment.isDhWorldGenThreadRef.remove();
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); }
} });
}
return null; catch (RejectedExecutionException e)
}, worldGeneratorThreadPool); {
return generationEvent; genEvent.future.completeExceptionally(e);
}
return genEvent;
} }
/** There's probably a better way to handle this, but it'll work for now */ /** There's probably a better way to handle this, but it'll work for now */
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable) private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
@@ -137,9 +141,8 @@ public final class GenerationEvent
throwable = throwable.getCause(); throwable = throwable.getCause();
} }
if (throwable instanceof InterruptedException boolean isShutdownException = ExceptionUtil.isShutdownException(throwable);
|| throwable instanceof UncheckedInterruptedException if (isShutdownException)
|| throwable instanceof RejectedExecutionException)
{ {
// these exceptions can be ignored, generally they just mean // these exceptions can be ignored, generally they just mean
// the thread is busy so it'll need to try again later. // the thread is busy so it'll need to try again later.
@@ -154,35 +157,15 @@ public final class GenerationEvent
} }
} }
public boolean isComplete() { return this.future.isDone(); }
public boolean hasTimeout(int duration, TimeUnit unit)
{
if (this.timeoutTime == -1)
{
return false;
}
long currentTime = System.nanoTime();
long delta = currentTime - this.timeoutTime;
return (delta > TimeUnit.NANOSECONDS.convert(duration, unit));
}
public boolean terminate() //================//
{ // base overrides //
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN======================="); //================//
ThreadPoolUtil.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
this.future.cancel(true);
return this.future.isCancelled();
}
public void refreshTimeout()
{
this.timeoutTime = System.nanoTime();
UncheckedInterruptedException.throwIfInterrupted();
}
@Override @Override
public String toString() { return this.id + ":" + this.size + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; } public String toString() { return this.id + ":" + this.widthInChunks + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
} }
@@ -0,0 +1,310 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.function.Function;
public class InternalServerGenerator
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD World Gen - Internal Server")
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
.build();
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Loading")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final IC2meAccessor C2ME_ACCESSOR = ModAccessorInjector.INSTANCE.get(IC2meAccessor.class);
/**
* Used to revert the ignore logic in {@link SharedApi} so
* that given chunk pos can be handled again.
* A timer is used so we don't have to inject into MC's code and it works sell enough
* most of the time.
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
*/
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
#if MC_VER < MC_1_21_5
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
#elif MC_VER < MC_1_21_9
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
#else
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
#endif
private static boolean c2meMissingWarningLogged = false;
private final GlobalWorldGenParams params;
private final IDhServerLevel dhServerLevel;
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
//=============//
// constructor //
//=============//
public InternalServerGenerator(GlobalWorldGenParams params, IDhServerLevel dhServerLevel)
{
this.params = params;
this.dhServerLevel = dhServerLevel;
}
//============//
// generation //
//============//
public void generateChunksViaInternalServer(GenerationEvent genEvent)
{
this.runValidation();
try
{
//=====================//
// create gen requests //
//=====================//
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
{
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
CompletableFuture<ChunkAccess> requestChunkFuture =
this.requestChunkFromServerAsync(chunkPos)
// log errors if necessary
.whenCompleteAsync(
(chunk, throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException)
{
actualThrowable = actualThrowable.getCause();
}
if (actualThrowable != null)
{
// ignore expected shutdown exceptions
boolean isShutdownException =
ExceptionUtil.isShutdownException(actualThrowable)
|| actualThrowable.getMessage().contains("Unloaded chunk");
if (!isShutdownException)
{
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
}
}
});
getChunkFutureList.add(requestChunkFuture);
}
}
//==============================//
// wait for generation requests //
//==============================//
// Join-ing each thread will prevent DH from working on anything else
// but will also prevent over-queuing world gen tasks.
// If C2ME is present the CPU will still be well utilized.
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
for (int i = 0; i < getChunkFutureList.size(); i++)
{
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
ChunkAccess chunk = getChunkFuture.join();
if (chunk != null)
{
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
chunkWrappers.add(chunkWrapper);
}
}
//==========================//
// process generated chunks //
//==========================//
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
for (int i = 0; i < chunkWrappers.size(); i++)
{
ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i);
// pre-generated chunks should have lighting but new ones won't
if (!chunkWrapper.isDhBlockLightingCorrect())
{
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, chunkWrappers, maxSkyLight);
}
this.dhServerLevel.updateBeaconBeamsForChunk(chunkWrapper, chunkWrappers);
genEvent.resultConsumer.accept(chunkWrapper);
}
}
finally
{
// release all chunks from the server to prevent out of memory issues
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
while (chunkPosIterator.hasNext())
{
ChunkPos chunkPos = chunkPosIterator.next();
this.releaseChunkFromServer(this.params.mcServerLevel, chunkPos);
}
}
}
private void runValidation()
{
// DH thread check
if (!DhApi.isDhThread()
&& ModInfo.IS_DEV_BUILD)
{
throw new IllegalStateException("Internal server generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
}
// C2ME present?
if (C2ME_ACCESSOR == null
&& !c2meMissingWarningLogged)
{
c2meMissingWarningLogged = true;
String c2meWarning = "C2ME missing, \n" +
"low CPU usage and slow world gen speeds expected. \n" +
"DH is set to use MC's internal server for world gen \n" +
"this mode is less efficient unless a mod like C2ME is present."
;
if (Config.Common.Logging.Warning.showSlowWorldGenSettingWarnings.get())
{
String message =
// orange text
"\u00A76" + "Distant Horizons: slow world gen." + "\u00A7r\n" +
c2meWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(c2meWarning);
}
}
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
{
return CompletableFuture.supplyAsync(() ->
{
ServerLevel level = this.params.mcServerLevel;
// ignore chunk update events for this position
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z));
#if MC_VER < MC_1_21_5
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
#else
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
#endif
// probably not the most optimal to run updates here, but fast enough
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap);
ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
if (chunkHolder == null)
{
throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added.");
}
#if MC_VER <= MC_1_20_4
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.left().orElseThrow(() -> new RuntimeException(result.right().get().toString()))); // can throw if the server is shutting down
#elif MC_VER <= MC_1_20_6
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.toString()))); // can throw if the server is shutting down
#else
return chunkHolder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down
#endif
}, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor)
.thenCompose(Function.identity());
}
/**
* mitigates out of memory issues in the vanilla chunk system. <br>
* See: https://github.com/pop4959/Chunky/pull/383
*/
private void releaseChunkFromServer(ServerLevel level, ChunkPos chunkPos)
{
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
{
try
{
#if MC_VER < MC_1_21_5
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
#else
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
#endif
level.getChunkSource().chunkMap.tick(() -> false);
#if MC_VER > MC_1_16_5
level.entityManager.tick();
#endif
// give MC a few seconds to save the chunk before
// we can process update events there again
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
{
@Override
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); }
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
}
catch (Exception e)
{
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e);
}
});
}
}
@@ -1,109 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 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.worldGeneration;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment.PerfCalculator;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
public final class ThreadedParameters
{
private static final ThreadLocal<ThreadedParameters> LOCAL_PARAM = new ThreadLocal<>();
final ServerLevel level;
public WorldGenStructFeatManager structFeat = null;
#if MC_VER >= MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
public final PerfCalculator perf = new PerfCalculator();
private static GlobalParameters previousGlobalParameters = null;
public static ThreadedParameters getOrMake(GlobalParameters param)
{
ThreadedParameters tParam = LOCAL_PARAM.get();
if (tParam != null && tParam.isValid && tParam.level == param.level)
{
return tParam;
}
tParam = new ThreadedParameters(param);
LOCAL_PARAM.set(tParam);
return tParam;
}
private ThreadedParameters(GlobalParameters param)
{
previousGlobalParameters = param;
this.level = param.level;
#if MC_VER < MC_1_18_2
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
#elif MC_VER < MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, param.randomState, level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
#endif
}
public void markAsInvalid() { isValid = false; }
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
{
#if MC_VER < MC_1_19_4
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if MC_VER >= MC_1_18_2 , structCheck #endif );
#else
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel, structCheck);
#endif
}
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalParameters != null)
{
this.structCheck = createStructureCheck(previousGlobalParameters);
}
}
private StructureCheck createStructureCheck(GlobalParameters param)
{
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.level.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.fixerUpper);
}
#else
public void recreateStructureCheck() { /* do nothing */ }
#endif
}
@@ -0,0 +1,744 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import com.mojang.serialization.Codec;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.core.Registry;
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
#if MC_VER < MC_1_21_9
import net.minecraft.world.level.block.Blocks;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.world.level.material.Fluids;
#endif
#if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER >= MC_1_21_1
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import net.minecraft.world.level.material.Fluid;
public class ChunkCompoundTagParser
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Reader")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
private static boolean lightingSectionErrorLogged = false;
//============//
// read chunk //
//============//
public static ChunkWrapper createFromTag(
WorldGenLevel mcWorldGenLevel, IDhServerLevel dhServerLevel,
ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
#endif
//=======================//
// validate the chunkPos //
//=======================//
int chunkX = CompoundTagUtil.getInt(tagLevel,"xPos");
int chunkZ = CompoundTagUtil.getInt(tagLevel, "zPos");
ChunkPos actualChunkPos = new ChunkPos(chunkX, chunkZ);
// confirm chunk pos is correct
if (!Objects.equals(chunkPos, actualChunkPos))
{
if (chunkX == 0 && chunkZ == 0)
{
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(true))
{
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
"This might happen if the world was created using an external program. \n" +
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
"If issues arise please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen." +
" ");
}
}
else
{
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \n" +
"Please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen. \n" +
"(Expected pos: ["+chunkPos.toString()+"], actual ["+actualChunkPos.toString()+"])" +
" ");
return null;
}
}
//===========//
// get ticks //
//===========//
#if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
mcWorldGenLevel.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 mcWorldGenLevel, #endif
chunkPos, mcWorldGenLevel.getLevel().getChunkSource().getGenerator().getBiomeSource(),
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
tagLevel.getList("ToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
#else
// ticks shouldn't be needed so ignore them for MC versions after 1.18.2
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
//=====================//
// get misc properties //
//=====================//
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else mcWorldGenLevel.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYCount];
boolean hasBlocks = readAndPopulateSections(mcWorldGenLevel, chunkPos, tagLevel, chunkSections);
if (!hasBlocks)
{
return null;
}
long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime");
boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn");
//============//
// make chunk //
//============//
#if MC_VER < MC_1_18_2
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks,
fluidTicks, inhabitedTime, chunkSections, null);
#else
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel, chunkPos, UpgradeData.EMPTY, blockTicks,
fluidTicks, inhabitedTime, chunkSections, null, null);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
boolean hasHeightmapData = readHeightmaps(chunk, chunkData);
// chunk wrapper so we can pass along extra data more easily
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper(), !hasHeightmapData);
//===========================//
// check if chunk has blocks //
//===========================//
// in some MC versions all the NBT data will be there
// but the chunk will be totally empty,
// usually this means the chunk was only partially generated.
// If that happens we should try to generate the chunk from scratch
// otherwise we can end up with large empty holes in the world.
// walking through the heightmap (recreated by DH if missing)
// is a fast way to check if there are any blocks in the chunk
boolean chunkHasBlocks = false;
int serverMinHeight = dhServerLevel.getServerLevelWrapper().getMinHeight();
for (int x = 0; x < 16 && !chunkHasBlocks; x++)
{
for (int z = 0; z < 16 && !chunkHasBlocks; z++)
{
int heightMap = Math.max(
// max between both heightmaps just in case there's a discrepancy
chunkWrapper.getLightBlockingHeightMapValue(x, z),
chunkWrapper.getSolidHeightMapValue(x, z)
);
if (heightMap != serverMinHeight)
{
chunkHasBlocks = true;
}
}
}
if (chunkHasBlocks)
{
return chunkWrapper;
}
else
{
// no blocks detected, this chunk should be generated from scratch
return null;
}
}
//=================//
// chunk sections //
// (Blocks/biomes) //
//=================//
/** handles both blocks and biomes */
private static boolean readAndPopulateSections(
LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData,
LevelChunkSection[] chunkSections)
{
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
ListTag tagSections = CompoundTagUtil.getListTag(chunkData, "Sections", 10);
// try lower-case "sections" if capital "Sections" is missing
if (tagSections == null
|| tagSections.isEmpty())
{
tagSections = CompoundTagUtil.getListTag(chunkData, "sections", 10);
}
boolean blocksFound = false;
if (tagSections != null)
{
for (int i = 0; i < tagSections.size(); ++i)
{
CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, i);
if (tagSection == null)
{
continue;
}
final int sectionYPos = CompoundTagUtil.getByte(tagSection, "Y");
//===================//
// get blocks/biomes //
//===================//
#if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9)
&& tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10), tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
{
int sectionIndex;
#if MC_VER < MC_1_17_1
sectionIndex = sectionYPos;
#else
sectionIndex = level.getSectionIndexFromSectionY(sectionYPos);
#endif
chunkSections[sectionIndex] = levelChunkSection;
}
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0
&& sectionId < chunkSections.length)
{
//========//
// blocks //
//========//
PalettedContainer<BlockState> blockStateContainer;
boolean containsBlockStates = CompoundTagUtil.contains(tagSection, "block_states", 10);
if (containsBlockStates)
{
Codec<PalettedContainer<BlockState>> blockStateCodec = getBlockStateCodec(level);
#if MC_VER < MC_1_20_6
blockStateContainer = blockStateCodec
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
blockStateContainer = blockStateCodec
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
blocksFound = true;
}
else
{
#if MC_VER < MC_1_21_9
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#else
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
#endif
}
//========//
// biomes //
//========//
Registry<Biome> biomeRegistry = getBiomeRegistry(level);
#if MC_VER < MC_1_19_2
Codec<PalettedContainer<Biome>> biomeCodec;
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec;
#endif
biomeCodec = getBiomeCodec(level, biomeRegistry);
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomeRegistry", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomeRegistry")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomeRegistry, biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
{
CompoundTag biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomeRegistry");
if (biomeTag == null)
{
biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomes");
}
if (biomeTag != null
&& !biomeTag.isEmpty())
{
#if MC_VER < MC_1_20_6
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomeRegistry.asHolderIdMap(),
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, biomeTag)
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYCount, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
// no biomes found, use the default (probably plains)
#if MC_VER < MC_1_21_3
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomeRegistry.asHolderIdMap(),
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VER < MC_1_21_9
biomeContainer = new PalettedContainer<Holder<Biome>>(biomeRegistry.asHolderIdMap(),
biomeRegistry.getOrThrow(Biomes.PLAINS),
PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = PalettedContainerFactory.create(level.registryAccess()).createForBiomes();
#endif
}
}
#endif
#if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
}
return blocksFound;
}
private static Codec<PalettedContainer<BlockState>> getBlockStateCodec(LevelAccessor level)
{
#if MC_VER < MC_1_18_2
return null; // unused for older MC versions
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif MC_VER <= MC_1_21_8
return PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#else
return PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
#endif
}
private static Registry<Biome> getBiomeRegistry(LevelAccessor level)
{
#if MC_VER < MC_1_18_2
// not needed
return null;
#elif MC_VER < MC_1_19_4
return level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#elif MC_VER < MC_1_21_3
return level.registryAccess().registryOrThrow(Registries.BIOME);
#else
return level.registryAccess().lookupOrThrow(Registries.BIOME);
#endif
}
private static
#if MC_VER < MC_1_19_2 Codec<PalettedContainer<Biome>>
#else Codec<PalettedContainer<Holder<Biome>>>
#endif
getBiomeCodec(LevelAccessor level, Registry<Biome> biomeRegistry)
{
#if MC_VER < MC_1_18_2
return null; // unused for older MC versions
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(
biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
return PalettedContainer.codec(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_3
return PalettedContainer.codecRW(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_9
return PalettedContainer.codecRW(
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
#else
return PalettedContainer.codecRW(
biomeRegistry.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomeRegistry.getOrThrow(Biomes.PLAINS));
#endif
}
//============//
// heightmaps //
//============//
private static boolean readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = CompoundTagUtil.getCompoundTag(chunkData, "Heightmaps");
if (tagHeightmaps == null)
{
return false;
}
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmapKey = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmapKey, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmapKey));
}
#else
if (tagHeightmaps.contains(heightmapKey))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmapKey);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
#endif
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
return true;
}
//================//
// chunk lighting //
//================//
/** source: https://minecraft.wiki/w/Chunk_format */
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
{
#if MC_VER <= MC_1_17_1
// MC 1.16 and 1.17 doesn't have the necessary NBT info
return null;
#else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
boolean foundSkyLight = false;
//===================//
// get NBT tags info //
//===================//
Tag chunkSectionTags = chunkData.get("sections");
if (chunkSectionTags == null)
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
}
return null;
}
else if (!(chunkSectionTags instanceof ListTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
}
return null;
}
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
//===================//
// get lighting info //
//===================//
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
byte[] blockLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "BlockLight");
byte[] skyLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "SkyLight");
if (blockLightNibbleArray != null
&& skyLightNibbleArray != null)
{
// if any sky light was found then all lights above will be max brightness
if (skyLightNibbleArray.length != 0)
{
foundSkyLight = true;
}
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{
// chunk sections are also 16 blocks tall
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
{
int blockPosIndex = relY*16*16 + relZ*16 + relX;
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
if (skyLightNibbleArray.length == 0 && foundSkyLight)
{
skyLight = LodUtil.MAX_MC_LIGHT;
}
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
}
}
}
}
}
return combinedStorage;
#endif
}
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
private static byte getNibbleAtIndex(byte[] arr, int index)
{
if (index % 2 == 0)
{
return (byte)(arr[index/2] & 0x0F);
}
else
{
return (byte)((arr[index/2]>>4) & 0x0F);
}
}
//=========//
// logging //
//=========//
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
}
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e)
{
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
e);
return newMessage;
});
}
private static RuntimeException logErrorAndReturnException(String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
return null; //new RuntimeException(message);
}
//================//
// helper classes //
//================//
public static class CombinedChunkLightStorage
{
public ChunkLightStorage blockLightStorage;
public ChunkLightStorage skyLightStorage;
public CombinedChunkLightStorage(int minY, int maxY)
{
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
}
}
}
@@ -0,0 +1,343 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.RegionFileStorageExternalCache;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.UpgradeData;
import net.minecraft.world.level.chunk.storage.IOWorker;
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
#if MC_VER <= MC_1_17_1
import net.minecraft.world.level.chunk.ChunkStatus;
#elif MC_VER <= MC_1_19_2
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.core.Registry;
#elif MC_VER <= MC_1_19_4
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.ChunkStatus;
#elif MC_VER <= MC_1_20_6
import net.minecraft.core.registries.Registries;
#elif MC_VER <= MC_1_21_3
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#elif MC_VER <= MC_1_21_8
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#elif MC_VER <= MC_1_21_9
import net.minecraft.world.level.chunk.PalettedContainerFactory;
#else
import net.minecraft.world.level.chunk.PalettedContainerFactory;
#endif
public class ChunkFileReader implements AutoCloseable
{
public static final DhLogger LOGGER = new DhLoggerBuilder()
.name("LOD World Gen")
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
.build();
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
.name("LOD Chunk Loading")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
public final GlobalWorldGenParams params;
/**
* will be true if C2ME is installed (since they require us to
* pull chunks using their async method), or if there
* was an issue with the sync pulling method.
*/
private boolean pullExistingChunkUsingMcAsyncMethod = false;
private final AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
{
RegionFileStorageExternalCache cache = this.regionFileStorageCacheRef.get();
if (cache == null)
{
cache = new RegionFileStorageExternalCache(storage);
if (!this.regionFileStorageCacheRef.compareAndSet(null, cache))
{
cache = this.regionFileStorageCacheRef.get();
}
}
return cache;
}
//=============//
// constructor //
//=============//
public ChunkFileReader(GlobalWorldGenParams params)
{
this.params = params;
if (MOD_CHECKER.isModLoaded("c2me"))
{
LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
this.pullExistingChunkUsingMcAsyncMethod = true;
}
}
//=============//
// constructor //
//=============//
/**
* If the given chunk pos already exists in the world, that chunk will be returned,
* otherwise this will return an empty chunk.
*/
public CompletableFuture<ChunkWrapper> createEmptyOrPreExistingChunkWrapperAsync(
int chunkX, int chunkZ,
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
Map<DhChunkPos, ChunkWrapper> generatedChunkWrapperByDhPos)
{
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ);
if (generatedChunkWrapperByDhPos.containsKey(dhChunkPos))
{
return CompletableFuture.completedFuture(generatedChunkWrapperByDhPos.get(dhChunkPos));
}
return this.getChunkNbtDataAsync(chunkPos)
.thenApply((CompoundTag chunkData) ->
{
ChunkWrapper newChunkWrapper = this.loadOrMakeChunkWrapper(chunkPos, chunkData);
// attempt to get chunk lighting
ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunkWrapper.getChunk(), chunkData);
if (combinedLights != null)
{
// may be empty, empty checks are handled later
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
return newChunkWrapper;
})
// separate handle so we can cleanly handle missing chunks and/or thrown errors
.handle((ChunkWrapper newChunkWrapper, Throwable throwable) ->
{
if (newChunkWrapper != null)
{
return newChunkWrapper;
}
else
{
return this.CreateProtoChunkWrapper(this.params.mcServerLevel, chunkPos);
}
})
.thenApply((ChunkWrapper newChunkWrapper) ->
{
generatedChunkWrapperByDhPos.put(dhChunkPos, newChunkWrapper);
return newChunkWrapper;
});
}
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
{
ServerLevel level = this.params.mcServerLevel;
try
{
IOWorker ioWorker = level.getChunkSource().chunkMap.worker;
#if MC_VER <= MC_1_18_2
return CompletableFuture.completedFuture(ioWorker.load(chunkPos));
#else
// storage will be null if C2ME is installed
if (!this.pullExistingChunkUsingMcAsyncMethod
&& ioWorker.storage != null)
{
try
{
RegionFileStorage storage = this.params.mcServerLevel.getChunkSource().chunkMap.worker.storage;
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
return CompletableFuture.completedFuture(cache.read(chunkPos));
}
catch (NullPointerException e)
{
// this shouldn't happen, if anything is null it should be
// ioWorker.storage
// but just in case
LOGGER.error("Unexpected issue pulling pre-existing chunk ["+chunkPos+"], falling back to async chunk pulling. This may cause server-tick lag.", e);
this.pullExistingChunkUsingMcAsyncMethod = true;
// try again now using the async method
return this.getChunkNbtDataAsync(chunkPos);
}
}
else
{
// log if we unexpectedly weren't able to run the sync chunk pulling
if (!this.pullExistingChunkUsingMcAsyncMethod)
{
// this shouldn't happen, but just in case
LOGGER.info("Unable to pull pre-existing chunk using synchronous method. Falling back to async method. this may cause server-tick lag.");
this.pullExistingChunkUsingMcAsyncMethod = true;
}
//GET_CHUNK_COUNT_REF.incrementAndGet();
// When running in vanilla MC on versions before 1.21.4,
// DH would attempt to run loadAsync on this same thread via a threading mixin,
// to prevent causing lag on the server thread.
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
return ioWorker.loadAsync(chunkPos)
.thenApply(optional ->
{
// Debugging note:
// If there are reports of extreme memory use when C2ME is installed, that probably means
// this method is queuing a lot of tasks (1,000+), which causes C2ME to explode.
//GET_CHUNK_COUNT_REF.decrementAndGet();
//PREF_LOGGER.info("chunk getter count ["+F3Screen.NUMBER_FORMAT.format(GET_CHUNK_COUNT_REF.get())+"]");
return optional.orElse(null);
})
.exceptionally((throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException completionException)
{
actualThrowable = completionException.getCause();
}
boolean isShutdownException = ExceptionUtil.isShutdownException(actualThrowable);
if (!isShutdownException)
{
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
}
return null;
});
}
#endif
}
catch (ClosedByInterruptException ignore)
{
// this just means the world generator is being shut down
return CompletableFuture.completedFuture(null);
}
catch (Exception e)
{
CHUNK_LOAD_LOGGER.warn("Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
return CompletableFuture.completedFuture(null);
}
}
private ChunkWrapper loadOrMakeChunkWrapper(ChunkPos chunkPos, CompoundTag chunkTagData)
{
ServerLevel mcServerLevel = this.params.mcServerLevel;
if (chunkTagData == null)
{
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
else
{
try
{
ChunkWrapper chunkWrapper = ChunkCompoundTagParser.createFromTag(mcServerLevel, this.params.dhServerLevel, chunkPos, chunkTagData);
if (chunkWrapper == null)
{
chunkWrapper = this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
return chunkWrapper;
}
catch (Exception e)
{
CHUNK_LOAD_LOGGER.error(
"DistantHorizons: couldn't load or make chunk at [" + chunkPos + "]." +
"Please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen.\n" +
"Error: [" + e.getMessage() + "]."
, e);
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
}
}
}
public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos)
{
ProtoChunk chunk = CreateProtoChunk(level, chunkPos);
return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper(), false);
}
public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos)
{
#if MC_VER <= MC_1_16_5
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
#elif MC_VER <= MC_1_17_1
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level);
#elif MC_VER <= MC_1_19_2
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null);
#elif MC_VER <= MC_1_19_4
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
#elif MC_VER < MC_1_21_3
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
#elif MC_VER < MC_1_21_9
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().lookupOrThrow(Registries.BIOME), null);
#else
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, PalettedContainerFactory.create(level.registryAccess()), null);
#endif
}
//================//
// base overrides //
//================//
@Override
public void close()
{
RegionFileStorageExternalCache regionStorage = this.regionFileStorageCacheRef.get();
if (regionStorage != null)
{
try
{
regionStorage.close();
}
catch (ClosedChannelException ignore) { /* world generator is being shut down */ }
catch (IOException e)
{
LOGGER.error("Failed to close region file storage cache, error: ["+e.getMessage()+"].", e);
}
}
}
}
@@ -0,0 +1,148 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import org.jetbrains.annotations.Nullable;
/**
* these tag helpers are usedd to simplify tag accessing between MC versions
*/
public class CompoundTagUtil
{
/** defaults to "false" if the tag isn't present */
public static boolean getBoolean(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getBoolean(key);
#else
return tag.getBoolean(key).orElse(false);
#endif
}
/** defaults to "0" if the tag isn't present */
public static byte getByte(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByte(key);
#else
return tag.getByte(key).orElse((byte)0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static short getShort(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getShort(index);
#else
return tag.getShort(index).orElse((short)0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static int getInt(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getInt(key).orElse(0);
#endif
}
/** defaults to "0" if the tag isn't present */
public static long getLong(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getLong(key).orElse(0L);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static String getString(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getString(key);
#else
return tag.getString(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static byte[] getByteArray(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByteArray(key);
#else
return tag.getByteArray(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static CompoundTag getCompoundTag(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(key);
#else
return tag.getCompound(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static CompoundTag getCompoundTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(index);
#else
return tag.getCompound(index).orElse(null);
#endif
}
/**
* defaults to null if the tag isn't present
* @param elementType unused after MC 1.21.5
*/
@Nullable
public static ListTag getListTag(CompoundTag tag, String key, int elementType)
{
#if MC_VER < MC_1_21_5
return tag.getList(key, elementType);
#else
return tag.getList(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
public static ListTag getListTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getList(index);
#else
return tag.getList(index).orElse(null);
#endif
}
public static boolean contains(CompoundTag tag, String key, int index)
{
#if MC_VER < MC_1_21_5
return tag.contains(key, index);
#else
return tag.contains(key);
#endif
}
}
@@ -1,875 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry;
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
#endif
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
#endif
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
import net.minecraft.world.level.material.Fluids;
#endif
#if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER >= MC_1_21_1
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#endif
import net.minecraft.world.level.material.Fluid;
import org.jetbrains.annotations.Nullable;
public class ChunkFileReader
{
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
#if MC_VER >= MC_1_21_9
// BLOCK_STATE_CODEC can no longer be statically created since
// it needs a level reference
#elif MC_VER >= MC_1_19_2
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#elif MC_VER >= MC_1_18_2
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
#endif
private static final String TAG_UPGRADE_DATA = "UpgradeData";
private static final String BLOCK_TICKS_TAG_18 = "block_ticks";
private static final String FLUID_TICKS_TAG_18 = "fluid_ticks";
private static final String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
private static boolean lightingSectionErrorLogged = false;
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
//============//
// read chunk //
//============//
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_18_2
CompoundTag tagLevel = chunkData.getCompound("Level");
#else
CompoundTag tagLevel = chunkData;
#endif
int chunkX = tagGetInt(tagLevel,"xPos");
int chunkZ = tagGetInt(tagLevel, "zPos");
ChunkPos actualPos = new ChunkPos(chunkX, chunkZ);
if (!Objects.equals(chunkPos, actualPos))
{
if (chunkX == 0 && chunkZ == 0)
{
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(true))
{
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
"This might happen if the world was created using an external program. \n" +
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
"If issues arise please try optimizing your world to fix this issue. \n" +
"World optimization can be done from the singleplayer world selection screen."+
"");
}
}
else
{
// everything is on one line to fix a JDK 17 compiler issue
// if the issue is ever resolved, feel free to make this multi-line for readability
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \nPlease try optimizing your world to fix this issue. \nWorld optimization can be done from the singleplayer world selection screen. \n(Expected pos: ["+chunkPos.toString()+"], actual ["+actualPos.toString()+"])");
return null;
}
}
#if MC_VER < MC_1_20_6
ChunkStatus.ChunkType chunkType;
#else
ChunkType chunkType;
#endif
chunkType = readChunkType(tagLevel);
#if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#elif MC_VER < MC_1_21_6
BlendingData blendingData = readBlendingData(tagLevel);
#if MC_VER < MC_1_19_2
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
return null;
#else
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
return null;
#endif
#else
// ignore blending data, there appears to be an issue with parsing it in 1.21.6
BlendingData blendingData = null;
if (chunkType == ChunkType.PROTOCHUNK)
{
return null;
}
#endif
long inhabitedTime = tagGetLong(tagLevel, "InhabitedTime");
//================== Read params for making the LevelChunk ==================
UpgradeData upgradeData = UpgradeData.EMPTY;
// commented out 2025-06-04 as a test to see if the upgrade data
// is actually necessary for DH or if it can be ignored
// (if it can't be ignored we'll need to handle null responses from tagGetCompoundTag())
//
//#if MC_VER < MC_1_17_1
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA))
// : UpgradeData.EMPTY;
//#elif MC_VER < MC_1_21_5
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#else
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#endif
boolean isLightOn = tagGetBoolean(tagLevel, "isLightOn");
#if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif ,
chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(),
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
tagLevel.getList("ToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
#else
#if MC_VER < MC_1_19_4
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#elif MC_VER < MC_1_21_4
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
(string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#else
// do we need the ticks for what we're doing?
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
#endif
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
// ====================== Make the chunk =========================
#if MC_VER < MC_1_18_2
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null);
#else
LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
#endif
// Set some states after object creation
chunk.setLightCorrect(isLightOn);
readHeightmaps(chunk, chunkData);
//readPostPocessings(chunk, chunkData);
return chunk;
}
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_21_9
// BLOCK_STATE_CODEC is created statically
// TODO clean up this code separation
#else
final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
#endif
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#elif MC_VER < MC_1_21_3
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#else
Registry<Biome> biomes = level.registryAccess().lookupOrThrow(Registries.BIOME);
#endif
#if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_3
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_9
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomes.getOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
ListTag tagSections = tagGetListTag(chunkData, "Sections", 10);
if (tagSections == null || tagSections.isEmpty())
{
tagSections = tagGetListTag(chunkData, "sections", 10);
}
if (tagSections != null)
{
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagGetCompoundTag(tagSections, j);
if (tagSection == null)
{
continue;
}
final int sectionYPos = tagGetByte(tagSection, "Y");
#if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
{
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
tagSection.getLongArray("BlockStates"));
levelChunkSection.recalcBlockCounts();
if (!levelChunkSection.isEmpty())
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
= levelChunkSection;
}
#else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length)
{
PalettedContainer<BlockState> blockStateContainer;
#if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer;
#else
PalettedContainer<Holder<Biome>> biomeContainer;
#endif
boolean containsBlockStates;
#if MC_VER < MC_1_21_5
containsBlockStates = tagSection.contains("block_states", 10);
#else
containsBlockStates = tagSection.contains("block_states");
#endif
if (containsBlockStates)
{
#if MC_VER < MC_1_20_6
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
#if MC_VER < MC_1_21_9
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#else
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
#endif
}
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
boolean containsBiomes;
#if MC_VER < MC_1_21_5
containsBiomes = tagSection.contains("biomes", 10);
#else
containsBiomes = tagSection.contains("biomes");
#endif
if (containsBiomes)
{
#if MC_VER < MC_1_20_6
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
#if MC_VER < MC_1_21_3
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomes.asHolderIdMap(),
biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VER < MC_1_21_9
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
biomes.getOrThrow(Biomes.PLAINS),
PalettedContainer.Strategy.SECTION_BIOMES);
#else
biomeContainer = PalettedContainerFactory.create(level.registryAccess()).createForBiomes();
#endif
}
#endif
#if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif
}
#endif
}
}
return chunkSections;
}
private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21_1 ChunkType
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
String statusString = tagGetString(tagLevel,"Status");
if (statusString != null)
{
ChunkStatus chunkStatus = ChunkStatus.byName(statusString);
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
}
#if MC_VER <= MC_1_20_4
return ChunkStatus.ChunkType.PROTOCHUNK;
#else
return ChunkType.PROTOCHUNK;
#endif
}
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{
CompoundTag tagHeightmaps = tagGetCompoundTag(chunkData, "Heightmaps");
if (tagHeightmaps != null)
{
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
String heightmap = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmap, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
#else
if (tagHeightmaps.contains(heightmap))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmap);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
#endif
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
}
// commented out as a test as of 2025-06-04 to see if this is actually necessary for DH
// DH probably doesn't need any chunk post-processing data
//private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
//{
// ListTag tagPostProcessings = tagGetListTag(chunkData,"PostProcessing", 9);
// if (tagPostProcessings != null)
// {
// for (int i = 0; i < tagPostProcessings.size(); ++i)
// {
// ListTag listTag3 = tagGetListTag(tagPostProcessings, i);
// for (int j = 0; j < listTag3.size(); ++j)
// {
// #if MC_VER < MC_1_21_3
// chunk.addPackedPostProcess(listTag3.getShort(j), i);
// #else
// chunk.addPackedPostProcess(ShortList.of(tagGetShort(listTag3, j)), i);
// #endif
// }
// }
// }
//}
#if MC_VER >= MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
boolean containsBlendingData;
#if MC_VER < MC_1_21_5
containsBlendingData = chunkData.contains("blending_data", 10);
#else
containsBlendingData = chunkData.contains("blending_data");
#endif
if (containsBlendingData)
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
try
{
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null);
#else
// blending data appears to have changed as of 1.21.6 causing a class cast exception here due to it being wrapped in a Java.Optional
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null));
#endif
}
catch (Exception e)
{
String message = e.getMessage();
if (message == null || message.trim().isEmpty())
{
message = "Failed to parse blending data";
}
logParsingWarningOnce(message, e);
}
}
return blendingData;
}
#endif
//=====================//
// read chunk lighting //
//=====================//
/**
* https://minecraft.wiki/w/Chunk_format
*/
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
{
#if MC_VER <= MC_1_17_1
// MC 1.16 and 1.17 doesn't have the necessary NBT info
return null;
#else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
boolean foundSkyLight = false;
//===================//
// get NBT tags info //
//===================//
Tag chunkSectionTags = chunkData.get("sections");
if (chunkSectionTags == null)
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
}
return null;
}
else if (!(chunkSectionTags instanceof ListTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
}
return null;
}
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
//===================//
// get lighting info //
//===================//
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag))
{
if (!lightingSectionErrorLogged)
{
lightingSectionErrorLogged = true;
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
byte[] blockLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "BlockLight");
byte[] skyLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "SkyLight");
if (blockLightNibbleArray != null
&& skyLightNibbleArray != null)
{
// if any sky light was found then all lights above will be max brightness
if (skyLightNibbleArray.length != 0)
{
foundSkyLight = true;
}
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{
// chunk sections are also 16 blocks tall
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
{
int blockPosIndex = relY*16*16 + relZ*16 + relX;
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
if (skyLightNibbleArray.length == 0 && foundSkyLight)
{
skyLight = LodUtil.MAX_MC_LIGHT;
}
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
}
}
}
}
}
return combinedStorage;
#endif
}
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
private static byte getNibbleAtIndex(byte[] arr, int index)
{
if (index % 2 == 0)
{
return (byte)(arr[index/2] & 0x0F);
}
else
{
return (byte)((arr[index/2]>>4) & 0x0F);
}
}
//=========//
// logging //
//=========//
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
}
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
}
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e)
{
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
e);
return newMessage;
});
}
private static RuntimeException logErrorAndReturnException(String message)
{
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
return newMessage;
});
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
return null; //new RuntimeException(message);
}
//====================//
// tag helper methods //
//====================//
// TODO move into separate file (this file is getting long)
// these tag helpers are to simplify tag accessing between MC versions
/** defaults to "false" if the tag isn't present */
private static boolean tagGetBoolean(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getBoolean(key);
#else
return tag.getBoolean(key).orElse(false);
#endif
}
/** defaults to "0" if the tag isn't present */
private static byte tagGetByte(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByte(key);
#else
return tag.getByte(key).orElse((byte)0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static short tagGetShort(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getShort(index);
#else
return tag.getShort(index).orElse((short)0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static int tagGetInt(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getInt(key).orElse(0);
#endif
}
/** defaults to "0" if the tag isn't present */
private static long tagGetLong(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getInt(key);
#else
return tag.getLong(key).orElse(0L);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static String tagGetString(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getString(key);
#else
return tag.getString(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static byte[] tagGetByteArray(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getByteArray(key);
#else
return tag.getByteArray(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static CompoundTag tagGetCompoundTag(CompoundTag tag, String key)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(key);
#else
return tag.getCompound(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static CompoundTag tagGetCompoundTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getCompound(index);
#else
return tag.getCompound(index).orElse(null);
#endif
}
/**
* defaults to null if the tag isn't present
* @param elementType unused after MC 1.21.5
*/
@Nullable
private static ListTag tagGetListTag(CompoundTag tag, String key, int elementType)
{
#if MC_VER < MC_1_21_5
return tag.getList(key, elementType);
#else
return tag.getList(key).orElse(null);
#endif
}
/** defaults to null if the tag isn't present */
@Nullable
private static ListTag tagGetListTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getList(index);
#else
return tag.getList(index).orElse(null);
#endif
}
//================//
// helper classes //
//================//
public static class CombinedChunkLightStorage
{
public ChunkLightStorage blockLightStorage;
public ChunkLightStorage skyLightStorage;
public CombinedChunkLightStorage(int minY, int maxY)
{
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
}
}
}
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SpawnerBlock; import net.minecraft.world.level.block.SpawnerBlock;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -78,7 +78,7 @@ import net.minecraft.world.ticks.LevelTickAccess;
public class DhLitWorldGenRegion extends WorldGenRegion public class DhLitWorldGenRegion extends WorldGenRegion
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static ChunkStatus debugTriggeredForStatus = null; private static ChunkStatus debugTriggeredForStatus = null;
@@ -352,7 +352,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull); ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
if (chunk instanceof LevelChunk) if (chunk instanceof LevelChunk)
{ {
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 , true #endif ); chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 ,/* allow writes */ false #endif );
} }
return chunk; return chunk;
} }
@@ -392,10 +392,11 @@ public class DhLitWorldGenRegion extends WorldGenRegion
} }
} }
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) if (chunkStatus != ChunkStatus.EMPTY
&& chunkStatus != debugTriggeredForStatus)
{ {
LOGGER.info("WorldGen requiring " + chunkStatus LOGGER.info("WorldGen requiring [" + chunkStatus + "]"
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works."); + " is outside the expected range. Returning EMPTY chunk.");
debugTriggeredForStatus = chunkStatus; debugTriggeredForStatus = chunkStatus;
} }
@@ -1,13 +1,13 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile; import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileStorage; import net.minecraft.world.level.chunk.storage.RegionFileStorage;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.io.DataInputStream; import java.io.DataInputStream;
@@ -29,7 +29,7 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
*/ */
public class RegionFileStorageExternalCache implements AutoCloseable public class RegionFileStorageExternalCache implements AutoCloseable
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** Can be null due to the C2ME mod */ /** Can be null due to the C2ME mod */
@Nullable @Nullable
@@ -55,7 +55,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; } public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
@Nullable @Nullable
public RegionFile getRegionFile(ChunkPos pos) throws IOException public RegionFile getRegionFile(ChunkPos chunkPos) throws IOException
{ {
if (this.storage == null) if (this.storage == null)
{ {
@@ -70,8 +70,8 @@ public class RegionFileStorageExternalCache implements AutoCloseable
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()); long chunkPosLong = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ());
RegionFile rFile = null; RegionFile regionFile = null;
// Check vanilla cache // Check vanilla cache
int retryCount = 0; int retryCount = 0;
@@ -85,7 +85,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
this.getRegionFileLock.lock(); this.getRegionFileLock.lock();
#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
rFile = this.storage.getRegionFile(pos); regionFile = this.storage.getRegionFile(chunkPos);
// keeping the region cache size low helps prevent concurrency issues // keeping the region cache size low helps prevent concurrency issues
if (this.storage.regionCache.size() > 150) // max 256 if (this.storage.regionCache.size() > 150) // max 256
@@ -97,7 +97,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
} }
} }
#else #else
rFile = this.storage.regionCache.getOrDefault(posLong, null); regionFile = this.storage.regionCache.getOrDefault(chunkPosLong, null);
#endif #endif
break; break;
@@ -139,19 +139,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
if (retryCount >= maxRetryCount) if (retryCount >= maxRetryCount)
{ {
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "]."); ChunkFileReader.CHUNK_LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + chunkPos + "].");
} }
if (rFile != null) if (regionFile != null)
{ {
return rFile; return regionFile;
} }
// Then check our custom cache // Then check our custom cache
for (RegionFileCache cache : this.regionFileCache) for (RegionFileCache cache : this.regionFileCache)
{ {
if (cache.pos == posLong) if (cache.pos == chunkPosLong)
{ {
return cache.file; return cache.file;
} }
@@ -170,22 +170,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
return null; return null;
} }
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca"); Path regionFilePath = storageFolderPath.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca");
#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
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false); regionFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
#elif MC_VER <= MC_1_20_4 #elif MC_VER <= MC_1_20_4
rFile = new RegionFile(regionFilePath, storageFolderPath, false); regionFile = new RegionFile(regionFilePath, storageFolderPath, false);
#else #else
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false); regionFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
#endif #endif
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile)); this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()), regionFile));
while (this.regionFileCache.size() > MAX_CACHE_SIZE) while (this.regionFileCache.size() > MAX_CACHE_SIZE)
{ {
this.regionFileCache.poll().file.close(); this.regionFileCache.poll().file.close();
} }
return rFile; return regionFile;
} }
@@ -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.worldGeneration; package com.seibel.distanthorizons.common.wrappers.worldGeneration.params;
import com.mojang.datafixers.DataFixer; import com.mojang.datafixers.DataFixer;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
@@ -27,35 +27,50 @@ import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess; import net.minecraft.core.RegistryAccess;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ThreadedLevelLightEngine;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager; import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.chunk.ChunkGenerator;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.chunk.storage.ChunkScanAccess; import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
#endif #endif
import net.minecraft.world.level.levelgen.WorldGenSettings;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#elif MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
#else #else
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.RandomState;
#if MC_VER >= MC_1_19_4 import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
import net.minecraft.world.level.levelgen.WorldOptions;
import net.minecraft.core.registries.Registries;
#endif
#endif #endif
import net.minecraft.world.level.storage.WorldData; import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters #if MC_VER < MC_1_19_4
#elif MC_VER < MC_1_21_3
import net.minecraft.core.registries.Registries;
#else
import net.minecraft.core.registries.Registries;
#endif
#if MC_VER < MC_1_19_4
import net.minecraft.world.level.levelgen.WorldGenSettings;
#else
import net.minecraft.world.level.levelgen.WorldOptions;
#endif
/**
* Handles parameters that are relevant for the entire MC world.
*
* @see ThreadWorldGenParams
*/
public final class GlobalWorldGenParams
{ {
public final ChunkGenerator generator; public final ChunkGenerator generator;
public final IDhServerLevel lodLevel; public final IDhServerLevel dhServerLevel;
public final ServerLevel level; public final ServerLevel mcServerLevel;
public final Registry<Biome> biomes; public final Registry<Biome> biomes;
public final RegistryAccess registry; public final RegistryAccess registry;
public final long worldSeed; public final long worldSeed;
public final DataFixer fixerUpper; public final DataFixer dataFixer;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
public final StructureManager structures; public final StructureManager structures;
@@ -72,18 +87,24 @@ public final class GlobalParameters
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
public final BiomeManager biomeManager; public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed public final ChunkScanAccess chunkScanner;
#endif #endif
public GlobalParameters(IDhServerLevel lodLevel)
//=============//
// constructor //
//=============//
public GlobalWorldGenParams(IDhServerLevel dhServerLevel)
{ {
this.lodLevel = lodLevel; this.dhServerLevel = dhServerLevel;
this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject(); this.mcServerLevel = ((ServerLevelWrapper) dhServerLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = this.level.getServer(); MinecraftServer server = this.mcServerLevel.getServer();
WorldData worldData = server.getWorldData(); WorldData worldData = server.getWorldData();
this.registry = server.registryAccess(); this.registry = server.registryAccess();
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
this.worldGenSettings = worldData.worldGenSettings(); this.worldGenSettings = worldData.worldGenSettings();
this.biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY); this.biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
@@ -99,15 +120,19 @@ public final class GlobalParameters
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed)); this.biomeManager = new BiomeManager(this.mcServerLevel, BiomeManager.obfuscateSeed(this.worldSeed));
this.chunkScanner = this.level.getChunkSource().chunkScanner(); this.chunkScanner = this.mcServerLevel.getChunkSource().chunkScanner();
#endif #endif
this.structures = server.getStructureManager(); this.structures = server.getStructureManager();
this.generator = this.level.getChunkSource().getGenerator(); this.generator = this.mcServerLevel.getChunkSource().getGenerator();
this.fixerUpper = server.getFixerUpper(); this.dataFixer = server.getFixerUpper();
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
this.randomState = this.level.getChunkSource().randomState(); this.randomState = this.mcServerLevel.getChunkSource().randomState();
#endif #endif
} }
} }
@@ -0,0 +1,124 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 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.worldGeneration.params;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.WorldGenLevel;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.structure.StructureCheck;
#endif
public final class ThreadWorldGenParams
{
private static final ThreadLocal<ThreadWorldGenParams> LOCAL_PARAM_REF = new ThreadLocal<>();
final ServerLevel level;
public WorldGenStructFeatManager structFeatManager = null;
#if MC_VER >= MC_1_18_2
public StructureCheck structCheck;
#endif
boolean isValid = true;
// used for some older MC versions
private static GlobalWorldGenParams previousGlobalWorldGenParams = null;
//=============//
// constructor //
//=============//
public static ThreadWorldGenParams getOrMake(GlobalWorldGenParams globalParams)
{
ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get();
if (threadParam != null
&& threadParam.isValid
&& threadParam.level == globalParams.mcServerLevel)
{
return threadParam;
}
threadParam = new ThreadWorldGenParams(globalParams);
LOCAL_PARAM_REF.set(threadParam);
return threadParam;
}
private ThreadWorldGenParams(GlobalWorldGenParams param)
{
previousGlobalWorldGenParams = param;
this.level = param.mcServerLevel;
#if MC_VER < MC_1_18_2
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, this.level);
#elif MC_VER < MC_1_19_2
this.structCheck = this.createStructureCheck(param);
#else
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.mcServerLevel.dimension(), param.generator, param.randomState, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.dataFixer);
#endif
}
//==========//
// builders //
//==========//
public void makeStructFeatManager(WorldGenLevel genLevel, GlobalWorldGenParams param)
{
#if MC_VER < MC_1_18_2
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel);
#elif MC_VER < MC_1_19_4
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel, this.structCheck);
#else
this.structFeatManager = new WorldGenStructFeatManager(param.worldOptions, genLevel, this.structCheck);
#endif
}
#if MC_VER < MC_1_18_2
#elif MC_VER < MC_1_19_2
public void recreateStructureCheck()
{
if (previousGlobalWorldGenParams != null)
{
this.structCheck = this.createStructureCheck(previousGlobalWorldGenParams);
}
}
private StructureCheck createStructureCheck(GlobalWorldGenParams param)
{
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
param.mcServerLevel.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
param.dataFixer);
}
#else
public void recreateStructureCheck() { /* do nothing */ }
#endif
}
@@ -1,7 +1,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
@@ -19,7 +19,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
public abstract class AbstractWorldGenStep public abstract class AbstractWorldGenStep
{ {
public abstract void generateGroup( public abstract void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers); ArrayGridList<ChunkWrapper> chunkWrappers);
public abstract ChunkStatus getChunkStatus(); public abstract ChunkStatus getChunkStatus();
@@ -27,26 +27,26 @@ public abstract class AbstractWorldGenStep
/** @return the list of chunks that have an earlier status and can be generated */ /** @return the list of chunks that have an earlier status and can be generated */
protected ArrayList<ChunkAccess> getChunksToGenerate(List<ChunkWrapper> chunkWrappers) protected ArrayList<ChunkWrapper> getChunkWrappersToGenerate(List<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToGenerate = new ArrayList<>(); ArrayList<ChunkWrapper> chunkWrappersToGenerate = new ArrayList<>(chunkWrappers.size());
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(this.getChunkStatus())) if (chunkWrapper.getStatus().isOrAfter(this.getChunkStatus()))
{ {
// this chunk has already generated this step // this chunk has already been generated up to this step
continue; continue;
} }
else if (chunk instanceof ProtoChunk) else if (chunk instanceof ProtoChunk)
{ {
chunkWrapper.trySetStatus(this.getChunkStatus()); chunkWrapper.trySetStatus(this.getChunkStatus());
chunksToGenerate.add(chunk); chunkWrappersToGenerate.add(chunkWrapper);
} }
} }
return chunksToGenerate; return chunkWrappersToGenerate;
} }
@@ -23,13 +23,11 @@ import java.util.ArrayList;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
@@ -66,47 +64,50 @@ public final class StepBiomes extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = this.getChunksToGenerate(chunkWrappers); ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkAccess chunk : chunksToDo) for (ChunkWrapper chunkWrapper : chunksToDo)
{ {
ChunkAccess chunk = chunkWrapper.getChunk();
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk); this.environment.globalParams.generator.createBiomes(this.environment.globalParams.biomes, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.biomes, this.environment.globalParams.biomes,
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.biomes, this.environment.globalParams.biomes,
Runnable::run, Runnable::run,
this.environment.params.randomState, Blender.of(worldGenRegion), this.environment.globalParams.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
Runnable::run, Runnable::run,
this.environment.params.randomState, this.environment.globalParams.randomState,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#else #else
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes( this.environment.globalParams.generator.createBiomes(
this.environment.params.randomState, this.environment.globalParams.randomState,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk) chunk)
); );
#endif #endif
@@ -21,15 +21,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -37,12 +36,13 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
import java.util.ArrayList;
import java.util.ConcurrentModificationException; import java.util.ConcurrentModificationException;
public final class StepFeatures extends AbstractWorldGenStep public final class StepFeatures extends AbstractWorldGenStep
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final ChunkStatus STATUS = ChunkStatus.FEATURES; public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
@@ -67,32 +67,24 @@ public final class StepFeatures extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
for (ChunkWrapper chunkWrapper : chunkWrappers) ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
}
try try
{ {
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
worldGenRegion.setOverrideCenter(chunk.getPos()); worldGenRegion.setOverrideCenter(chunk.getPos());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat); environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeatManager);
#else #else
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ())) if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
{ {
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion)); this.environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeatManager.forWorldGenRegion(worldGenRegion));
} }
else else
{ {
@@ -20,18 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.Blender; import net.minecraft.world.level.levelgen.blending.Blender;
@@ -68,56 +64,45 @@ public final class StepNoise extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>(); ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
continue;
}
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo)
{
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk); this.environment.globalParams.generator.fillFromNoise(worldGenRegion, tParams.structFeatManager, chunk);
#elif MC_VER < MC_1_18_2 #elif MC_VER < MC_1_18_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#elif MC_VER < MC_1_21_1 #elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Runnable::run, Runnable::run,
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
this.environment.params.randomState, this.environment.globalParams.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#else #else
chunk = this.environment.confirmFutureWasRunSynchronously( chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise( this.environment.globalParams.generator.fillFromNoise(
Blender.of(worldGenRegion), Blender.of(worldGenRegion),
this.environment.params.randomState, this.environment.globalParams.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
chunk)); chunk));
#endif #endif
UncheckedInterruptedException.throwIfInterrupted();
} }
} }
@@ -20,17 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -64,30 +61,14 @@ public final class StepStructureReference extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>(); ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS)) this.environment.globalParams.generator.createReferences(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureReference: "+chunk.getPos());
this.environment.params.generator.createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
} }
} }
@@ -20,22 +20,17 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.Logger;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -46,7 +41,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
public final class StepStructureStart extends AbstractWorldGenStep public final class StepStructureStart extends AbstractWorldGenStep
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock(); private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock();
@@ -71,74 +66,80 @@ public final class StepStructureStart extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = this.getChunksToGenerate(chunkWrappers); ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
// TODO should be put in wrapped environment so we can skip some other world gen steps
// SURFACE wouldn't need structure generation either
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
if (this.environment.params.worldGenSettings.generateFeatures()) if (!this.environment.globalParams.worldGenSettings.generateFeatures())
{
#elif MC_VER < MC_1_19_4 #elif MC_VER < MC_1_19_4
if (this.environment.params.worldGenSettings.generateStructures()) if (!this.environment.globalParams.worldGenSettings.generateStructures())
{
#else #else
if (this.environment.params.worldOptions.generateStructures()) if (!this.environment.globalParams.worldOptions.generateStructures())
{
#endif #endif
for (ChunkAccess chunk : chunksToDo) {
return;
}
for (ChunkWrapper chunkWrapper : chunksToDo)
{
ChunkAccess chunk = chunkWrapper.getChunk();
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine)
// and should prevent some concurrency issues
STRUCTURE_PLACEMENT_LOCK.lock();
#if MC_VER < MC_1_19_2
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.worldSeed);
#elif MC_VER < MC_1_19_4
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, this.environment.globalParams.randomState, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
tParams.structFeatManager, chunk, this.environment.globalParams.structures);
#else
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
tParams.structFeatManager, chunk, this.environment.globalParams.structures,
this.environment.globalParams.mcServerLevel.dimension());
#endif
#if MC_VER >= MC_1_18_2
try
{ {
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine) tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
// and should prevent some concurrency issues }
STRUCTURE_PLACEMENT_LOCK.lock(); catch (ArrayIndexOutOfBoundsException firstEx)
{
// There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds
// This means the structFeat is corrupted (For some reason) and I need to reset it.
// TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
#if MC_VER < MC_1_19_2 // reset the structureStart
this.environment.params.generator.createStructures(this.environment.params.registry, tParams.structFeat, chunk, this.environment.params.structures, tParams.recreateStructureCheck();
this.environment.params.worldSeed);
#elif MC_VER < MC_1_19_4
this.environment.params.generator.createStructures(this.environment.params.registry, this.environment.params.randomState, tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures);
#else
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.level.dimension());
#endif
#if MC_VER >= MC_1_18_2
try try
{ {
// try running the structure logic again
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()); tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
} }
catch (ArrayIndexOutOfBoundsException firstEx) catch (ArrayIndexOutOfBoundsException secondEx)
{ {
// There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds // the structure logic failed again, log it and move on
// This means the structFeat is corrupted (For some reason) and I need to reset it. LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
// TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
// reset the structureStart
tParams.recreateStructureCheck();
try
{
// try running the structure logic again
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
}
catch (ArrayIndexOutOfBoundsException secondEx)
{
// the structure logic failed again, log it and move on
LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
}
} }
#endif
STRUCTURE_PLACEMENT_LOCK.unlock();
} }
#endif
STRUCTURE_PLACEMENT_LOCK.unlock();
} }
} }
@@ -20,17 +20,14 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step; package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion; import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.ChunkStatus;
@@ -64,37 +61,24 @@ public final class StepSurface extends AbstractWorldGenStep
@Override @Override
public void generateGroup( public void generateGroup(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion, ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers) ArrayGridList<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>(); ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
for (ChunkWrapper chunkWrapper : chunksToDo)
for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepSurface: "+chunk.getPos());
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk); this.environment.globalParams.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
#elif MC_VER < MC_1_19_2 #elif MC_VER < MC_1_19_2
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
#else #else
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk); this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), this.environment.globalParams.randomState, chunk);
#endif #endif
} }
} }
} }
@@ -68,7 +68,7 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
/** /**
@@ -85,7 +85,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
private final ClientApi clientApi = ClientApi.INSTANCE; private final ClientApi clientApi = ClientApi.INSTANCE;
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
// TODO we shouldn't be filtering keys on the Forge/Fabric side, only in ClientApi // TODO we shouldn't be filtering keys on the Forge/Fabric side, only in ClientApi
private static final int[] KEY_TO_CHECK_FOR = { GLFW.GLFW_KEY_F6, GLFW.GLFW_KEY_F8, GLFW.GLFW_KEY_P}; private static final int[] KEY_TO_CHECK_FOR = { GLFW.GLFW_KEY_F6, GLFW.GLFW_KEY_F8, GLFW.GLFW_KEY_P};
@@ -223,7 +223,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// render event // // render event //
//==============// //==============//
// TODO wait for fabric to re-add their rendering API
#if MC_VER < MC_1_21_9 #if MC_VER < MC_1_21_9
WorldRenderEvents.AFTER_SETUP.register((renderContext) -> WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{ {
@@ -38,7 +38,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs; import org.lwjgl.util.tinyfd.TinyFileDialogs;
#if MC_VER >= MC_1_19_2 #if MC_VER >= MC_1_19_2
@@ -47,8 +47,6 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback; import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
#endif #endif
import javax.swing.*;
import java.awt.*;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@@ -64,7 +62,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH); private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#endif #endif
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -110,6 +108,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
this.tryCreateModCompatAccessor("starlight", IStarlightAccessor.class, StarlightAccessor::new); this.tryCreateModCompatAccessor("starlight", IStarlightAccessor.class, StarlightAccessor::new);
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new); this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("bclib", IBCLibAccessor.class, BCLibAccessor::new); this.tryCreateModCompatAccessor("bclib", IBCLibAccessor.class, BCLibAccessor::new);
this.tryCreateModCompatAccessor("c2me", IC2meAccessor.class, C2meAccessor::new);
#if MC_VER >= MC_1_19_4 #if MC_VER >= MC_1_19_4
// 1.19.4 is the lowest version Iris supports DH // 1.19.4 is the lowest version Iris supports DH
this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new); this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new);
@@ -30,7 +30,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
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.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER >= MC_1_20_6 #if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload; import com.seibel.distanthorizons.common.CommonPacketPayload;
@@ -52,7 +52,7 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private static final ServerApi SERVER_API = ServerApi.INSTANCE; private static final ServerApi SERVER_API = ServerApi.INSTANCE;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final boolean isDedicatedServer; private final boolean isDedicatedServer;
@@ -93,10 +93,6 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
/* Register the mod needed event callbacks */ /* Register the mod needed event callbacks */
// ServerTickEvent
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test overrides/events without having to build a separate API project // can be enabled to test overrides/events without having to build a separate API project
if (false) if (false)
{ {
@@ -77,7 +77,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -88,7 +88,7 @@ public class MixinLevelRenderer
private ClientLevel level; private ClientLevel level;
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
@@ -16,7 +16,7 @@ import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
@@ -34,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class MixinMinecraft public abstract class MixinMinecraft
{ {
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Shadow @Shadow
@@ -13,8 +13,7 @@ public class MixinLevelTicks<T>
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.world.ticks.LevelTicks; import net.minecraft.world.ticks.LevelTicks;
import net.minecraft.world.ticks.ScheduledTick; import net.minecraft.world.ticks.ScheduledTick;
@@ -26,18 +25,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+ @Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+
public class MixinLevelTicks<T> public class MixinLevelTicks<T>
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true) @Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci) private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci)
{ {
// In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks // In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks
// this caused a lot of unnecessary errors when generating sand (FallingBlock.class). // this caused a lot of unnecessary errors when generating sand (FallingBlock.class).
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
ci.cancel(); ci.cancel();
} }
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@@ -35,10 +36,8 @@ public class MixinTracingExecutor
} }
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import net.minecraft.TracingExecutor; import net.minecraft.TracingExecutor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@@ -55,16 +54,11 @@ import java.util.concurrent.Executor;
@Mixin(TracingExecutor.class) @Mixin(TracingExecutor.class)
public class MixinTracingExecutor public class MixinTracingExecutor
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
// replaced with TracingExecutor in MC 1.21.3+ // replaced with TracingExecutor in MC 1.21.3+
@Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true) @Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true)
private void forName(String executorName, CallbackInfoReturnable<Executor> ci) private void forName(String executorName, CallbackInfoReturnable<Executor> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -19,19 +19,19 @@
package com.seibel.distanthorizons.fabric.mixins.server; package com.seibel.distanthorizons.fabric.mixins.server;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.Util;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.Util;
import java.util.concurrent.ExecutorService;
#if MC_VER < MC_1_16_5 #if MC_VER < MC_1_16_5
#elif MC_VER < MC_1_21_3 #elif MC_VER < MC_1_21_3
import java.util.concurrent.ExecutorService;
import java.util.function.Supplier; import java.util.function.Supplier;
#else #else
#endif #endif
@@ -46,15 +46,11 @@ import java.util.function.Supplier;
@Mixin(Util.class) @Mixin(Util.class)
public class MixinUtilBackgroundThread public class MixinUtilBackgroundThread
{ {
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true) @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci) private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -70,7 +66,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci) private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -86,7 +82,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci) private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -5,13 +5,13 @@ import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.IOException; import java.io.IOException;
public class TestChunkInputReplacerEvent extends DhApiChunkProcessingEvent public class TestChunkInputReplacerEvent extends DhApiChunkProcessingEvent
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final String REPLACEMENT_BLOCK_STATE_NAMESPACE = "minecraft:stone"; private static final String REPLACEMENT_BLOCK_STATE_NAMESPACE = "minecraft:stone";
@@ -13,7 +13,7 @@ import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
public class TestGenericWorldGenerator implements IDhApiWorldGenerator public class TestGenericWorldGenerator implements IDhApiWorldGenerator
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final IDhApiLevelWrapper levelWrapper; private final IDhApiLevelWrapper levelWrapper;
@@ -6,12 +6,12 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLo
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
// TODO add to API example once Builderb0y has given the all-clear // TODO add to API example once Builderb0y has given the all-clear
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override @Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event) public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
@@ -17,20 +17,16 @@
* 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; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import java.util.function.Supplier; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
public class DependencySetupDoneCheck public class C2meAccessor implements IC2meAccessor
{ {
// TODO move to DependencySetup @Override
public static boolean isDone = false; public String getModName()
/** {
* This is used so we can override some MC logic when running return "c2me";
* in DH's world generator. }
* Specifically so we can redirect threads to run on DH threads instead
* of MC threads.
*/
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
} }
@@ -34,22 +34,14 @@ import net.irisshaders.iris.api.v0.IrisApi;
public class IrisAccessor implements IIrisAccessor public class IrisAccessor implements IIrisAccessor
{ {
@Override @Override
public String getModName() public String getModName() { return Iris.MODID; }
{
return Iris.MODID;
}
@Override @Override
public boolean isShaderPackInUse() public boolean isShaderPackInUse() { return IrisApi.getInstance().isShaderPackInUse(); }
{
return IrisApi.getInstance().isShaderPackInUse();
}
@Override @Override
public boolean isRenderingShadowPass() public boolean isRenderingShadowPass() { return IrisApi.getInstance().isRenderingShadowPass(); }
{
return IrisApi.getInstance().isRenderingShadowPass();
}
} }
#endif #endif
@@ -55,7 +55,7 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
@@ -81,7 +81,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
{ {
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
@@ -47,7 +47,7 @@ import net.minecraftforge.event.server.ServerStoppingEvent;
#endif #endif
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -90,16 +90,6 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
// events // // events //
//========// //========//
// ServerTickEvent (at end)
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (event.phase == TickEvent.Phase.END)
{
this.serverApi.serverTickEvent();
}
}
// ServerWorldLoadEvent // ServerWorldLoadEvent
@SubscribeEvent @SubscribeEvent
public void dedicatedWorldLoadEvent(#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 FMLServerAboutToStartEvent #else ServerAboutToStartEvent #endif event) public void dedicatedWorldLoadEvent(#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 FMLServerAboutToStartEvent #else ServerAboutToStartEvent #endif event)
@@ -12,7 +12,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinMinecraft public class MixinMinecraft
{ {
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -22,12 +22,15 @@ package com.seibel.distanthorizons.forge.mixins.server;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import net.minecraft.Util; import net.minecraft.Util;
@@ -35,17 +38,17 @@ import net.minecraft.Util;
@Mixin(Util.class) @Mixin(Util.class)
public class MixinUtilBackgroundThread public class MixinUtilBackgroundThread
{ {
private static boolean shouldApplyOverride() @Unique
{ private static final DhLogger LOGGER = new DhLoggerBuilder().name("MixinUtilBackgroundThread").build();
return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get();
}
@Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true) @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci) private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci)
{ {
if (shouldApplyOverride()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util backgroundExecutor triggered"); //LOGGER.info("util backgroundExecutor triggered");
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
} }
} }
@@ -55,21 +58,22 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci) private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
{ {
if (shouldApplyOverride()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered"); //LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
} }
} }
#endif #endif
#if MC_VER >= MC_1_18_2 #if MC_VER >= MC_1_18_2
@Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/util/function/Supplier;)Ljava/util/function/Supplier;", @Inject(method = "wrapThreadWithTaskName(Ljava/lang/String;Ljava/util/function/Supplier;)Ljava/util/function/Supplier;",
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci) private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
{ {
if (shouldApplyOverride()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered"); //LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
} }
} }
@@ -6,7 +6,7 @@
"server.MixinUtilBackgroundThread", "server.MixinUtilBackgroundThread",
"server.MixinChunkGenerator", "server.MixinChunkGenerator",
"server.MixinTFChunkGenerator", "server.MixinTFChunkGenerator",
"server.MixinChunkMap", "server.MixinChunkMap",
"server.MixinServerPlayer", "server.MixinServerPlayer",
"server.MixinEntity" "server.MixinEntity"
], ],
+3 -2
View File
@@ -5,8 +5,8 @@ org.gradle.caching=true
# Mod Info # Mod Info
mod_name=DistantHorizons mod_name=DistantHorizons
mod_version=2.3.6-b mod_version=2.3.7-b-dev
api_version=4.1.0 api_version=5.0.0
maven_group=com.seibel.distanthorizons maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons mod_readable_name=Distant Horizons
mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow. mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.
@@ -23,6 +23,7 @@ manifold_version=2025.1.27
nightconfig_version=3.6.6 nightconfig_version=3.6.6
lz4_version=1.8.0 lz4_version=1.8.0
xz_version=1.9 xz_version=1.9
zstd_version=1.5.7-6
# Before updating, read relocate_natives/README.md # Before updating, read relocate_natives/README.md
sqlite_jdbc_version=3.47.2.0 sqlite_jdbc_version=3.47.2.0
# 8.2.1 is the newest version we can use since that's the version MC 1.16.5 uses # 8.2.1 is the newest version we can use since that's the version MC 1.16.5 uses
+4 -1
View File
@@ -65,7 +65,10 @@ dependencies {
// 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)
// Iris
addMod("maven.modrinth:iris:${rootProject.neo_iris_version}", rootProject.neo_enable_iris)
} }
@@ -42,7 +42,7 @@ import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -64,7 +64,7 @@ import java.util.concurrent.AbstractExecutorService;
public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
{ {
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -30,9 +30,13 @@ import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
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;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.C2meAccessor;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.IrisAccessor;
import com.seibel.distanthorizons.neoforge.wrappers.NeoforgeMinecraftRenderWrapper; import com.seibel.distanthorizons.neoforge.wrappers.NeoforgeMinecraftRenderWrapper;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.ModChecker; import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.ModChecker;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.OptifineAccessor; import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.OptifineAccessor;
@@ -144,6 +148,12 @@ public class NeoforgeMain extends AbstractModInitializer
protected void initializeModCompat() protected void initializeModCompat()
{ {
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new); this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("c2me", IC2meAccessor.class, C2meAccessor::new);
#if MC_VER >= MC_1_20_6
// 1.20.6 is the lowest version Iris supports Neoforge
this.tryCreateModCompatAccessor("iris", IIrisAccessor.class, IrisAccessor::new);
#endif
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, ModLoadingContext.get().registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class,
@@ -24,14 +24,13 @@ import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.neoforge.event.server.ServerAboutToStartEvent; import net.neoforged.neoforge.event.server.ServerAboutToStartEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.function.Supplier; import java.util.function.Supplier;
#if MC_VER < MC_1_20_6 #if MC_VER < MC_1_20_6
import net.neoforged.neoforge.event.TickEvent; import net.neoforged.neoforge.event.TickEvent;
#else #else
import net.neoforged.neoforge.event.tick.ServerTickEvent;
#endif #endif
@@ -40,7 +39,7 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); } private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
private final ServerApi serverApi = ServerApi.INSTANCE; private final ServerApi serverApi = ServerApi.INSTANCE;
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final boolean isDedicated; private final boolean isDedicated;
public static Supplier<Boolean> isGenerationThreadChecker = null; public static Supplier<Boolean> isGenerationThreadChecker = null;
@@ -52,7 +51,7 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
public NeoforgeServerProxy(boolean isDedicated) public NeoforgeServerProxy(boolean isDedicated)
{ {
this.isDedicated = isDedicated; this.isDedicated = isDedicated;
isGenerationThreadChecker = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread; isGenerationThreadChecker = BatchGenerationEnvironment::isThisDhWorldGenThread;
} }
@Override @Override
@@ -68,23 +67,6 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
// events // // events //
//========// //========//
#if MC_VER < MC_1_20_6
@SubscribeEvent
public void serverTickEvent(TickEvent.ServerTickEvent event)
{
if (event.phase == TickEvent.Phase.END)
{
this.serverApi.serverTickEvent();
}
}
#else
@SubscribeEvent
public void serverTickEvent(ServerTickEvent.Post event)
{
this.serverApi.serverTickEvent();
}
#endif
// ServerWorldLoadEvent // ServerWorldLoadEvent
@SubscribeEvent @SubscribeEvent
public void dedicatedWorldLoadEvent(ServerAboutToStartEvent event) public void dedicatedWorldLoadEvent(ServerAboutToStartEvent event)
@@ -48,7 +48,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy; import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
@@ -76,7 +76,7 @@ public class MixinLevelRenderer
private ClientLevel level; private ClientLevel level;
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@@ -12,7 +12,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.gui.screens.TitleScreen;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinMinecraft public class MixinMinecraft
{ {
@Unique @Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
/** /**
* Can be enabled for testing the auto updater UI. <br/> * Can be enabled for testing the auto updater UI. <br/>
@@ -13,7 +13,7 @@ public class MixinLevelTicks<T>
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import net.minecraft.world.ticks.LevelTicks; import net.minecraft.world.ticks.LevelTicks;
import net.minecraft.world.ticks.ScheduledTick; import net.minecraft.world.ticks.ScheduledTick;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -24,18 +24,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+ @Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+
public class MixinLevelTicks<T> public class MixinLevelTicks<T>
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true) @Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci) private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci)
{ {
// In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks // In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks
// this caused a lot of unnecessary errors when generating sand (FallingBlock.class). // this caused a lot of unnecessary errors when generating sand (FallingBlock.class).
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
ci.cancel(); ci.cancel();
} }
@@ -35,7 +35,7 @@ public class MixinTracingExecutor
} }
#else #else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import net.minecraft.TracingExecutor; import net.minecraft.TracingExecutor;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -55,11 +55,6 @@ import java.util.concurrent.Executor;
@Mixin(TracingExecutor.class) @Mixin(TracingExecutor.class)
public class MixinTracingExecutor public class MixinTracingExecutor
{ {
// TODO put in a common location
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
// Util.backgroundExecutor().forName("init_biomes") // Util.backgroundExecutor().forName("init_biomes")
// needed for world gen // needed for world gen
@@ -67,7 +62,7 @@ public class MixinTracingExecutor
@Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true) @Inject(method = "forName(Ljava/lang/String;)Ljava/util/concurrent/Executor;", at = @At("HEAD"), cancellable = true)
private void forName(String executorName, CallbackInfoReturnable<Executor> ci) private void forName(String executorName, CallbackInfoReturnable<Executor> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -19,18 +19,20 @@
package com.seibel.distanthorizons.neoforge.mixins.server; package com.seibel.distanthorizons.neoforge.mixins.server;
import java.util.concurrent.ExecutorService; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import java.util.function.Supplier;
import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService; import com.seibel.distanthorizons.core.util.objects.RunOnThisThreadExecutorService;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.Util;
#if MC_VER < MC_1_21_3
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import java.util.concurrent.ExecutorService;
import java.util.function.Supplier;
import net.minecraft.Util; #endif
/** /**
* This is needed for DH's world gen so we can run * This is needed for DH's world gen so we can run
@@ -42,16 +44,13 @@ import net.minecraft.Util;
@Mixin(Util.class) @Mixin(Util.class)
public class MixinUtilBackgroundThread public class MixinUtilBackgroundThread
{ {
private static boolean isWorldGenThread()
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true) @Inject(method = "backgroundExecutor", at = @At("HEAD"), cancellable = true)
private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci) private static void overrideUtil$backgroundExecutor(CallbackInfoReturnable<ExecutorService> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
// run this task on the current DH thread instead of a new MC thread // run this task on the current DH thread instead of a new MC thread
ci.setReturnValue(new RunOnThisThreadExecutorService()); ci.setReturnValue(new RunOnThisThreadExecutorService());
@@ -67,7 +66,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci) private static void overrideUtil$wrapThreadWithTaskName(String string, Runnable r, CallbackInfoReturnable<Runnable> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Runnable) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -83,7 +82,7 @@ public class MixinUtilBackgroundThread
at = @At("HEAD"), cancellable = true) at = @At("HEAD"), cancellable = true)
private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci) private static void overrideUtil$wrapThreadWithTaskNameForSupplier(String string, Supplier<?> r, CallbackInfoReturnable<Supplier<?>> ci)
{ {
if (isWorldGenThread()) if (BatchGenerationEnvironment.isThisDhWorldGenThread())
{ {
//ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered"); //ApiShared.LOGGER.info("util wrapThreadWithTaskName(Supplier) triggered");
ci.setReturnValue(r); ci.setReturnValue(r);
@@ -3,7 +3,7 @@ package com.seibel.distanthorizons.neoforge.wrappers;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger; import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER < MC_1_21_9 #if MC_VER < MC_1_21_9
#else #else
@@ -18,7 +18,7 @@ public class NeoforgeMinecraftRenderWrapper extends MinecraftRenderWrapper
{ {
public static final NeoforgeMinecraftRenderWrapper INSTANCE = new NeoforgeMinecraftRenderWrapper(); public static final NeoforgeMinecraftRenderWrapper INSTANCE = new NeoforgeMinecraftRenderWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName()); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final Minecraft MC = Minecraft.getInstance(); private static final Minecraft MC = Minecraft.getInstance();
@@ -17,40 +17,16 @@
* 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.neoforge.wrappers.modAccessor;
package com.seibel.distanthorizons.common.wrappers.worldGeneration; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
//FIXME: Move this outside the WorldGenerationStep thingy public class C2meAccessor implements IC2meAccessor
public class Rolling
{ {
@Override
private final int size; public String getModName()
private double total = 0d;
private int index = 0;
private final double[] samples;
public Rolling(int size)
{ {
this.size = size; return "c2me";
samples = new double[size];
for (int i = 0; i < size; i++)
{
samples[i] = 0d;
}
} }
public void add(double x) }
{
total -= samples[index];
samples[index] = x;
total += x;
if (++index == size)
index = 0; // cheaper than modulus
}
public double getAverage()
{
return size == 0 ? 0 : total / size;
}
}
@@ -0,0 +1,67 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 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.neoforge.wrappers.modAccessor;
// 1.20.6 is the lowest version Iris supports Neoforge
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
#if MC_VER != MC_1_21_9
import net.irisshaders.iris.Iris;
import net.irisshaders.iris.api.v0.IrisApi;
#endif
public class IrisAccessor implements IIrisAccessor
{
@Override
public String getModName()
{
#if MC_VER == MC_1_21_9
return "iris"; // Iris doesn't support this MC version
#else
return Iris.MODID;
#endif
}
@Override
public boolean isShaderPackInUse()
{
#if MC_VER == MC_1_21_9
return true; // Iris doesn't support this MC version
#else
return IrisApi.getInstance().isShaderPackInUse();
#endif
}
@Override
public boolean isRenderingShadowPass()
{
#if MC_VER == MC_1_21_9
return false; // Iris doesn't support this MC version
#else
return IrisApi.getInstance().isRenderingShadowPass();
#endif
}
}
#endif
-1
View File
@@ -48,7 +48,6 @@ forge_version=36.2.39
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version=4044290 terraforged_version=4044290
# Forge mod run # Forge mod run
-1
View File
@@ -47,7 +47,6 @@ forge_version=37.1.1
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=3457784
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
-1
View File
@@ -56,7 +56,6 @@ forge_version=40.2.10
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
-1
View File
@@ -47,7 +47,6 @@ forge_version=43.3.2
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
-1
View File
@@ -46,7 +46,6 @@ forge_version=45.2.4
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
-1
View File
@@ -46,7 +46,6 @@ forge_version=47.2.1
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
-1
View File
@@ -46,7 +46,6 @@ forge_version=48.0.13
neoforge_version_range=[*,) neoforge_version_range=[*,)
# Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# Forge mod run # Forge mod run
+4 -5
View File
@@ -41,16 +41,15 @@ fabric_api_version=0.91.2+1.20.4
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # Forge loader
forge_version=49.1.13 forge_version=49.1.13
neoforge_version=20.4.246 neoforge_version=
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions # Forge mod versions
starlight_version_forge=
terraforged_version= terraforged_version=
# (Neo)Forge mod run # Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
+7 -10
View File
@@ -41,19 +41,16 @@ fabric_api_version=0.97.8+1.20.6
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version=50.0.19 forge_version=
neoforge_version=20.6.136 neoforge_version=20.6.136
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.8.12+1.21.1-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+6 -8
View File
@@ -41,19 +41,17 @@ fabric_api_version=0.115.0+1.21.1
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.1.192 neoforge_version=21.1.216
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.8.12+1.21.1-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+7 -10
View File
@@ -12,7 +12,7 @@ netty_version=4.1.97.Final
# Fabric loader # Fabric loader
fabric_loader_version=0.17.3 fabric_loader_version=0.17.3
fabric_api_version=0.135.0+1.21.10 fabric_api_version=0.138.3+1.21.10
modmenu_version=16.0.0-rc.1 modmenu_version=16.0.0-rc.1
starlight_version_fabric= starlight_version_fabric=
phosphor_version_fabric= phosphor_version_fabric=
@@ -39,19 +39,16 @@ fabric_api_version=0.135.0+1.21.10
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.10.6-beta neoforge_version=21.10.56-beta
neoforge_version_range=[21.10.6-beta,) neoforge_version_range=[21.10.6-beta,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.9.6+1.21.10-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+5 -8
View File
@@ -41,19 +41,16 @@ fabric_api_version=0.110.0+1.21.3
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.3.86 neoforge_version=21.3.86
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.8.1+1.21.3-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+5 -8
View File
@@ -40,20 +40,17 @@ fabric_api_version=0.110.5+1.21.4
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.4.147 neoforge_version=21.4.147
# version range may not be necessary, but having compiled DH for an older version caused issues with shaders # version range may not be necessary, but having compiled DH for an older version caused issues with shaders
neoforge_version_range=[21.4.147,) neoforge_version_range=[21.4.147,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.8.8+1.21.4-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+5 -8
View File
@@ -40,19 +40,16 @@ fabric_api_version=0.119.5+1.21.5
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.5.87 neoforge_version=21.5.87
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions # NeoForge mod versions
starlight_version_forge= neo_iris_version=1.8.11+1.21.5-neoforge
terraforged_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+5 -8
View File
@@ -39,20 +39,17 @@ fabric_api_version=0.127.0+1.21.6
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.6.20-beta neoforge_version=21.6.20-beta
# around 6.19 neo changed how their render API works, failing to meet this causes the game to crash # around 6.19 neo changed how their render API works, failing to meet this causes the game to crash
neoforge_version_range=[21.6.19-beta,) neoforge_version_range=[21.6.19-beta,)
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# NeoForge mod versions
neo_iris_version=1.9.6+1.21.8-neoforge
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+7 -9
View File
@@ -39,20 +39,18 @@ fabric_api_version=0.133.4+1.21.8
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.8.2-beta neoforge_version=21.8.52
# around 6.19 neo changed how their render API works, failing to meet this causes the game to crash # around 6.19 neo changed how their render API works, failing to meet this causes the game to crash
neoforge_version_range=[*,) neoforge_version_range=[*,)
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# NeoForge mod versions
neo_iris_version=1.9.6+1.21.8-neoforge
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=1
enable_terraforged=0
enable_terrafirmacraft=0
+6 -8
View File
@@ -39,21 +39,19 @@ fabric_api_version=0.134.0+1.21.9
enable_immersive_portals=0 enable_immersive_portals=0
enable_canvas=0 enable_canvas=0
# (Neo)Forge loader # NeoForge loader
forge_version= forge_version=
neoforge_version=21.9.15-beta neoforge_version=21.9.15-beta
# sometime before 21.9.15-beta Neoforge changed how their rendering API events handle levels # sometime before 21.9.15-beta Neoforge changed how their rendering API events handle levels
# so we can't support both versions at once # so we can't support both versions at once
neoforge_version_range=[21.9.15-beta,) neoforge_version_range=[21.9.15-beta,)
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# NeoForge mod versions
# Iris doesn't exist for this MC version
neo_iris_version=
# (Neo)Forge mod run # (Neo)Forge mod run
# 0 = Don't enable and don't run # 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run # 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client # 2 = Can be referenced in code and runs in client
enable_starlight_forge=0 neo_enable_iris=0
enable_terraforged=0
enable_terrafirmacraft=0