Compare commits

...

47 Commits

Author SHA1 Message Date
James Seibel f67fb1758d Speed up shutdown and reduce logging 2025-11-14 07:46: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
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
James Seibel 106d97e0a1 Revert "minor AbstractDhTintGetter optimization"
This reverts commit 6a418de153.
2025-11-07 07:02:21 -06: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
James Seibel 3597d69fa4 remove dev from version number 2025-10-13 17:45:45 -05:00
James Seibel ab10265150 Fix 1.21.9 missing fabric resource loader v1 2025-10-13 17:45:45 -05:00
s809 671ee84136 Handle server keyed level wrappers correctly 2025-10-13 18:07:06 +05:00
James Seibel c489cebae3 Fix world gen progress ui button 2025-10-13 07:43:02 -05:00
James Seibel e55eeda1ac Improve config gui object casting 2025-10-13 07:35:53 -05:00
James Seibel 0086f40053 bug fixing for major config refactoring 2025-10-12 21:11:16 -05:00
James Seibel 71bb151e61 Fix 1.21.10 compile typo 2025-10-12 19:41:42 -05:00
James Seibel a71ceac15d Fix forge compiling 2025-10-12 19:41:03 -05:00
James Seibel ada36c34c7 minor client wrapper refactor for clarity 2025-10-12 15:30:20 -05:00
James Seibel 3cfb4386d9 set default dev version to 1.21.10 2025-10-12 15:07:23 -05:00
James Seibel 352d0f4759 re-add MC 1.21.9 support 2025-10-12 15:04:31 -05:00
James Seibel d158a89592 Fix neoforge rendering 2025-10-12 15:02:09 -05:00
James Seibel 9cc826f8a9 Fix client tinted biomes 2025-10-12 14:49:32 -05:00
James Seibel 22a4c6bc79 Fix neoforge server startup crash 2025-10-12 14:05:27 -05:00
James Seibel 20b9f4f1cb Fix stuttering when flying over un-generated chunks 2025-10-12 13:58:03 -05:00
James Seibel 5c0c1c5e20 up version number 2.3.5 -> 2.3.6 2025-10-11 20:55:45 -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
65 changed files with 1001 additions and 626 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ build:
parallel:
matrix:
- MC_VER: [
"1.21.10", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
"1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
"1.20.6", "1.20.4", "1.20.2", "1.20.1",
"1.19.4", "1.19.2",
"1.18.2",
+1 -1
View File
@@ -248,7 +248,7 @@ subprojects { p ->
// We cannot relocate this library since we call some MC classes that reference it
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
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
@@ -10,7 +10,7 @@ import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapp
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.config.ConfigBase;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
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.util.function.Consumer;
@@ -36,7 +36,7 @@ import java.util.function.Supplier;
*/
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;
@@ -46,7 +46,8 @@ public abstract class AbstractModInitializer
// abstract methods //
//==================//
protected abstract void createInitialBindings();
protected abstract void createInitialSharedBindings();
protected abstract void createInitialClientBindings();
protected abstract IEventProxy createClientProxy();
protected abstract IEventProxy createServerProxy(boolean isDedicated);
protected abstract void initializeModCompat();
@@ -66,6 +67,7 @@ public abstract class AbstractModInitializer
public void onInitializeClient()
{
DependencySetup.createClientBindings();
this.createInitialClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client, firing DhApiBeforeDhInitEvent...");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
@@ -84,7 +86,7 @@ public abstract class AbstractModInitializer
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// debug screen rendering handled via a mixin
#else
DhDebugScreenEntry.register();
@@ -140,7 +142,7 @@ public abstract class AbstractModInitializer
{
DependencySetup.createSharedBindings();
SharedApi.init();
this.createInitialBindings();
this.createInitialSharedBindings();
}
private void logBuildInfo()
@@ -168,8 +170,9 @@ public abstract class AbstractModInitializer
private void initConfig()
{
ConfigBase.RunFirstTimeSetup();
ConfigHandler.tryRunFirstTimeSetup();
Config.completeDelayedSetup();
DhLogger.runDelayedConfigSetup();
}
private void checkForUpdates()
@@ -190,6 +193,12 @@ public abstract class AbstractModInitializer
{
LOGGER.info("Running Delayed setup...");
this.runDelayedSetup();
if (ConfigHandler.INSTANCE == null)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
LOGGER.info("Delayed setup complete, firing DhApiAfterDhInitEvent event...");
// should be fired after all delayed setup so singletons and config can be accessed
@@ -1,8 +1,8 @@
package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
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.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Common.Logging.logNetworkEvent.get());
private static final DhLogger LOGGER = new DhLoggerBuilder()
.fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
.build();
#if MC_VER >= MC_1_21_1
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
@@ -3,8 +3,8 @@ package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import net.minecraft.commands.CommandSourceStack;
@@ -43,7 +43,7 @@ public class ConfigCommand extends AbstractCommand
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
HashSet<String> addedCommands = new HashSet<>();
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
for (AbstractConfigBase<?> type : ConfigHandler.INSTANCE.configBaseList)
{
// Skip non-config entries
if (!(type instanceof ConfigEntry))
@@ -17,9 +17,9 @@ public class CrashCommand extends AbstractCommand
.requires(this::isPlayerSource)
.then(literal("encode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
@@ -29,9 +29,9 @@ public class CrashCommand extends AbstractCommand
}))
.then(literal("decode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
assert SharedApi.tryGetDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
@@ -79,6 +79,8 @@ public class VersionConstants implements IVersionConstants
#elif MC_VER == MC_1_21_8
return "1.21.8";
#elif MC_VER == MC_1_21_9
return "1.21.9";
#elif MC_VER == MC_1_21_10
return "1.21.10";
#else
@@ -1,16 +1,31 @@
package com.seibel.distanthorizons.common.wrappers.block;
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.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
@@ -18,10 +33,24 @@ import net.minecraft.core.Holder;
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
{
protected final BiomeWrapper biomeWrapper;
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
protected final int smoothingRadiusInBlocks;
protected final FullDataSourceV2 fullDataSource;
#if MC_VER < MC_1_18_2
private static final ConcurrentHashMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#else
private static final ConcurrentHashMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
#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;
@@ -29,10 +58,18 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// constructor //
//=============//
public AbstractDhTintGetter(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
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.blockStateWrapper = blockStateWrapper;
this.fullDataSource = fullDataSource;
this.clientLevelWrapper = clientLevelWrapper;
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
}
@@ -42,8 +79,27 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// shared methods //
//================//
/** Called by MC's tint getter */
@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
// if blending should be used
@@ -57,7 +113,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
if (this.smoothingRadiusInBlocks == 0
|| dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks)
{
return colorResolver.getColor(this.unwrapBiome(this.biomeWrapper.biome), blockPos.getX(), blockPos.getZ());
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
}
@@ -67,13 +123,13 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
int rollingGreen = 0;
int rollingBlue = 0;
int xMin = blockPos.getX() - this.smoothingRadiusInBlocks;
int xMax = blockPos.getX() + this.smoothingRadiusInBlocks;
int xMin = mutableBlockPos.getX() - this.smoothingRadiusInBlocks;
int xMax = mutableBlockPos.getX() + this.smoothingRadiusInBlocks;
int zMin = mutableBlockPos.getZ() - this.smoothingRadiusInBlocks;
int zMax = mutableBlockPos.getZ() + this.smoothingRadiusInBlocks;
int zMin = blockPos.getZ() - this.smoothingRadiusInBlocks;
int zMax = blockPos.getZ() + this.smoothingRadiusInBlocks;
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(0, blockPos.getY(), 0);
for (int x = xMin; x < xMax; x++)
{
for (int z = zMin; z < zMax; z++)
@@ -84,7 +140,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// 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
// we ignore smoothing anyway
long dataPoint = this.fullDataSource.getAtBlockPos(mutableBlockPos);
long dataPoint = this.fullDataSource.getDataPointAtBlockPos(mutableBlockPos);
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
{
continue;
@@ -93,7 +149,11 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// get the color for this nearby position
int id = FullDataPointUtil.getId(dataPoint);
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
int color = colorResolver.getColor(this.unwrapBiome(biomeWrapper.biome), mutableBlockPos.getX(), mutableBlockPos.getZ());
int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
if (color == INVALID_COLOR)
{
return INVALID_COLOR;
}
// rolling average
@@ -110,7 +170,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
// just use the default center's color
if (dataPointCount == 0)
{
return colorResolver.getColor(this.unwrapBiome(this.biomeWrapper.biome), blockPos.getX(), blockPos.getZ());
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
}
int colorInt = ColorUtil.argbToInt(
@@ -121,7 +181,50 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
return colorInt;
}
protected Biome unwrapBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
/**
* 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)
{
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();
if (biomeString == null
|| biomeString.isEmpty()
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
{
// default to "plains" for empty/invalid biomes
biomeString = "minecraft:plains";
}
return unwrapBiome(getClientBiome(biomeString));
}
protected static Biome unwrapBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
{
#if MC_VER >= MC_1_18_2
return biome.value();
@@ -130,6 +233,103 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter
#endif
}
/**
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
* <pre>{@code
* // Inside constructor (OLD WAY - PROBLEMATIC):
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
* }</pre>
*
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
* the Holder itself is now necessary:</p>
* <ol>
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
* at construction time.</li>
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
* actual {@code Biome} object.</li>
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
* instance to the {@code Holder} reference, replacing any previous one.</li>
* </ol>
*
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
*
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
* instance associated with the holder at that time.</p>
*/
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
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
(resourceString, existingBiome) ->
{
if (existingBiome != null)
{
return existingBiome;
}
ClientLevel clientLevel = Minecraft.getInstance().level;
if (clientLevel == null)
{
// shouldn't happen, but just in case
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
}
BiomeWrapper.BiomeDeserializeResult result;
try
{
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
}
catch (Exception e)
{
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
try
{
result = BiomeWrapper.deserializeBiome(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, clientLevel.registryAccess());
}
catch (IOException ex)
{
// should never happen, if it does this log will explode, but just in case
LOGGER.error("Unable to deserialize fallback client biome ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], returning NULL.");
return null;
}
}
if (result.success)
{
existingBiome = result.biome;
}
return existingBiome;
});
}
}
@@ -26,10 +26,11 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.Level;
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;
@@ -57,7 +58,7 @@ import net.minecraft.world.level.biome.Biomes;
public class BiomeWrapper implements IBiomeWrapper
{
// 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
@@ -103,7 +104,7 @@ public class BiomeWrapper implements IBiomeWrapper
// constructors //
//==============//
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
{
if (biome == null)
{
@@ -111,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
{
@@ -299,7 +301,7 @@ public class BiomeWrapper implements IBiomeWrapper
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(deserializeResult.biome, levelWrapper);
foundWrapper = getBiomeWrapper(deserializeResult.biome, levelWrapper);
return foundWrapper;
}
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.SoundType;
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.io.IOException;
@@ -72,7 +72,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// 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<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
@@ -20,17 +20,16 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
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.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
@@ -41,7 +40,7 @@ import net.minecraft.util.RandomSource;
import java.util.Random;
#endif
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 java.util.ArrayList;
@@ -63,10 +62,8 @@ import net.minecraft.client.renderer.block.model.BlockModelPart;
*/
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> BROKEN_BLOCK_STATES = new HashSet<>();
@@ -96,14 +93,11 @@ public class ClientBlockStateColorCache
#endif
private final IClientLevelWrapper clientLevelWrapper;
private final BlockStateWrapper blockStateWrapper;
private final BlockState blockState;
private final LevelReader level;
private final BlockStateWrapper blockStateWrapper;
private boolean isColorResolved = false;
private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false;
private int tintIndex = 0;
@@ -171,18 +165,20 @@ public class ClientBlockStateColorCache
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 //
//=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
{
this.blockState = blockState;
this.clientLevelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.blockStateWrapper = BlockStateWrapper.fromBlockState(this.blockState, this.clientLevelWrapper);
this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
this.clientLevelWrapper = clientLevelWrapper;
this.resolveColors();
}
@@ -236,32 +232,29 @@ public class ClientBlockStateColorCache
this.needPostTinting = firstQuad.isTinted();
#if MC_VER <= MC_1_21_4
this.needShade = firstQuad.isShade();
this.tintIndex = firstQuad.getTintIndex();
#else
this.needShade = firstQuad.shade();
this.tintIndex = firstQuad.tintIndex();
#endif
#if MC_VER < MC_1_17_1
this.baseColor = calculateColorFromTexture(
firstQuad.sprite,
ColorMode.getColorMode(this.blockState.getBlock()));
EColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER < MC_1_21_5
this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(),
ColorMode.getColorMode(this.blockState.getBlock()));
EColorMode.getColorMode(this.blockState.getBlock()));
#else
this.baseColor = calculateColorFromTexture(
firstQuad.sprite(),
ColorMode.getColorMode(this.blockState.getBlock()));
EColorMode.getColorMode(this.blockState.getBlock()));
#endif
}
else
{
// Backup method.
this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = this.getParticleIconColor();
}
@@ -270,19 +263,11 @@ public class ClientBlockStateColorCache
{
// Liquid Block
this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0;
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;
}
finally
@@ -320,7 +305,7 @@ public class ClientBlockStateColorCache
}
//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 alpha = 0;
@@ -330,8 +315,8 @@ public class ClientBlockStateColorCache
int tempColor;
// don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel)
// Since EColorMode is set per block, you only need to check this once.
if (colorMode != EColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int v = 0; v < getTextureHeight(texture); v++)
@@ -349,7 +334,7 @@ public class ClientBlockStateColorCache
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
if (colorMode == EColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
@@ -368,11 +353,11 @@ public class ClientBlockStateColorCache
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != ColorMode.Glass)
else if (a == 0 && colorMode != EColorMode.Glass)
{
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;
}
@@ -455,7 +440,7 @@ public class ClientBlockStateColorCache
{
return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
EColorMode.getColorMode(this.blockState.getBlock()));
}
@@ -464,7 +449,7 @@ public class ClientBlockStateColorCache
// public getter //
//===============//
public int getColor(BiomeWrapper biome, FullDataSourceV2 fullDataSource, DhBlockPos pos)
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
{
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
@@ -488,16 +473,27 @@ public class ClientBlockStateColorCache
{
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState,
new TintWithoutLevelSmoothOverrider(biome, fullDataSource), // TODO can this object be cached?
McObjectConverter.Convert(pos),
this.tintIndex);
TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
// try using DH's cached tint values first if possible
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)
{
// this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+TintWithoutLevelSmoothOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] 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);
}
}
@@ -505,13 +501,22 @@ public class ClientBlockStateColorCache
// use the level logic only if requested
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
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState,
new TintGetterOverrideSmooth(this.level, biome, fullDataSource), // TODO can this object be cached?
McObjectConverter.Convert(pos),
this.tintIndex);
TintGetterOverride tintOverride = TintOverrideGetter.get();
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
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)
@@ -519,7 +524,7 @@ public class ClientBlockStateColorCache
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] 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);
}
}
@@ -543,7 +548,7 @@ public class ClientBlockStateColorCache
// helper classes //
//================//
enum ColorMode
private enum EColorMode
{
Default,
Flower,
@@ -551,7 +556,7 @@ public class ClientBlockStateColorCache
Chisel,
Glass;
static ColorMode getColorMode(Block block)
static EColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower;
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.*;
@@ -38,9 +39,9 @@ import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideSmooth extends AbstractDhTintGetter
public class TintGetterOverride extends AbstractDhTintGetter
{
private final LevelReader parent;
private LevelReader parent;
@@ -48,9 +49,11 @@ public class TintGetterOverrideSmooth extends AbstractDhTintGetter
// constructor //
//=============//
public TintGetterOverrideSmooth(LevelReader parent, BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
{
super(biomeWrapper, fullDataSource);
public TintGetterOverride() { }
public void update(LevelReader parent, BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
{
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
this.parent = parent;
}
@@ -20,23 +20,25 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos;
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.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import org.jetbrains.annotations.Nullable;
public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
public class TintWithoutLevelOverrider extends AbstractDhTintGetter
{
//=============//
// constructor //
//=============//
public TintWithoutLevelSmoothOverrider(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
{ super(biomeWrapper, fullDataSource); }
public TintWithoutLevelOverrider()
{ }
@@ -46,21 +48,21 @@ public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
@Override
public float getShade(Direction direction, boolean shade)
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); }
@Override
public LevelLightEngine getLightEngine()
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); }
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only."); }
@Override
public BlockState getBlockState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
@Override
public FluidState getFluidState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only."); }
//==============//
@@ -71,7 +73,7 @@ public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
@Override
public int getHeight()
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#if MC_VER < MC_1_21_3
@Override
@@ -80,7 +82,7 @@ public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
#else
@Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
#endif
@@ -38,7 +38,7 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.*;
@@ -75,7 +75,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
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. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
@@ -7,31 +7,22 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.regex.Pattern;
// Logger (for debug stuff)
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.common.wrappers.gui.config.ConfigGuiInfo;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.config.types.*;
import com.seibel.distanthorizons.common.wrappers.gui.updater.ChangelogScreen;
// Uses https://github.com/TheElectronWill/night-config for toml (only for Fabric since Forge already includes this)
// Gets info from our own mod
// Minecraft imports
import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosition;
import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
import com.seibel.distanthorizons.core.logging.SpamReducedLogger;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.AnnotationUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
@@ -47,9 +38,9 @@ import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.LogManager;
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_20_1
@@ -81,8 +72,10 @@ import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.Translata
@SuppressWarnings("unchecked")
public class ClassicConfigGUI
{
private static final Logger LOGGER = LogManager.getLogger();
public static final SpamReducedLogger SPAM_LOGGER = new SpamReducedLogger(4);
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder()
.maxCountPerSecond(1)
.build();
public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface();
@@ -111,16 +104,6 @@ public class ClassicConfigGUI
}
// TODO
/** The terribly coded old stuff */
public static class EntryInfo
{
Object widget;
Map.Entry<EditBox, Component> error;
String tempValue;
int index;
}
//==============//
@@ -128,10 +111,9 @@ public class ClassicConfigGUI
//==============//
/** if you want to get this config gui's screen call this */
public static Screen getScreen(ConfigBase configBase, Screen parent, String category)
{ return new ConfigScreen(configBase, parent, category); }
public static Screen getScreen(Screen parent, String category)
{ return new ConfigScreen(parent, category); }
/** Pain */
private static class ConfigScreen extends DhScreen
{
private static final ILangWrapper LANG_WRAPPER = SingletonInjector.INSTANCE.get(ILangWrapper.class);
@@ -139,8 +121,6 @@ public class ClassicConfigGUI
private static final String TRANSLATION_PREFIX = ModInfo.ID + ".config.";
private final ConfigBase configBase;
private final Screen parent;
private final String category;
private ConfigListWidget configListWidget;
@@ -154,14 +134,13 @@ public class ClassicConfigGUI
// constructor //
//=============//
protected ConfigScreen(ConfigBase configBase, Screen parent, String category)
protected ConfigScreen(Screen parent, String category)
{
super(Translatable(
LANG_WRAPPER.langExists(ModInfo.ID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ?
ModInfo.ID + ".config.title" :
ModInfo.ID + ".config" + (category.isEmpty() ? "" : "." + category) + ".title")
);
this.configBase = configBase;
this.parent = parent;
this.category = category;
}
@@ -182,7 +161,7 @@ public class ClassicConfigGUI
super.init();
if (!this.reload)
{
ConfigBase.INSTANCE.configFileHandler.loadFromFile();
ConfigHandler.INSTANCE.configFileHandler.loadFromFile();
}
// Changelog button
@@ -229,7 +208,7 @@ public class ClassicConfigGUI
ConfigScreenConfigs.OPTION_FIELD_WIDTH, ConfigScreenConfigs.OPTION_FIELD_HEIGHT,
(button) ->
{
ConfigBase.INSTANCE.configFileHandler.loadFromFile();
ConfigHandler.INSTANCE.configFileHandler.loadFromFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
}));
@@ -240,7 +219,7 @@ public class ClassicConfigGUI
ConfigScreenConfigs.OPTION_FIELD_WIDTH, ConfigScreenConfigs.OPTION_FIELD_HEIGHT,
(button) ->
{
ConfigBase.INSTANCE.configFileHandler.saveToFile();
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
}));
@@ -255,22 +234,22 @@ public class ClassicConfigGUI
this.addWidget(this.configListWidget);
for (AbstractConfigType info : ConfigBase.INSTANCE.entries)
for (AbstractConfigBase<?> configEntry : ConfigHandler.INSTANCE.configBaseList)
{
try
{
if (info.getCategory().matches(this.category)
&& info.getAppearance().showInGui)
if (configEntry.getCategory().matches(this.category)
&& configEntry.getAppearance().showInGui)
{
this.addMenuItem(info);
this.addMenuItem(configEntry);
}
}
catch (Exception e)
{
String message = "ERROR: Failed to show [" + info.getNameWCategory() + "], error: ["+e.getMessage()+"]";
if (info.get() != null)
String message = "ERROR: Failed to show [" + configEntry.getNameAndCategory() + "], error: ["+e.getMessage()+"]";
if (configEntry.get() != null)
{
message += " with the value [" + info.get() + "] with type [" + info.getType() + "]";
message += " with the value [" + configEntry.get() + "] with type [" + configEntry.getType() + "]";
}
LOGGER.error(message, e);
@@ -279,115 +258,63 @@ public class ClassicConfigGUI
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
}
private void addMenuItem(AbstractConfigType configType)
private void addMenuItem(AbstractConfigBase<?> configEntry)
{
trySetupConfigEntry(configType);
trySetupConfigEntry(configEntry);
if (this.tryCreateInputField(configType)) return;
if (this.tryCreateCategoryButton(configType)) return;
if (this.tryCreateButton(configType)) return;
if (this.tryCreateComment(configType)) return;
if (this.tryCreateSpacer(configType)) return;
if (this.tryCreateLinkedEntry(configType)) return;
if (this.tryCreateInputField(configEntry)) return;
if (this.tryCreateCategoryButton(configEntry)) return;
if (this.tryCreateButton(configEntry)) return;
if (this.tryCreateComment(configEntry)) return;
if (this.tryCreateSpacer(configEntry)) return;
if (this.tryCreateLinkedEntry(configEntry)) return;
LOGGER.warn("Config [" + configType.getNameWCategory() + "] failed to show. Please try something like changing its type.");
LOGGER.warn("Config [" + configEntry.getNameAndCategory() + "] failed to show. Please try something like changing its type.");
}
private static void trySetupConfigEntry(AbstractConfigType configType)
private static void trySetupConfigEntry(AbstractConfigBase<?> configMenuOption)
{
configType.guiValue = new EntryInfo();
Class<?> fieldClass = configType.getType();
configMenuOption.guiValue = new ConfigGuiInfo();
Class<?> configValueClass = configMenuOption.getType();
if (configType instanceof ConfigEntry)
if (configMenuOption instanceof ConfigEntry)
{
ConfigEntry configEntry = (ConfigEntry) configType;
ConfigEntry<?> configEntry = (ConfigEntry<?>) configMenuOption;
if (fieldClass == Integer.class)
if (configValueClass == Integer.class)
{
// For int
setupEntryInfoTextField(configEntry, Integer::parseInt, INTEGER_ONLY_REGEX, true);
setupTextMenuOption(configEntry, Integer::parseInt, INTEGER_ONLY_REGEX, true);
}
else if (fieldClass == Double.class)
else if (configValueClass == Double.class)
{
// For double
setupEntryInfoTextField(configEntry, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
setupTextMenuOption(configEntry, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
}
else if (fieldClass == String.class || fieldClass == List.class)
else if (configValueClass == String.class || configValueClass == List.class)
{
// For string or list
setupEntryInfoTextField(configEntry, String::length, null, true);
setupTextMenuOption(configEntry, String::length, null, true);
}
else if (fieldClass == Boolean.class)
else if (configValueClass == Boolean.class)
{
// For boolean
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
((EntryInfo) configEntry.guiValue).widget =
new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(
(button) ->
{
button.active = !configEntry.apiIsOverriding();
configEntry.uiSetWithoutSaving(!(Boolean) configEntry.get());
button.setMessage(func.apply(configEntry.get()));
}, func);
ConfigEntry<Boolean> booleanConfigEntry = (ConfigEntry<Boolean>) configEntry;
setupBooleanMenuOption(booleanConfigEntry);
}
else if (fieldClass.isEnum())
else if (configValueClass.isEnum())
{
// For enum
List<?> values = Arrays.asList(configEntry.getType().getEnumConstants());
Function<Object, Component> func = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + fieldClass.getSimpleName() + "." + configEntry.get().toString());
((EntryInfo) configEntry.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>((button) ->
{
// get the currently selected enum and enum index
int startingIndex = values.indexOf(configEntry.get());
Enum<?> enumValue = (Enum<?>) values.get(startingIndex);
// search for the next enum that is selectable
int index = startingIndex + 1;
index = (index >= values.size()) ? 0 : index;
while (index != startingIndex)
{
enumValue = (Enum<?>) values.get(index);
if (!AnnotationUtil.doesEnumHaveAnnotation(enumValue, DisallowSelectingViaConfigGui.class))
{
// this enum shouldn't be selectable via the UI,
// skip it
break;
}
index++;
index = (index >= values.size()) ? 0 : index;
}
if (index == startingIndex)
{
// none of the enums should be selectable, this is a programmer error
enumValue = (Enum<?>) values.get(startingIndex);
LOGGER.warn("Enum [" + enumValue.getClass() + "] doesn't contain any values that should be selectable via the UI, sticking to the currently selected value [" + enumValue + "].");
}
((ConfigEntry<Enum<?>>) configEntry).uiSetWithoutSaving(enumValue);
if (configEntry.getApiValue() != null)
{
button.active = false;
}
else
{
button.active = true;
}
button.setMessage(func.apply(configEntry.get()));
}, func);
ConfigEntry<Enum<?>> enumConfigEntry = (ConfigEntry<Enum<?>>) configEntry;
Class<? extends Enum<?>> configEnumClass = (Class<? extends Enum<?>>) configValueClass;
setupEnumMenuOption(enumConfigEntry, configEnumClass);
}
}
}
private static void setupEntryInfoTextField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast)
private static void setupTextMenuOption(AbstractConfigBase<?> configMenuOption, Function<String, Number> parsingFunc, @Nullable Pattern pattern, boolean cast)
{
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configMenuOption.guiValue);
configGuiInfo.tooltipFunction =
(editBox, button) ->
(stringValue) ->
{
boolean isNumber = (pattern != null);
@@ -398,67 +325,137 @@ public class ClassicConfigGUI
}
Number value = info.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception)
((EntryInfo) info.guiValue).error = null;
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals("."))
Number numberValue = configMenuOption.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception)
configGuiInfo.errorMessage = null;
if (isNumber
&& !stringValue.isEmpty()
&& !stringValue.equals("-")
&& !stringValue.equals("."))
{
ConfigEntry<Number> numberConfigEntry = (ConfigEntry<Number>) configMenuOption;
try
{
value = func.apply(stringValue);
numberValue = parsingFunc.apply(stringValue);
}
catch (Exception e)
{
value = null;
numberValue = null;
}
byte isValid = ((ConfigEntry) info).isValid(value);
switch (isValid)
EConfigValidity validity = numberConfigEntry.getValidity(numberValue);
switch (validity)
{
case 0:
((EntryInfo) info.guiValue).error = null;
case VALID:
configGuiInfo.errorMessage = null;
break;
case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin()));
case NUMBER_TOO_LOW:
configGuiInfo.errorMessage = TextOrTranslatable("§cMinimum length is " + numberConfigEntry.getMin());
break;
case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax()));
case NUMBER_TOO_HIGH:
configGuiInfo.errorMessage = TextOrTranslatable("§cMaximum length is " + numberConfigEntry.getMax());
break;
case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid"));
case INVALID:
configGuiInfo.errorMessage = TextOrTranslatable("§cValue is invalid");
break;
}
}
((EntryInfo) info.guiValue).tempValue = stringValue;
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777); // white and red
editBox.setTextColor(((ConfigEntry<Number>) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID ? 0xFFFFFFFF : 0xFFFF7777); // white and red
if (info.getType() == String.class
|| info.getType() == List.class)
if (configMenuOption.getType() == String.class
|| configMenuOption.getType() == List.class)
{
((ConfigEntry) info).uiSetWithoutSaving(stringValue);
((ConfigEntry<String>) configMenuOption).uiSetWithoutSaving(stringValue);
}
else if (((ConfigEntry) info).isValid(value) == 0)
else if (((ConfigEntry<Number>) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID)
{
if (!cast)
{
((ConfigEntry) info).uiSetWithoutSaving(value);
((ConfigEntry<Number>) configMenuOption).uiSetWithoutSaving(numberValue);
}
else
{
((ConfigEntry) info).uiSetWithoutSaving(value != null ? value.intValue() : 0);
((ConfigEntry<Number>) configMenuOption).uiSetWithoutSaving(numberValue != null ? numberValue.intValue() : 0);
}
}
return true;
};
}
private boolean tryCreateInputField(AbstractConfigType configType)
private static void setupBooleanMenuOption(ConfigEntry<Boolean> booleanConfigEntry)
{
if (configType instanceof ConfigEntry)
// For boolean
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) booleanConfigEntry.guiValue);
configGuiInfo.buttonOptionMap =
new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(
(button) ->
{
button.active = !booleanConfigEntry.apiIsOverriding();
booleanConfigEntry.uiSetWithoutSaving(!booleanConfigEntry.get());
button.setMessage(func.apply(booleanConfigEntry.get()));
}, func);
}
private static void setupEnumMenuOption(ConfigEntry<Enum<?>> enumConfigEntry, Class<? extends Enum<?>> enumClass)
{
List<Enum<?>> enumList = Arrays.asList(enumClass.getEnumConstants());
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) enumConfigEntry.guiValue);
Function<Object, Component> getEnumTranslatableFunc = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + enumClass.getSimpleName() + "." + enumConfigEntry.get().toString());
configGuiInfo.buttonOptionMap =
new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(
(button) ->
{
ConfigEntry configEntry = (ConfigEntry) configType;
// get the currently selected enum and enum index
int startingIndex = enumList.indexOf(enumConfigEntry.get());
Enum<?> enumValue = enumList.get(startingIndex);
// search for the next enum that is selectable
int index = startingIndex + 1;
index = (index >= enumList.size()) ? 0 : index;
while (index != startingIndex)
{
enumValue = enumList.get(index);
if (!AnnotationUtil.doesEnumHaveAnnotation(enumValue, DisallowSelectingViaConfigGui.class))
{
// this enum shouldn't be selectable via the UI,
// skip it
break;
}
index++;
index = (index >= enumList.size()) ? 0 : index;
}
if (index == startingIndex)
{
// none of the enums should be selectable, this is a programmer error
enumValue = enumList.get(startingIndex);
LOGGER.warn("Enum [" + enumValue.getClass() + "] doesn't contain any values that should be selectable via the UI, sticking to the currently selected value [" + enumValue + "].");
}
enumConfigEntry.uiSetWithoutSaving(enumValue);
button.active = !enumConfigEntry.apiIsOverriding();
button.setMessage(getEnumTranslatableFunc.apply(enumConfigEntry.get()));
}, getEnumTranslatableFunc);
}
private boolean tryCreateInputField(AbstractConfigBase<?> configBase)
{
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue);
if (configBase instanceof ConfigEntry)
{
ConfigEntry configEntry = (ConfigEntry) configBase;
//==============//
@@ -468,7 +465,6 @@ public class ClassicConfigGUI
Button.OnPress btnAction = (button) ->
{
configEntry.uiSetWithoutSaving(configEntry.getDefaultValue());
((EntryInfo) configEntry.guiValue).index = 0;
this.reload = true;
Objects.requireNonNull(this.minecraft).setScreen(this);
};
@@ -509,11 +505,11 @@ public class ClassicConfigGUI
- ConfigScreenConfigs.OPTION_FIELD_WIDTH;
int optionFieldPosZ = 0;
if (((EntryInfo) configEntry.guiValue).widget instanceof Map.Entry)
if (configGuiInfo.buttonOptionMap != null)
{
// enum/multi option input button
Map.Entry<Button.OnPress, Function<Object, Component>> widget = (Map.Entry<Button.OnPress, Function<Object, Component>>) ((EntryInfo) configEntry.guiValue).widget;
Map.Entry<Button.OnPress, Function<Object, Component>> widget = configGuiInfo.buttonOptionMap;
if (configEntry.getType().isEnum())
{
widget.setValue((value) -> Translatable(TRANSLATION_PREFIX + "enum." + configEntry.getType().getSimpleName() + "." + configEntry.get().toString()));
@@ -537,7 +533,7 @@ public class ClassicConfigGUI
return true;
}
else if (((EntryInfo) configEntry.guiValue).widget != null)
else
{
// text box input
@@ -548,7 +544,7 @@ public class ClassicConfigGUI
widget.setMaxLength(ConfigScreenConfigs.OPTION_FIELD_WIDTH);
widget.insertText(String.valueOf(configEntry.get()));
Predicate<String> processor = ((BiFunction<EditBox, Button, Predicate<String>>) ((EntryInfo) configEntry.guiValue).widget).apply(widget, this.doneButton);
Predicate<String> processor = configGuiInfo.tooltipFunction.apply(widget, this.doneButton);
widget.setFilter(processor);
this.configListWidget.addButton(this, configEntry, widget, resetButton, null, textComponent);
@@ -559,7 +555,7 @@ public class ClassicConfigGUI
return false;
}
private boolean tryCreateCategoryButton(AbstractConfigType configType)
private boolean tryCreateCategoryButton(AbstractConfigBase<?> configType)
{
if (configType instanceof ConfigCategory)
{
@@ -575,8 +571,8 @@ public class ClassicConfigGUI
ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT,
((button) ->
{
ConfigBase.INSTANCE.configFileHandler.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(ClassicConfigGUI.getScreen(this.configBase, this, configCategory.getDestination()));
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination()));
}));
this.configListWidget.addButton(this, configType, widget, null, null, null);
@@ -585,7 +581,7 @@ public class ClassicConfigGUI
return false;
}
private boolean tryCreateButton(AbstractConfigType configType)
private boolean tryCreateButton(AbstractConfigBase<?> configType)
{
if (configType instanceof ConfigUIButton)
{
@@ -606,7 +602,7 @@ public class ClassicConfigGUI
return false;
}
private boolean tryCreateComment(AbstractConfigType configType)
private boolean tryCreateComment(AbstractConfigBase<?> configType)
{
if (configType instanceof ConfigUIComment)
{
@@ -625,7 +621,7 @@ public class ClassicConfigGUI
return false;
}
private boolean tryCreateSpacer(AbstractConfigType configType)
private boolean tryCreateSpacer(AbstractConfigBase<?> configType)
{
if (configType instanceof ConfigUISpacer)
{
@@ -642,7 +638,7 @@ public class ClassicConfigGUI
return false;
}
private boolean tryCreateLinkedEntry(AbstractConfigType configType)
private boolean tryCreateLinkedEntry(AbstractConfigBase<?> configType)
{
if (configType instanceof ConfigUiLinkedEntry)
{
@@ -654,8 +650,8 @@ public class ClassicConfigGUI
return false;
}
private Component GetTranslatableTextComponentForConfig(AbstractConfigType configType)
{ return Translatable(TRANSLATION_PREFIX + configType.getNameWCategory());}
private Component GetTranslatableTextComponentForConfig(AbstractConfigBase<?> configType)
{ return Translatable(TRANSLATION_PREFIX + configType.getNameAndCategory());}
@@ -729,33 +725,32 @@ public class ClassicConfigGUI
}
Component text = ButtonEntry.TEXT_BY_WIDGET.get(hoveredWidget);
ButtonEntry button = ButtonEntry.BUTTON_BY_WIDGET.get(hoveredWidget);
// A quick fix for tooltips on linked entries
AbstractConfigType dhConfigType = ConfigUiLinkedEntry.class.isAssignableFrom(button.dhConfigType.getClass()) ?
AbstractConfigBase<?> configBase = ConfigUiLinkedEntry.class.isAssignableFrom(button.dhConfigType.getClass()) ?
((ConfigUiLinkedEntry) button.dhConfigType).get() :
button.dhConfigType;
boolean apiOverrideActive = false;
if (dhConfigType instanceof ConfigEntry)
if (configBase instanceof ConfigEntry)
{
apiOverrideActive = ((ConfigEntry)dhConfigType).apiIsOverriding();
apiOverrideActive = ((ConfigEntry<?>)configBase).apiIsOverriding();
}
Component name = Translatable(TRANSLATION_PREFIX + (dhConfigType.category.isEmpty() ? "" : dhConfigType.category + ".") + dhConfigType.getName());
String key = TRANSLATION_PREFIX + (dhConfigType.category.isEmpty() ? "" : dhConfigType.category + ".") + dhConfigType.getName() + ".@tooltip";
String key = TRANSLATION_PREFIX + (configBase.category.isEmpty() ? "" : configBase.category + ".") + configBase.getName() + ".@tooltip";
if (apiOverrideActive)
{
key = "distanthorizons.general.disabledByApi.@tooltip";
}
// display the validation error if present
if (((EntryInfo) dhConfigType.guiValue).error != null)
// display the validation error tooltip if present
final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue);
if (configGuiInfo.errorMessage != null)
{
this.DhRenderTooltip(matrices, this.font, ((EntryInfo) dhConfigType.guiValue).error.getValue(), mouseX, mouseY);
this.DhRenderTooltip(matrices, this.font, configGuiInfo.errorMessage, mouseX, mouseY);
}
// display the tooltip if present
else if (LANG_WRAPPER.langExists(key))
@@ -781,7 +776,7 @@ public class ClassicConfigGUI
@Override
public void onClose()
{
ConfigBase.INSTANCE.configFileHandler.saveToFile();
ConfigHandler.INSTANCE.configFileHandler.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
@@ -792,9 +787,9 @@ public class ClassicConfigGUI
//================//
// helper classes //
//================//
public static class ConfigListWidget extends ContainerObjectSelectionList<ButtonEntry>
{
@@ -812,7 +807,7 @@ public class ClassicConfigGUI
this.textRenderer = minecraftClient.font;
}
public void addButton(ConfigScreen gui, AbstractConfigType dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text)
public void addButton(ConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text)
{ this.addEntry(new ButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton)); }
@Override
@@ -858,7 +853,7 @@ public class ClassicConfigGUI
private final AbstractWidget button;
private final ConfigScreen gui;
private final AbstractConfigType dhConfigType;
private final AbstractConfigBase dhConfigType;
private final AbstractWidget resetButton;
private final AbstractWidget indexButton;
@@ -869,12 +864,12 @@ public class ClassicConfigGUI
private final EConfigCommentTextPosition textPosition;
public static final Map<AbstractWidget, Component> TEXT_BY_WIDGET = new HashMap<>();
// TODO we should just use a wrapper or something
/// TODO we should just use a wrapper or something
public static final Map<AbstractWidget, ButtonEntry> BUTTON_BY_WIDGET = new HashMap<>();
public ButtonEntry(ConfigScreen gui, AbstractConfigType dhConfigType,
public ButtonEntry(ConfigScreen gui, AbstractConfigBase dhConfigType,
AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton)
{
TEXT_BY_WIDGET.put(button, text);
@@ -920,7 +915,7 @@ public class ClassicConfigGUI
@Override
#if MC_VER < MC_1_20_1
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
#else
public void renderContent(GuiGraphics matrices, int mouseX, int mouseY, boolean hovered, float tickDelta)
@@ -928,9 +923,9 @@ public class ClassicConfigGUI
{
try
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
#else
int y = this.getY(); // TODO why is the Y value being set during render?
int y = this.getY(); /// TODO why is the Y value being set during render?
#endif
if (this.button != null)
@@ -1008,7 +1003,7 @@ public class ClassicConfigGUI
catch (Exception e)
{
// 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);
}
}
@@ -1028,13 +1023,11 @@ public class ClassicConfigGUI
//================//
// event handling //
//================//
private static class ConfigCoreInterface implements IConfigGui
public static class ConfigCoreInterface implements IConfigGui
{
/**
* in the future it would be good to pass in the current page and other variables,
@@ -1,6 +1,6 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// not supported for older MC versions
#else
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
@@ -19,7 +19,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
#endif
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// not supported for older MC versions
public class DhDebugScreenEntry
{}
@@ -1,17 +1,15 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen;
import net.minecraft.client.gui.screens.Screen;
import org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandles;
import com.seibel.distanthorizons.core.logging.DhLogger;
public class GetConfigScreen
{
protected static final Logger LOGGER = DhLoggerBuilder.getLogger();
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public static EType useScreen = EType.Classic;
@@ -28,7 +26,7 @@ public class GetConfigScreen
// which won't be for sure added until we request a GUI
if (ModInfo.IS_DEV_BUILD)
{
String missingLangEntries = ConfigBase.INSTANCE.generateLang(true, true);
String missingLangEntries = ConfigHandler.INSTANCE.generateLang(true, true);
// trim to remove any newlines/spaces
// that may be present when no lang entries need changing
@@ -45,7 +43,7 @@ public class GetConfigScreen
switch (useScreen)
{
case Classic:
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
return ClassicConfigGUI.getScreen(parent, "client");
case JavaSwing:
//return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
@@ -39,7 +39,7 @@ public class MinecraftScreen
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
{
super(translate(translationName));
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
#else
screen.minecraftWindow = Minecraft.getInstance().getWindow().handle();
@@ -0,0 +1,33 @@
package com.seibel.distanthorizons.common.wrappers.gui.config;
import com.seibel.distanthorizons.core.config.gui.IConfigGuiInfo;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.EditBox;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;
import java.util.AbstractMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* holds information needed by the config GUI for rendering.
*
* @see AbstractConfigBase
*/
public class ConfigGuiInfo implements IConfigGuiInfo
{
/**
* Used to display validation errors.
* Null if no error is present.
*/
@Nullable
public Component errorMessage;
public BiFunction<EditBox, Button, Predicate<String>> tooltipFunction;
/** determines which options the button will show */
public AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>> buttonOptionMap;
}
@@ -14,7 +14,7 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.Screen;
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
import net.minecraft.client.gui.narration.NarratableEntry;
@@ -41,7 +41,7 @@ import java.util.*;
// TODO: Make this
public class ChangelogScreen extends DhScreen
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent;
@@ -262,7 +262,7 @@ public class ChangelogScreen extends DhScreen
@Override
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{ GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); }
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
@Override
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
@@ -16,7 +16,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
#endif
import net.minecraft.client.gui.screens.Screen;
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.*;
@@ -31,7 +31,7 @@ import java.util.*;
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
public class UpdateModScreen extends DhScreen
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private Screen parent;
@@ -36,9 +36,9 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
@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.enabled = true;
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.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
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 */
private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey)
//=============//
// constructor //
//=============//
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
{
super(level);
this.serverKey = serverKey;
this.serverLevelKey = serverLevelKey;
}
@Override
public String getServerKey() { return this.serverKey; }
//======================//
// level identification //
//======================//
@Override
public String getServerLevelKey() { return this.serverLevelKey; }
@@ -27,4 +39,6 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
@Override
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.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_21_3
@@ -72,7 +72,7 @@ import net.minecraft.util.profiling.Profiler;
*/
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();
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
@@ -158,9 +158,17 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
@Override
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
public boolean clientConnectedToDedicatedServer()
{
return MINECRAFT.getCurrentServer() != null
&& !this.hasSinglePlayerServer();
}
@Override
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
public boolean connectedToReplay()
{
return MINECRAFT.getCurrentServer() == null
&& !this.hasSinglePlayerServer() ;
}
@Override
public String getCurrentServerName()
@@ -315,7 +323,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#else
@@ -28,7 +28,7 @@ import com.mojang.blaze3d.opengl.GlStateManager;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
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;
@@ -54,7 +54,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
{
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.Minecraft;
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
import net.minecraft.tags.FluidTags;
import net.minecraft.world.entity.Entity;
@@ -67,9 +73,6 @@ import org.lwjgl.opengl.GL15;
#else
import net.minecraft.world.level.material.FogType;
#endif
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
#if MC_VER >= MC_1_21_5
import com.mojang.blaze3d.opengl.GlTexture;
@@ -84,7 +87,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
{
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 IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
@@ -318,7 +321,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@Override
public int getTargetFrameBuffer()
public int getTargetFramebuffer()
{
// used so we can access the framebuffer shaders end up rendering to
if (AbstractOptifineAccessor.optifinePresent())
@@ -399,9 +402,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@Override
public int getTargetFrameBufferViewportWidth()
public int getTargetFramebufferViewportWidth()
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
return this.getRenderTarget().viewWidth;
#else
return this.getRenderTarget().width;
@@ -409,9 +412,9 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@Override
public int getTargetFrameBufferViewportHeight()
public int getTargetFramebufferViewportHeight()
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
return this.getRenderTarget().viewHeight;
#else
return this.getRenderTarget().height;
@@ -419,7 +422,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
}
@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
public boolean isFogStateSpecial()
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
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 java.nio.ByteBuffer;
@@ -32,7 +32,7 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper
{
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;
@@ -2,7 +2,6 @@ package com.seibel.distanthorizons.common.wrappers.world;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
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.block.ClientBlockStateColorCache;
@@ -25,8 +24,7 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -54,7 +52,7 @@ import com.seibel.distanthorizons.core.util.ColorUtil;
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
* 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 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 */
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 waterBlockWrapper;
private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
private IDhLevel dhLevel;
@@ -99,6 +94,11 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Nullable
public static IClientLevelWrapper getWrapperIfDifferent(@Nullable IClientLevelWrapper levelWrapper, @NotNull ClientLevel level)
{
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != levelWrapper)
{
return getWrapper(level);
}
ClientLevelWrapper clientLevelWrapper = (ClientLevelWrapper)levelWrapper;
if (clientLevelWrapper == null
|| clientLevelWrapper.level != level)
@@ -163,13 +163,17 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{
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();
// 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;
// TODO: Surely there is a more efficient way to write this code
for (ServerLevel serverLevel : serverLevels)
{
if (serverLevel.dimension() == this.level.dimension())
@@ -195,16 +199,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================//
@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(
((BlockStateWrapper) blockWrapper).blockState,
this.cachedBlockColorCacheFunction);
ClientBlockStateColorCache blockColorCache = this.blockColorCacheByBlockState.get(((BlockStateWrapper) blockWrapper).blockState);
if (blockColorCache == null)
{
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
@@ -227,48 +233,8 @@ public class ClientLevelWrapper implements IClientLevelWrapper
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
public void clearBlockColorCache() { this.blockCache.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;
}
public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
@Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@@ -326,20 +292,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
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
public ClientLevel getWrappedMcObject() { return this.level; }
@@ -347,18 +299,18 @@ public class ClientLevelWrapper implements IClientLevelWrapper
public void onUnload()
{
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null;
this.dhLevel = null;
}
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
if (this.dhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
return this.dhLevel.getSaveStructure().getSaveFolder(this);
}
@@ -369,17 +321,19 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//===================//
@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
public IDhApiCustomRenderRegister getRenderRegister()
{
if (this.parentDhLevel == null)
if (this.dhLevel == null)
{
return null;
}
return this.parentDhLevel.getGenericRenderer();
return this.dhLevel.getGenericRenderer();
}
@Override
@@ -24,21 +24,18 @@ import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
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.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.level.IDhLevel;
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.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
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 net.minecraft.server.level.ServerLevel;
@@ -52,16 +49,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
#if MC_VER < MC_1_21_3
#else
import java.nio.file.Path;
#endif
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
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
* 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 final ServerLevel level;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
private IDhLevel dhLevel;
/**
* 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();
}
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
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
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
if (Config.Server.sendLevelKeys.get())
{
String levelKeyPrefix = Config.Server.levelKeyPrefix.get();
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
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@@ -180,26 +226,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
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
public ServerLevel getWrappedMcObject() { return this.level; }
@@ -208,28 +234,31 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@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
public IDhApiCustomRenderRegister getRenderRegister()
{
if (this.parentDhLevel == null)
if (this.dhLevel == null)
{
return null;
}
return this.parentDhLevel.getGenericRenderer();
return this.dhLevel.getGenericRenderer();
}
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
if (this.dhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
return this.dhLevel.getSaveStructure().getSaveFolder(this);
}
@@ -29,8 +29,8 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
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.objects.EventTimer;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -42,6 +42,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.Abstrac
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -71,15 +73,17 @@ import net.minecraft.world.level.levelgen.FlatLevelSource;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager;
#if MC_VER <= MC_1_17_1
#elif MC_VER <= MC_1_19_2
import net.minecraft.core.Registry;
#else
#elif MC_VER <= MC_1_19_4
import net.minecraft.core.registries.Registries;
#elif MC_VER < MC_1_21_3
import net.minecraft.core.registries.Registries;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
#else
@@ -101,28 +105,32 @@ Lod Generation: 0.269023348s
*/
public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnvironmentWrapper
{
public static final ConfigBasedSpamLogger PREF_LOGGER =
new ConfigBasedSpamLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Common.Logging.logWorldGenPerformance.get(), 1);
public static final ConfigBasedLogger EVENT_LOGGER =
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Common.Logging.logWorldGenEvent.get());
public static final ConfigBasedLogger LOAD_LOGGER =
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Common.Logging.logWorldGenLoadEvent.get());
public static final DhLogger PREF_LOGGER = new DhLoggerBuilder()
.name("LOD World Gen")
.fileLevelConfig(Config.Common.Logging.logWorldGenPerformanceToFile)
.maxCountPerSecond(1)
.build();
public static final DhLogger EVENT_LOGGER = new DhLoggerBuilder()
.name("LOD World Gen")
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
.build();
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
.name("LOD World Gen")
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
.build();
#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_10
#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 */0);
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
#endif
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
private final IDhServerLevel serverlevel;
private final IDhServerLevel serverLevel;
/**
* will be true if C2ME is installed (since they require us to
@@ -214,16 +222,16 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
}
public BatchGenerationEnvironment(IDhServerLevel serverlevel)
public BatchGenerationEnvironment(IDhServerLevel serverLevel)
{
super(serverlevel);
this.serverlevel = serverlevel;
super(serverLevel);
this.serverLevel = serverLevel;
EVENT_LOGGER.info("================WORLD_GEN_STEP_INITING=============");
serverlevel.getServerLevelWrapper().getDimensionType();
serverLevel.getServerLevelWrapper().getDimensionType();
ChunkGenerator generator = ((ServerLevelWrapper) (serverlevel.getServerLevelWrapper())).getLevel().getChunkSource().getGenerator();
ChunkGenerator generator = ((ServerLevelWrapper) (serverLevel.getServerLevelWrapper())).getLevel().getChunkSource().getGenerator();
if (!(generator instanceof NoiseBasedChunkGenerator ||
generator instanceof DebugLevelSource ||
generator instanceof FlatLevelSource))
@@ -251,7 +259,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
this.pullExistingChunkUsingMcAsyncMethod = true;
}
this.params = new GlobalParameters(serverlevel);
this.params = new GlobalParameters(serverLevel);
}
@@ -311,7 +319,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
try
{
event.future.get(); // Should throw exception
LodUtil.assertNotReach();
LodUtil.assertNotReach("Exceptionally completed world gen Future should have thrown an exception.");
}
catch (Exception e)
{
@@ -465,7 +473,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
else if (chunk != null)
{
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.serverlevel.getLevelWrapper());
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.serverLevel.getLevelWrapper());
chunkWrapperList.set(relX, relZ, chunkWrapper);
// try setting the wrapper's lighting
@@ -523,10 +531,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
genEvent.timer.complete();
genEvent.refreshTimeout();
if (PREF_LOGGER.canMaybeLog())
if (PREF_LOGGER.canLog())
{
genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.debugInc(genEvent.timer.toString());
PREF_LOGGER.debug(genEvent.timer.toString());
}
}
catch (Exception e)
@@ -675,15 +683,28 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
actualThrowable = completionException.getCause();
}
LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
// interrupts mean the world generator is being shut down, no need to log that
boolean isShutdownException =
actualThrowable instanceof InterruptedException
|| actualThrowable instanceof ClosedByInterruptException;
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)
{
LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
return CompletableFuture.completedFuture(null);
}
}
@@ -699,7 +720,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{
try
{
LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
CHUNK_LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
@Nullable
ChunkAccess chunk = ChunkFileReader.read(level, chunkPos, chunkData);
@@ -721,7 +742,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
catch (Exception e)
{
LOAD_LOGGER.error(
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" +
@@ -744,7 +765,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
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_10
#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);
@@ -783,12 +804,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (throwable != null)
{
LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
}
if (chunk != null)
{
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.serverlevel.getLevelWrapper());
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.serverLevel.getLevelWrapper());
chunkWrappersByDhPos.put(new DhChunkPos(chunkPos.x, chunkPos.z), chunkWrapper);
}
}, runnableQueue::add);
@@ -802,7 +823,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{
// generate chunk lighting using DH's lighting engine
genEvent.timer.nextEvent("light");
int maxSkyLight = this.serverlevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
int maxSkyLight = this.serverLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
ArrayList<IChunkWrapper> generatedChunks = new ArrayList<>(chunkWrappersByDhPos.values());
for (IChunkWrapper iChunkWrapper : generatedChunks)
@@ -815,7 +836,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(iChunkWrapper, generatedChunks, maxSkyLight);
}
this.serverlevel.updateBeaconBeamsForChunk(iChunkWrapper, generatedChunks);
this.serverLevel.updateBeaconBeamsForChunk(iChunkWrapper, generatedChunks);
}
genEvent.timer.nextEvent("cleanup");
@@ -838,10 +859,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
genEvent.timer.complete();
genEvent.refreshTimeout();
if (PREF_LOGGER.canMaybeLog())
if (PREF_LOGGER.canLog())
{
genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.debugInc(genEvent.timer.toString());
PREF_LOGGER.debug(genEvent.timer.toString());
}
});
@@ -1046,7 +1067,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// generate lighting using DH's lighting engine
int maxSkyLight = this.serverlevel.getServerLevelWrapper().hasSkyLight() ? 15 : 0;
int maxSkyLight = this.serverLevel.getServerLevelWrapper().hasSkyLight() ? 15 : 0;
// only light generated chunks,
// attempting to light un-generated chunks will cause lighting issues on bordering generated chunks
@@ -1082,7 +1103,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(centerChunk, iChunkWrapperList, maxSkyLight);
}
this.serverlevel.updateBeaconBeamsForChunk(centerChunk, iChunkWrapperList);
this.serverLevel.updateBeaconBeamsForChunk(centerChunk, iChunkWrapperList);
}
genEvent.refreshTimeout();
@@ -1118,9 +1139,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{
regionStorage.close();
}
catch (ClosedChannelException ignore) { /* world generator is being shut down */ }
catch (IOException e)
{
EVENT_LOGGER.error("Failed to close region file storage cache!", e);
EVENT_LOGGER.error("Failed to close region file storage cache, error: ["+e.getMessage()+"].", e);
}
}
@@ -32,11 +32,11 @@ 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 org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
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;
public final int id;
@@ -24,7 +24,7 @@ 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.logging.DhLogger;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
@@ -93,8 +93,10 @@ public class ChunkFileReader
{
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
private static final DhLogger LOGGER = BatchGenerationEnvironment.CHUNK_LOAD_LOGGER;
#if MC_VER >= MC_1_21_10
#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
@@ -108,7 +110,6 @@ public class ChunkFileReader
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;
@@ -263,7 +264,7 @@ public class ChunkFileReader
}
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// BLOCK_STATE_CODEC is created statically
// TODO clean up this code separation
#else
@@ -288,7 +289,7 @@ public class ChunkFileReader
#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_10
#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
@@ -363,7 +364,7 @@ public class ChunkFileReader
}
else
{
#if MC_VER < MC_1_21_10
#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();
@@ -403,7 +404,7 @@ public class ChunkFileReader
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomes.asHolderIdMap(),
biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
biomes.getOrThrow(Biomes.PLAINS),
PalettedContainer.Strategy.SECTION_BIOMES);
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.world.level.block.EntityBlock;
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.Nullable;
@@ -78,7 +78,7 @@ import net.minecraft.world.ticks.LevelTickAccess;
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;
@@ -352,7 +352,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
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;
}
@@ -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
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
LOGGER.info("WorldGen requiring [" + chunkStatus + "]"
+ " is outside the expected range. Returning EMPTY chunk.");
debugTriggeredForStatus = chunkStatus;
}
@@ -7,7 +7,7 @@ import net.minecraft.nbt.NbtIo;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.storage.RegionFile;
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 java.io.DataInputStream;
@@ -29,7 +29,7 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
*/
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 */
@Nullable
@@ -139,7 +139,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
if (retryCount >= maxRetryCount)
{
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "].");
BatchGenerationEnvironment.CHUNK_LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "].");
}
@@ -29,7 +29,7 @@ import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
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
import net.minecraft.world.level.chunk.ChunkStatus;
@@ -42,7 +42,7 @@ import java.util.ConcurrentModificationException;
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;
@@ -35,7 +35,7 @@ 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.ProtoChunk;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
@@ -46,7 +46,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
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 ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock();
+5 -3
View File
@@ -19,7 +19,9 @@ loom {
"-Dminecraft.api.session.host=https://nope.invalid",
"-Dminecraft.api.services.host=https://nope.invalid",
// https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels
"-Dio.netty.leakDetection.level=advanced"
"-Dio.netty.leakDetection.level=advanced",
"-XX:+UseZGC",
"-XX:+ZGenerational"
)
programArgs("--username", "Dev")
}
@@ -76,11 +78,11 @@ dependencies {
// Fabric API
addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version))
if (buildVersionBefore(minecraft_version, "1.21.10"))
if (buildVersionBefore(minecraft_version, "1.21.9"))
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
}
else // > 1.21.10
else // > 1.21.9
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-resource-loader-v1", rootProject.fabric_api_version))
@@ -60,7 +60,7 @@ import java.nio.FloatBuffer;
import java.util.HashSet;
import java.util.concurrent.AbstractExecutorService;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
#endif
@@ -68,7 +68,7 @@ import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.glfw.GLFW;
/**
@@ -85,7 +85,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
private final ClientApi clientApi = ClientApi.INSTANCE;
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 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
private static final int[] KEY_TO_CHECK_FOR = { GLFW.GLFW_KEY_F6, GLFW.GLFW_KEY_F8, GLFW.GLFW_KEY_P};
@@ -130,8 +130,16 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
{
if (MC.clientConnectedToDedicatedServer())
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
// executor to prevent locking up the render/event thread
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
});
}
}
});
@@ -145,8 +153,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(blockPos.getX(), blockPos.getZ()))
{
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
@@ -185,8 +191,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
if (SharedApi.isChunkAtBlockPosAlreadyUpdating(hitResult.getBlockPos().getX(), hitResult.getBlockPos().getZ()))
{
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
@@ -220,7 +224,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
//==============//
// TODO wait for fabric to re-add their rendering API
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
@@ -20,10 +20,8 @@
package com.seibel.distanthorizons.fabric;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -40,7 +38,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
#if MC_VER >= MC_1_19_2
@@ -66,16 +64,19 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#endif
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override
protected void createInitialBindings()
protected void createInitialSharedBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new FabricPluginPacketSender());
}
@Override
protected void createInitialClientBindings() { /* no additional setup needed currently */ }
@Override
protected IEventProxy createClientProxy() { return new FabricClientProxy(); }
@@ -153,11 +154,6 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
}
#endif
if (ConfigBase.INSTANCE == null)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
}
}
@@ -30,7 +30,7 @@ import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel;
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
import com.seibel.distanthorizons.common.CommonPacketPayload;
@@ -52,7 +52,7 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private static final ServerApi SERVER_API = ServerApi.INSTANCE;
@SuppressWarnings("unused")
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;
@@ -19,7 +19,7 @@
package com.seibel.distanthorizons.fabric.mixins.client;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@@ -3,8 +3,10 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -16,6 +18,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.world.level.chunk.LevelChunk;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import java.util.concurrent.AbstractExecutorService;
#endif
@Mixin(ClientPacketListener.class)
@@ -45,8 +49,24 @@ public class MixinClientPacketListener
@Inject(method = "enableChunkLight", at = @At("TAIL"))
void onEnableChunkLight(LevelChunk chunk, int x, int z, CallbackInfo ci)
{
IClientLevelWrapper clientLevel = ClientLevelWrapper.getWrapper((ClientLevel) chunk.getLevel());
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel), clientLevel);
if (chunk == null)
{
return;
}
// executor to prevent locking up the render thread
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor == null)
{
return;
}
executor.execute(() ->
{
IClientLevelWrapper clientLevel = ClientLevelWrapper.getWrapper((ClientLevel) this.level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel), clientLevel);
});
}
#endif
@@ -12,7 +12,7 @@ import java.util.List;
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
@@ -30,7 +30,7 @@ import net.minecraft.client.renderer.RenderType;
import com.mojang.blaze3d.vertex.PoseStack;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
@@ -77,7 +77,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
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;
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_17_1
@@ -116,7 +116,7 @@ public class MixinLevelRenderer
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
@Inject(at = @At("HEAD"), method = "prepareChunkRenders", cancellable = true)
private void prepareChunkRenders(Matrix4fc projectionMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
#else
@@ -141,7 +141,7 @@ public class MixinLevelRenderer
// get the matrices directly from MC
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
// MC combined the model view and projection matricies
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f();
@@ -169,7 +169,7 @@ public class MixinLevelRenderer
{
ClientApi.INSTANCE.renderDeferredLodsForShaders();
}
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
// rendering handled via Fabric Api render event
#else
// handled here and in MixinChunkSectionsToRender
@@ -178,7 +178,7 @@ public class MixinLevelRenderer
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// rendering handled via Fabric Api render event
#else
@Inject(at = @At("HEAD"), method = "prepareChunkRenders")
@@ -29,6 +29,7 @@ import net.minecraft.client.renderer.LightTexture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -61,28 +62,40 @@ public class MixinLightTexture
private GpuTexture texture;
#endif
@Unique
private MinecraftRenderWrapper renderWrapper = null;
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
if (mc == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
if (clientLevel == null)
{
return;
}
// lazy initialization to make sure we don't call this too early
if (this.renderWrapper == null)
{
this.renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
}
#if MC_VER < MC_1_21_3
renderWrapper.updateLightmap(this.lightPixels, clientLevel);
this.renderWrapper.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
this.renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
#else
GlTexture glTexture = (GlTexture) this.texture;
renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
#endif
}
@@ -16,7 +16,7 @@ import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
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.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -34,7 +34,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class MixinMinecraft
{
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName());
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Shadow
@@ -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.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.io.IOException;
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";
@@ -13,7 +13,7 @@ import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
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.util.ArrayList;
@@ -23,7 +23,7 @@ import java.util.function.Consumer;
public class TestGenericWorldGenerator implements IDhApiWorldGenerator
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
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.core.logging.DhLoggerBuilder;
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
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
@Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
@@ -55,7 +55,7 @@ import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraft.world.level.chunk.ChunkAccess;
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.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 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
@@ -86,11 +86,13 @@ public class ForgeMain extends AbstractModInitializer
}
@Override
protected void createInitialBindings()
protected void createInitialSharedBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new ForgePluginPacketSender());
}
@Override
protected void createInitialClientBindings() { /* no additional setup needed currently */ }
@Override
protected IEventProxy createClientProxy() { return new ForgeClientProxy(); }
@@ -47,7 +47,7 @@ import net.minecraftforge.event.server.ServerStoppingEvent;
#endif
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.function.Supplier;
@@ -12,7 +12,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
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.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinMinecraft
{
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MixinMinecraft.class.getSimpleName());
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
+3 -2
View File
@@ -5,8 +5,8 @@ org.gradle.caching=true
# Mod Info
mod_name=DistantHorizons
mod_version=2.3.5-b
api_version=4.1.0
mod_version=2.3.7-b-dev
api_version=5.0.0
maven_group=com.seibel.distanthorizons
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.
@@ -23,6 +23,7 @@ manifold_version=2025.1.27
nightconfig_version=3.6.6
lz4_version=1.8.0
xz_version=1.9
zstd_version=1.5.7-6
# Before updating, read relocate_natives/README.md
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
@@ -42,7 +42,7 @@ import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
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 com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -64,7 +64,7 @@ import java.util.concurrent.AbstractExecutorService;
public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
{
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();
@@ -149,8 +149,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
}
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
@@ -176,8 +174,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
}
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
@@ -250,7 +246,7 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent
public void afterLevelEntityRenderEvent(RenderLevelStageEvent.AfterEntities event)
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
@@ -263,7 +259,7 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent
public void afterLevelTranslucentRenderEvent(RenderLevelStageEvent.AfterTranslucentBlocks event)
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
@@ -275,7 +271,7 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
@SubscribeEvent
public void afterLevelRenderEvent(RenderLevelStageEvent.AfterLevel event)
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
@@ -124,11 +124,14 @@ public class NeoforgeMain extends AbstractModInitializer
protected IEventProxy createServerProxy(boolean isDedicated) { return new NeoforgeServerProxy(isDedicated); }
@Override
protected void createInitialBindings()
protected void createInitialSharedBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new NeoforgePluginPacketSender());
}
@Override
protected void createInitialClientBindings()
{
// replace MC RenderWrapper with more specific neoforge version
SingletonInjector.INSTANCE.unbind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); // TODO replace with a replaceOrBind for simplicity
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, NeoforgeMinecraftRenderWrapper.INSTANCE);
@@ -24,7 +24,7 @@ import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.neoforge.event.server.ServerAboutToStartEvent;
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;
@@ -40,7 +40,7 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
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;
public static Supplier<Boolean> isGenerationThreadChecker = null;
@@ -152,7 +152,7 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); }
private static ServerLevelWrapper getServerLevelWrapper(ResourceKey<Level> resourceKey, PlayerEvent event)
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
//noinspection DataFlowIssue (possible NPE after getServer())
return getServerLevelWrapper(event.getEntity().getServer().getLevel(resourceKey));
#else
@@ -1,6 +1,6 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
@@ -18,7 +18,7 @@ import net.minecraft.client.gui.components.DebugScreenOverlay;
public class MixinDebugScreenOverlay
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
@@ -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.common.wrappers.McObjectConverter;
@@ -76,14 +76,14 @@ public class MixinLevelRenderer
private ClientLevel level;
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
#if MC_VER < MC_1_21_6
@Inject(at = @At("HEAD"), method = "renderSectionLayer")
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
@Inject(at = @At("HEAD"), method = "renderLevel")
private void onRenderLevel(
GraphicsResourceAllocator resourceAllocator, DeltaTracker deltaTracker,
@@ -38,7 +38,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.platform.NativeImage;
#elif MC_VER < MC_1_21_5
import com.mojang.blaze3d.pipeline.TextureTarget;
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
@@ -82,7 +82,7 @@ public class MixinLightTexture
renderWrapper.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
#elif MC_VER < MC_1_21_10
#elif MC_VER < MC_1_21_9
GlTexture glTexture = (GlTexture) this.texture;
renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
#else
@@ -12,7 +12,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo;
import net.minecraft.client.Minecraft;
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.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -29,7 +29,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public class MixinMinecraft
{
@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/>
@@ -3,9 +3,9 @@ package com.seibel.distanthorizons.neoforge.wrappers;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.core.logging.DhLogger;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
#else
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.textures.GpuTexture;
@@ -18,7 +18,7 @@ public class NeoforgeMinecraftRenderWrapper extends MinecraftRenderWrapper
{
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();
@@ -26,7 +26,7 @@ public class NeoforgeMinecraftRenderWrapper extends MinecraftRenderWrapper
@Override
public int getDepthTextureId()
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// no special handling required,
// both neo/fabric uses the same back end objects
return super.getDepthTextureId();
@@ -53,7 +53,7 @@ public class NeoforgeMinecraftRenderWrapper extends MinecraftRenderWrapper
@Override
public int getColorTextureId()
{
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
// no special handling required,
// both neo/fabric uses the same back end objects
return super.getColorTextureId();
@@ -1,6 +1,6 @@
package com.seibel.distanthorizons.neoforge.wrappers;
#if MC_VER < MC_1_21_10
#if MC_VER < MC_1_21_9
public class NeoforgeTextureUnwrapper
{ /* not needed for older MC versions */ }
#else
+59
View File
@@ -0,0 +1,59 @@
# 1.21.9 version
java_version=21
minecraft_version=1.21.9
parchment_version=1.21:2024.07.28
compatible_minecraft_versions=["1.21.9"]
accessWidenerVersion=1_21_10
builds_for=fabric,neoforge
# forge is broken due to gradle/build script issues
# Netty
netty_version=4.1.97.Final
# Fabric loader
fabric_loader_version=0.17.3
fabric_api_version=0.134.0+1.21.9
modmenu_version=16.0.0-rc.1
starlight_version_fabric=
phosphor_version_fabric=
lithium_version=
sodium_version=mc1.21.10-0.7.2-fabric
iris_version=1.9.3+1.21.9-fabric
bclib_version=
immersive_portals_version=
canvas_version=
fabric_incompatibility_list={ }
fabric_recommend_list={}
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=1
enable_bclib=0
enable_immersive_portals=0
enable_canvas=0
# (Neo)Forge loader
forge_version=
neoforge_version=21.9.15-beta
# sometime before 21.9.15-beta Neoforge changed how their rendering API events handle levels
# so we can't support both versions at once
neoforge_version_range=[21.9.15-beta,)
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# (Neo)Forge mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
enable_terrafirmacraft=0