Fix loading ClientLevel on server

This commit is contained in:
s809
2023-08-26 21:31:26 +05:00
48 changed files with 884 additions and 697 deletions
@@ -0,0 +1,30 @@
/*
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2022 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.api.enums.config;
/**
* @since API 1.0.0
*/
public enum EGLErrorHandlingMode
{
IGNORE,
LOG,
LOG_THROW;
}
@@ -85,9 +85,9 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
* Please run your generator in game at least once to confirm the objects you are returning are correct. <br><br>
*
* Consumer expected inputs for each minecraft version (in order): <br>
* <strong>1.18:</strong> [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader] <br>
* <strong>1.19:</strong> [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader] <br>
* <strong>1.20:</strong> [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader] <br>
* <strong>1.16</strong>, <strong>1.17</strong>, <strong>1.18</strong>, <strong>1.19</strong>, <strong>1.20</strong>: <br>
* - [net.minecraft.world.level.chunk.ChunkAccess] <br>
* - [net.minecraft.world.level.ServerLevel] or [net.minecraft.world.level.ClientLevel] <br>
*/
CompletableFuture<Void> generateChunks(
int chunkPosMinX, int chunkPosMinZ,
@@ -319,7 +319,12 @@ public class ClientApi
if (clientWorld != null)
{
clientWorld.clientTick();
SharedApi.worldGenTick(clientWorld::doWorldGen);
// Ignore local world gen, as it's managed by server ticking
if (!(clientWorld instanceof DhClientServerWorld))
{
SharedApi.worldGenTick(clientWorld::doWorldGen);
}
}
profiler.pop();
}
@@ -329,7 +334,7 @@ public class ClientApi
//============//
// networking //
//============//
/** @param byteBuf is Netty's {@link ByteBuffer} wrapper. */
public void serverMessageReceived(ByteBuf byteBuf)
{
@@ -568,4 +573,6 @@ public class ClientApi
MC.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
}
}
}
@@ -14,10 +14,16 @@ public class SharedApi
private static AbstractDhWorld currentWorld;
private static int lastWorldGenTickDelta = 0;
public static void init() { Initializer.init(); }
public static EWorldEnvironment getEnvironment() { return (currentWorld == null) ? null : currentWorld.environment; }
public static void setDhWorld(AbstractDhWorld newWorld)
{
currentWorld = newWorld;
@@ -2,7 +2,7 @@ package com.seibel.distanthorizons.core.config;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
// TODO: Make this intergrate with the config system
// TODO: Make this integrate with the config system
public class AppliedConfigState<T>
{
final ConfigEntry<T> entry;
@@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.config.eventHandlers.UnsafeValuesConfigLi
import com.seibel.distanthorizons.core.config.eventHandlers.WorldCurvatureConfigEventHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.RenderQualityPresetConfigEventHandler;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.types.*;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance;
import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance;
@@ -62,6 +61,7 @@ public class Config
public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build();
public static class Client
{
public static ConfigEntry<Boolean> quickEnableRendering = new ConfigEntry.Builder<Boolean>()
@@ -200,6 +200,7 @@ public class Config
+ ETransparency.DISABLED + ": LODs will be opaque. \n"
+ "")
.setPerformance(EConfigEntryPerformance.MEDIUM)
.addListener(RenderCacheConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<EBlocksToAvoid> blocksToIgnore = new ConfigEntry.Builder<EBlocksToAvoid>()
@@ -531,6 +532,7 @@ public class Config
+ "0 = black \n"
+ "1 = normal \n"
+ "2 = near white")
.addListener(RenderCacheConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Double> saturationMultiplier = new ConfigEntry.Builder<Double>() // TODO: Make this a float (the ClassicConfigGUI doesnt support floats)
@@ -541,6 +543,7 @@ public class Config
+ "0 = black and white \n"
+ "1 = normal \n"
+ "2 = very saturated")
.addListener(RenderCacheConfigEventHandler.INSTANCE)
.build();
public static ConfigEntry<Boolean> enableCaveCulling = new ConfigEntry.Builder<Boolean>()
@@ -1101,6 +1104,26 @@ public class Config
.addListener(UnsafeValuesConfigListener.INSTANCE)
.build();
public static ConfigEntry<Boolean> overrideVanillaGLLogger = new ConfigEntry.Builder<Boolean>()
.set(ModInfo.IS_DEV_BUILD)
.comment(""
+ "Requires a reboot to change. \n"
+ "")
.build();
public static ConfigEntry<EGLErrorHandlingMode> glErrorHandlingMode = new ConfigEntry.Builder<EGLErrorHandlingMode>()
.set(ModInfo.IS_DEV_BUILD ? EGLErrorHandlingMode.LOG : EGLErrorHandlingMode.IGNORE)
.comment(""
+ "Defines how OpenGL errors are handled. \n"
+ "May incorrectly catch OpenGL errors thrown by other mods. \n"
+ "\n"
+ EGLErrorHandlingMode.IGNORE + ": Do nothing. \n"
+ EGLErrorHandlingMode.LOG + ": write an error to the log. \n"
+ EGLErrorHandlingMode.LOG_THROW + ": write to the log and throw an exception. \n"
+ " Warning: this should only be enabled when debugging the LOD renderer \n"
+ " as it may break Minecraft's renderer when an exception is thrown. \n"
+ "")
.build();
// can be set to public inorder to show in the config file and UI
public static ConfigCategory exampleConfigScreen = new ConfigCategory.Builder()
@@ -1155,10 +1178,10 @@ public class Config
.set(new HashMap<String, String>())
.build();
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> {
System.setProperty("java.awt.headless", "false"); // Required to make it work
JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE);
});
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> { new Thread(() -> {
System.setProperty("java.awt.headless", "false"); // Required to make it work
JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE);
});});
public static ConfigCategory categoryTest = new ConfigCategory.Builder().set(CategoryTest.class).build();
@@ -18,37 +18,43 @@ import java.util.*;
*
* @author coolGi
* @author Ran
* @version 2023-8-26
*/
// Init the config after singletons have been blinded
public class ConfigBase
{
/** Our own config instance, don't modify */
/** Our own config instance, don't modify unless you are the DH mod */
public static ConfigBase INSTANCE;
public ConfigFileHandling configFileINSTANCE;
public static final Logger LOGGER = LogManager.getLogger(ConfigBase.class.getSimpleName());
private final Logger LOGGER;
public final String modID;
public final String modName;
public final int configVersion;
public boolean isLoaded = false;
/**
* What the config works with
*
* Enum
* Boolean
* Byte
* Integer
* Double
* Short
* Long
* Float
* String
*
* // Below, "T" should be a value from above
* List<T>
* ArrayList<T>
* Map<String, T>
* HashMap<String, T>
* What the config works with
* <br>
* <br> {@link Enum}
* <br> {@link Boolean}
* <br> {@link Byte}
* <br> {@link Integer}
* <br> {@link Double}
* <br> {@link Short}
* <br> {@link Long}
* <br> {@link Float}
* <br> {@link String}
* <br>
* <br> // Below, "T" should be a value from above
* <br> // Note: This is not checked, so we trust that you are doing the right thing
* <br> List<T>
* <br> ArrayList<T>
* <br> Map<String, T>
* <br> HashMap<String, T>
*/
public static final List<Class<?>> acceptableInputs = new ArrayList<Class<?>>()
{{
@@ -75,6 +81,8 @@ public class ConfigBase
public ConfigBase(String modID, String modName, Class<?> config, int configVersion)
{
this.LOGGER = LogManager.getLogger(this.getClass().getSimpleName() + ", " + modID);
LOGGER.info("Initialising config for " + modName);
this.modID = modID;
this.modName = modName;
@@ -85,6 +93,8 @@ public class ConfigBase
// File handling (load from file)
this.configFileINSTANCE = new ConfigFileHandling(this);
this.configFileINSTANCE.loadFromFile();
isLoaded = true;
LOGGER.info("Config for " + modName + " initialised");
}
@@ -102,7 +112,7 @@ public class ConfigBase
}
catch (IllegalAccessException exception)
{
exception.printStackTrace();
LOGGER.warn(exception);
}
AbstractConfigType<?, ?> entry = entries.get(entries.size() - 1);
@@ -151,10 +161,11 @@ public class ConfigBase
* @param checkEnums Checks if all the lang for the enum's exist
*/
// This is just to re-format the lang or check if there is something in the lang that is missing
@SuppressWarnings("unchecked")
public String generateLang(boolean onlyShowNew, boolean checkEnums)
{
ILangWrapper langWrapper = SingletonInjector.INSTANCE.get(ILangWrapper.class);
List<Class<? extends Enum>> enumList = new ArrayList<>();
List<Class<? extends Enum<?>>> enumList = new ArrayList<>();
String generatedLang = "";
@@ -168,7 +179,7 @@ public class ConfigBase
if (checkEnums && entry.getType().isEnum() && !enumList.contains(entry.getType()))
{ // Put it in an enum list to work with at the end
enumList.add((Class<? extends Enum>) entry.getType());
enumList.add((Class<? extends Enum<?>>) entry.getType());
}
if (!onlyShowNew || langWrapper.langExists(entryPrefix))
{
@@ -13,7 +13,7 @@ import java.util.Map;
public class NumberUtil
{
// Is there no better way of doing this?
public static Map<Class, Number> minValues = new HashMap<Class, Number>()
public static Map<Class<?>, Number> minValues = new HashMap<Class<?>, Number>()
{{
put(Byte.class, Byte.MIN_VALUE);
put(Short.class, Short.MIN_VALUE);
@@ -22,7 +22,7 @@ public class NumberUtil
put(Double.class, Double.MIN_VALUE);
put(Float.class, Float.MIN_VALUE);
}};
public static Map<Class, Number> maxValues = new HashMap<Class, Number>()
public static Map<Class<?>, Number> maxValues = new HashMap<Class<?>, Number>()
{{
put(Byte.class, Byte.MAX_VALUE);
put(Short.class, Short.MAX_VALUE);
@@ -32,11 +32,11 @@ public class NumberUtil
put(Float.class, Float.MAX_VALUE);
}};
public static Number getMinimum(Class c)
public static Number getMinimum(Class<?> c)
{
return minValues.get(c);
}
public static Number getMaximum(Class c)
public static Number getMaximum(Class<?> c)
{
return maxValues.get(c);
}
@@ -46,7 +46,7 @@ public class NumberUtil
{
if (a.getClass() != b.getClass())
return false;
Class typeClass = a.getClass();
Class<?> typeClass = a.getClass();
if (typeClass == Byte.class) return a.byteValue() > b.byteValue();
if (typeClass == Short.class) return a.shortValue() > b.shortValue();
@@ -62,7 +62,7 @@ public class NumberUtil
{
if (a.getClass() != b.getClass())
return false;
Class typeClass = a.getClass();
Class<?> typeClass = a.getClass();
if (typeClass == Byte.class) return a.byteValue() < b.byteValue();
if (typeClass == Short.class) return a.shortValue() < b.shortValue();
@@ -20,13 +20,8 @@ public class RenderCacheConfigEventHandler implements IConfigListener
{
public static RenderCacheConfigEventHandler INSTANCE = new RenderCacheConfigEventHandler();
// previous values used to check if a watched setting was actually modified
private EVerticalQuality previousVerticalQualitySetting = null;
private EMaxHorizontalResolution previousHorizontalResolution = null;
private ELodShading lodShading = null;
/** how long to wait in milliseconds before applying the config changes */
private static final long TIMEOUT_IN_MS = 400L;
private static final long TIMEOUT_IN_MS = 4_000L;
private Timer cacheClearingTimer;
@@ -38,38 +33,7 @@ public class RenderCacheConfigEventHandler implements IConfigListener
@Override
public void onConfigValueSet()
{
// confirm a setting was actually changed
boolean refreshRenderData = false;
EVerticalQuality newVerticalQuality = Config.Client.Advanced.Graphics.Quality.verticalQuality.get();
if (this.previousVerticalQualitySetting != newVerticalQuality)
{
this.previousVerticalQualitySetting = newVerticalQuality;
refreshRenderData = true;
}
EMaxHorizontalResolution newHorizontalResolution = Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution.get();
if (this.previousHorizontalResolution != newHorizontalResolution)
{
this.previousHorizontalResolution = newHorizontalResolution;
refreshRenderData = true;
}
ELodShading newLodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
if (this.lodShading != newLodShading)
{
this.lodShading = newLodShading;
refreshRenderData = true;
}
if (refreshRenderData)
{
this.refreshRenderDataAfterTimeout();
}
this.refreshRenderDataAfterTimeout();
}
@Override
@@ -1,5 +1,7 @@
package com.seibel.distanthorizons.core.config.eventHandlers.presets;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -64,6 +66,12 @@ public abstract class AbstractPresetConfigEventHandler<TPresetEnum extends Enum<
@Override
public void onConfigValueSet()
{
// don't try modifying the config before it's been loaded from file
if (!ConfigBase.INSTANCE.isLoaded)
{
return;
}
// don't have this method run on top of itself
if (this.changingPreset)
{
@@ -7,11 +7,10 @@ import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -19,63 +18,67 @@ import java.nio.file.Path;
* Handles reading and writing config files.
*
* @author coolGi
* @version 2023-7-16
* @version 2023-8-26
*/
public class ConfigFileHandling
{
private static final Logger LOGGER = ConfigBase.LOGGER;
public final ConfigBase configBase;
public final Path configPath;
private final Logger LOGGER;
/** This is the object for night-config */
private final CommentedFileConfig nightConfig;
public ConfigFileHandling(ConfigBase configBase)
{
this.LOGGER = LogManager.getLogger(this.getClass().getSimpleName() + ", " + configBase.modID);
this.configBase = configBase;
configPath = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class)
.getInstallationDirectory().toPath().resolve("config").resolve(this.configBase.modName + ".toml");
this.nightConfig = CommentedFileConfig.builder(configPath.toFile()).build();
}
/** Saves the entire config to the file */
public void saveToFile()
{
CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build();
saveToFile(this.nightConfig);
}
/** Saves the entire config to the file */
public void saveToFile(CommentedFileConfig nightConfig)
{
if (!Files.exists(configPath)) // Try to check if the config exists
try
{
if (!this.configPath.getParent().toFile().exists())
{
Files.createDirectory(this.configPath.getParent());
}
Files.createFile(configPath);
}
catch (IOException ex)
{
ex.printStackTrace();
}
{
reCreateFile(configPath);
}
loadNightConfig(nightConfig);
loadConfig(config);
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
{
createComment((ConfigEntry<?>) entry, config);
saveEntry((ConfigEntry<?>) entry, config);
createComment((ConfigEntry<?>) entry, nightConfig);
saveEntry((ConfigEntry<?>) entry, nightConfig);
}
}
try
{
config.save();
nightConfig.save();
}
catch (Exception e)
{
// If it fails to save, crash game
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
}
config.close();
}
/**
* Loads the entire config from the file
*
@@ -83,46 +86,50 @@ public class ConfigFileHandling
*/
public void loadFromFile()
{
CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build();
loadFromFile(nightConfig);
}
/**
* Loads the entire config from the file
*
* @apiNote This overwrites any value currently stored in the config
*/
public void loadFromFile(CommentedFileConfig nightConfig)
{
// Attempt to load the file and if it fails then save config to file
try
if (Files.exists(configPath))
{
if (Files.exists(configPath))
config.load();
else
{
saveToFile();
return;
}
loadNightConfig(nightConfig);
}
catch (Exception e)
else
{
e.printStackTrace();
saveToFile();
reCreateFile(configPath);
return;
}
// Load all the entries
for (AbstractConfigType<?, ?> entry : this.configBase.entries)
{
if (ConfigEntry.class.isAssignableFrom(entry.getClass()))
if (
ConfigEntry.class.isAssignableFrom(entry.getClass()) &&
entry.getAppearance().showInFile
)
{
createComment((ConfigEntry<?>) entry, config);
loadEntry((ConfigEntry<?>) entry, config);
createComment((ConfigEntry<?>) entry, nightConfig);
loadEntry((ConfigEntry<?>) entry, nightConfig);
}
}
try
{
config.save();
nightConfig.save();
}
catch (Exception e)
{
// If it fails to save, crash game
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e);
}
config.close();
}
@@ -131,142 +138,148 @@ public class ConfigFileHandling
// Save an entry when only given the entry
public void saveEntry(ConfigEntry<?> entry)
{
CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build();
loadConfig(config);
saveEntry(entry, config);
config.save();
config.close();
saveEntry(entry, nightConfig);
nightConfig.save();
}
// Save an entry
@SuppressWarnings("unchecked")
/** Save an entry */
public void saveEntry(ConfigEntry<?> entry, CommentedFileConfig workConfig)
{
if (!entry.getAppearance().showInFile) return;
if (entry.getTrueValue() == null)
throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + configBase.modName + "]. Please contact the authors");
Class<?> originalClass = ConfigTypeConverters.isClassConvertable(entry.getType());
if (originalClass != null)
{
workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.convertToString(originalClass, entry.getTrueValue()));
return;
}
workConfig.set(entry.getNameWCategory(), entry.getTrueValue());
workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.attemptToConvertToString(entry.getType(), entry.getTrueValue()));
}
// Loads an entry when only given the entry
/** Loads an entry when only given the entry */
public void loadEntry(ConfigEntry<?> entry)
{
CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).autosave().build();
loadConfig(config);
loadEntry(entry, config);
config.close();
loadEntry(entry, nightConfig);
}
// Loads an entry
@SuppressWarnings("unchecked") // Suppress due to its always safe
public <T> void loadEntry(ConfigEntry<T> entry, CommentedFileConfig workConfig)
/** Loads an entry */
@SuppressWarnings("unchecked")
public <T> void loadEntry(ConfigEntry<T> entry, CommentedFileConfig nightConfig)
{
if (!entry.getAppearance().showInFile) return;
if (!entry.getAppearance().showInFile)
return;
if (workConfig.contains(entry.getNameWCategory()))
if (!nightConfig.contains(entry.getNameWCategory()))
{
try
saveEntry(entry, nightConfig);
return;
}
try
{
if (entry.getType().isEnum())
{
if (entry.getType().isEnum())
{
entry.pureSet((T) (workConfig.getEnum(entry.getNameWCategory(), (Class<? extends Enum>) entry.getType())));
return;
}
Class<?> originalClass = ConfigTypeConverters.isClassConvertable(entry.getType());
if (originalClass != null)
{
entry.pureSet((T) ConfigTypeConverters.convertFromString(originalClass, workConfig.get(entry.getNameWCategory())));
return;
}
if (entry.getType() == workConfig.get(entry.getNameWCategory()).getClass())
{ // If the types are the same
entry.pureSet((T) workConfig.get(entry.getNameWCategory()));
entry.clampWithinRange();
return;
}
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] is invalid. Expected " + entry.getType() + " but got " + workConfig.get(entry.getNameWCategory()).getClass() + ". Using default value.");
saveEntry(entry, workConfig);
entry.pureSet((T) (nightConfig.getEnum(entry.getNameWCategory(), (Class<? extends Enum>) entry.getType())));
return;
}
catch (Exception e)
{
// e.printStackTrace();
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value.");
saveEntry(entry, workConfig);
entry.pureSet((T) ConfigTypeConverters.attemptToConvertFromString(entry.getType(), nightConfig.get(entry.getNameWCategory())));
if (entry.getTrueValue() == null) {
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] returned as null from the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
}
}
else
catch (Exception e)
{
saveEntry(entry, workConfig);
// e.printStackTrace();
LOGGER.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value.");
entry.pureSet(entry.getDefaultValue());
}
}
// Creates the comment for an entry when only given the entry
public void createComment(ConfigEntry<?> entry)
{
CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).autosave().build();
loadConfig(config);
createComment(entry, config);
config.close();
createComment(entry, nightConfig);
}
// Creates a comment for an entry
public void createComment(ConfigEntry<?> entry, CommentedFileConfig workConfig)
public void createComment(ConfigEntry<?> entry, CommentedFileConfig nightConfig)
{
if (!entry.getAppearance().showInFile)
if (
!entry.getAppearance().showInFile ||
entry.getComment() == null
)
return;
if (entry.getComment() != null)
{
workConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n ");
}
nightConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n ");
}
/** Does config.load(); but with error checking */
public void loadConfig(CommentedFileConfig config)
/**
* Uses {@link ConfigFileHandling#nightConfig} to do {@link CommentedFileConfig#load()} but with error checking
*
* @apiNote This overwrites any value currently stored in the config
*/
public void loadNightConfig()
{
loadNightConfig(this.nightConfig);
}
/**
* Does {@link CommentedFileConfig#load()} but with error checking
*
* @apiNote This overwrites any value currently stored in the config
*/
public void loadNightConfig(CommentedFileConfig nightConfig)
{
try
{
config.load();
}
catch (Exception e)
{
System.out.println("Loading file failed because of this expectation:\n" + e);
try
{ // Now try remaking the file and loading it
if (!this.configPath.getParent().toFile().exists())
{
Files.createDirectory(this.configPath.getParent());
}
boolean fileDeleted = Files.deleteIfExists(this.configPath);
System.out.println("File at [" + this.configPath + "] was " + (fileDeleted ? "" : "not ") + "able to be deleted.");
Files.createFile(this.configPath);
config.load();
}
catch (IOException ex)
{
System.out.println("Creating file failed");
ex.printStackTrace();
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Loading file and resetting config file failed at path [" + configPath + "]. Please check the file is ok and you have the permissions", ex);
if (!Files.exists(this.configPath))
Files.createFile(this.configPath);
nightConfig.load();
}
catch (Exception e)
{
LOGGER.warn("Loading file failed because of this expectation:\n" + e);
reCreateFile(this.configPath);
nightConfig.load();
}
}
catch (Exception ex)
{
System.out.println("Creating file failed");
LOGGER.error(ex);
SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Loading file and resetting config file failed at path [" + configPath + "]. Please check the file is ok and you have the permissions", ex);
}
}
public static void reCreateFile(Path path)
{
try
{
Files.deleteIfExists(path);
if (!path.getParent().toFile().exists())
{
Files.createDirectory(path.getParent());
}
Files.createFile(path);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
// ========== API (server) STUFF ========== //
/** ALWAYS CLEAR WHEN NOT ON SERVER!!!! */
/* * ALWAYS CLEAR WHEN NOT ON SERVER!!!! */
// We are not using this stuff, so comment it out for now (if we ever do need it then we can uncomment it)
/*
@SuppressWarnings("unchecked")
@@ -37,6 +37,32 @@ public class ConfigTypeConverters
return null;
}
public static Object attemptToConvertToString(Object value)
{
return attemptToConvertToString(value.getClass(), value);
}
public static Object attemptToConvertToString(Class<?> clazz, Object value)
{
Class<?> convertablClass = isClassConvertable(clazz);
if (convertablClass != null) {
return convertToString(convertablClass, value);
}
return value;
}
public static Object attemptToConvertFromString(Object value)
{
return attemptToConvertFromString(value.getClass(), value);
}
public static Object attemptToConvertFromString(Class<?> clazz, Object value)
{
Class<?> convertablClass = isClassConvertable(clazz);
if (convertablClass != null) {
return convertFromString(convertablClass, (String) value);
}
return value;
}
public static String convertToString(Class<?> clazz, Object value)
{
try
@@ -116,9 +142,11 @@ public class ConfigTypeConverters
Map<String, Object> mapObject = (Map<String, Object>) item;
Config jsonObject = Config.inMemory();
Object[] keyArray = mapObject.keySet().toArray();
for (int i = 0; i < mapObject.size(); i++)
{
jsonObject.add(mapObject.keySet().toArray()[i].toString(), mapObject.get(mapObject.keySet().toArray()[i]));
jsonObject.add(keyArray[i].toString(), mapObject.get(keyArray[i]));
}
return JsonFormat.minimalInstance().createWriter().writeToString(jsonObject);
@@ -8,10 +8,12 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance
*
* @author coolGi
*/
// Note for devs: The "S" is the class that is extending this
public abstract class AbstractConfigType<T, S>
{ // The S is the class that is extending this
{
public String category = ""; // This should only be set once in the init
public String name; // This should only be set once in the init
protected final T defaultValue;
protected T value;
public ConfigBase configBase;
@@ -21,8 +23,9 @@ public abstract class AbstractConfigType<T, S>
protected AbstractConfigType(EConfigEntryAppearance appearance, T value)
{
this.appearance = appearance;
this.defaultValue = value;
this.value = value;
this.appearance = appearance;
}
@@ -64,7 +67,7 @@ public abstract class AbstractConfigType<T, S>
// Gets the class of T
public Class<?> getType()
{
return value.getClass();
return this.defaultValue.getClass();
}
protected static abstract class Builder<T, S>
@@ -74,11 +77,13 @@ public abstract class AbstractConfigType<T, S>
// Put this into your own builder
@SuppressWarnings("unchecked")
public S setAppearance(EConfigEntryAppearance newAppearance)
{
this.tmpAppearance = newAppearance;
return (S) this;
}
@SuppressWarnings("unchecked")
public S set(T newValue)
{
this.tmpValue = newValue;
@@ -8,12 +8,12 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance
*
* @author coolGi
*/
public class ConfigCategory extends AbstractConfigType<Class, ConfigCategory>
public class ConfigCategory extends AbstractConfigType<Class<?>, ConfigCategory>
{
/** This should not be set by anything other than the config system itself */
public String destination; // Where the category goes to
private ConfigCategory(EConfigEntryAppearance appearance, Class value, String destination)
private ConfigCategory(EConfigEntryAppearance appearance, Class<?> value, String destination)
{
super(appearance, value);
this.destination = destination;
@@ -32,7 +32,7 @@ public class ConfigCategory extends AbstractConfigType<Class, ConfigCategory>
return value;
}
public static class Builder extends AbstractConfigType.Builder<Class, Builder>
public static class Builder extends AbstractConfigType.Builder<Class<?>, Builder>
{
private String tmpDestination = null;
@@ -19,7 +19,6 @@ import java.util.Arrays;
*/
public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implements IConfigEntry<T>
{
private final T defaultValue;
private String comment;
private T min;
private T max;
@@ -41,7 +40,6 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
{
super(appearance, value);
this.defaultValue = value;
this.comment = comment;
this.min = min;
this.max = max;
@@ -54,7 +52,7 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
/** Gets the default value of the option */
@Override
public T getDefaultValue() { return this.defaultValue; }
public T getDefaultValue() { return super.defaultValue; }
@Override
public void setApiValue(T newApiValue)
@@ -70,7 +68,7 @@ public class ConfigEntry<T> extends AbstractConfigType<T, ConfigEntry<T>> implem
/**
* DONT USE THIS IN YOUR CODE <br>
* Sets the value without informing the rest of the code (ie, doesnt call listeners, or saves the value). <br>
* Should only be used when loading the config from the file
* Should only be used when loading the config from the file (in places like the {@link com.seibel.distanthorizons.core.config.file.ConfigFileHandling} or {@link com.seibel.distanthorizons.core.config.ConfigBase})
*/
public void pureSet(T newValue) {
super.set(newValue);
@@ -8,9 +8,9 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance
*
* @author coolGi
*/
public class ConfigLinkedEntry extends AbstractConfigType<AbstractConfigType, ConfigLinkedEntry>
public class ConfigLinkedEntry extends AbstractConfigType<AbstractConfigType<?, ?>, ConfigLinkedEntry>
{
public ConfigLinkedEntry(AbstractConfigType value)
public ConfigLinkedEntry(AbstractConfigType<?, ?> value)
{
super(EConfigEntryAppearance.ONLY_IN_GUI, value);
}
@@ -21,10 +21,10 @@ public class ConfigLinkedEntry extends AbstractConfigType<AbstractConfigType, Co
/** Value shouldn't be changed after creation */
@Override
public void set(AbstractConfigType newValue) { }
public void set(AbstractConfigType<?, ?> newValue) { }
public static class Builder extends AbstractConfigType.Builder<AbstractConfigType, Builder>
public static class Builder extends AbstractConfigType.Builder<AbstractConfigType<?, ?>, Builder>
{
/** Appearance shouldn't be changed */
@Override
@@ -9,9 +9,10 @@ public class ConfigUIButton extends AbstractConfigType<Runnable, ConfigUIButton>
super(EConfigEntryAppearance.ONLY_IN_GUI, runnable);
}
/** Runs the action of the button. NOTE: Will run on the main thread (so can halt the main process if not offloaded to a different thread) */
public void runAction()
{
new Thread(this.value).start();
this.value.run();
}
public static class Builder extends AbstractConfigType.Builder<Runnable, Builder>
@@ -1,24 +1,19 @@
package com.seibel.distanthorizons.core.dataObjects.fullData;
import com.seibel.distanthorizons.api.enums.config.ELoggerMode;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream;
import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -37,7 +32,7 @@ public class FullDataPointIdMap
* Has the system check if any duplicate Entries were read/written
* when (de)serializing.
*/
private static final boolean RUN_SERIALIZATION_DUPLICATE_VALIDATION = true;
private static final boolean RUN_SERIALIZATION_DUPLICATE_VALIDATION = false;
/** Distant Horizons - Block State Wrapper */
private static final String BLOCK_STATE_SEPARATOR_STRING = "_DH-BSW_";
@@ -45,28 +40,28 @@ public class FullDataPointIdMap
// FIXME: Improve performance maybe?
/** used when the data point map is running normally */
private final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/** should only be used for debugging */
private final DhSectionPos pos;
/** The index should be the same as the Entry's ID */
private final ArrayList<Entry> entryList = new ArrayList<>();
private final HashMap<Entry, Integer> idMap = new HashMap<>();
//=============//
// constructor //
//=============//
public FullDataPointIdMap(DhSectionPos pos) { this.pos = pos; }
//=========//
// methods //
//=========//
private Entry getEntry(int id)
{
this.readWriteLock.readLock().lock();
@@ -80,7 +75,7 @@ public class FullDataPointIdMap
LOGGER.error("FullData ID Map out of sync for pos: " + this.pos + ". ID: [" + id + "] greater than the number of known ID's: [" + this.entryList.size() + "].");
throw e;
}
this.readWriteLock.readLock().unlock();
return entry;
}
@@ -96,9 +91,12 @@ public class FullDataPointIdMap
/** @param useWriteLocks should only be false if this method is already in a write lock to prevent unlocking at the wrong time */
private int addIfNotPresentAndGetId(Entry biomeBlockStateEntry, boolean useWriteLocks)
{
if (useWriteLocks) { this.readWriteLock.writeLock().lock(); }
if (useWriteLocks)
{
this.readWriteLock.writeLock().lock();
}
int id;
if (this.idMap.containsKey(biomeBlockStateEntry))
{
@@ -112,10 +110,13 @@ public class FullDataPointIdMap
this.entryList.add(biomeBlockStateEntry);
this.idMap.put(biomeBlockStateEntry, id);
}
if (useWriteLocks) { this.readWriteLock.writeLock().unlock(); }
if (useWriteLocks)
{
this.readWriteLock.writeLock().unlock();
}
return id;
}
@@ -131,7 +132,7 @@ public class FullDataPointIdMap
target.readWriteLock.readLock().lock();
this.readWriteLock.writeLock().lock();
ArrayList<Entry> entriesToMerge = target.entryList;
int[] remappedEntryIds = new int[entriesToMerge.size()];
for (int i = 0; i < entriesToMerge.size(); i++)
@@ -140,7 +141,7 @@ public class FullDataPointIdMap
int id = this.addIfNotPresentAndGetId(entity, false);
remappedEntryIds[i] = id;
}
this.readWriteLock.writeLock().unlock();
target.readWriteLock.readLock().unlock();
@@ -150,19 +151,19 @@ public class FullDataPointIdMap
}
/** Serializes all contained entries into the given stream, formatted in UTF */
public void serialize(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
public void serialize(DhDataOutputStream outputStream) throws IOException
{
this.readWriteLock.readLock().lock();
outputStream.writeInt(this.entryList.size());
// only used when debugging
HashMap<String, FullDataPointIdMap.Entry> dataPointEntryBySerialization = new HashMap<>();
for (Entry entry : this.entryList)
{
String entryString = entry.serialize();
outputStream.writeUTF(entryString);
if (RUN_SERIALIZATION_DUPLICATE_VALIDATION)
{
if (dataPointEntryBySerialization.containsKey(entryString))
@@ -185,20 +186,17 @@ public class FullDataPointIdMap
public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, DhSectionPos pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException
{
int entityCount = inputStream.readInt();
// only used when debugging
HashMap<String, FullDataPointIdMap.Entry> dataPointEntryBySerialization = new HashMap<>();
FullDataPointIdMap newMap = new FullDataPointIdMap(pos);
for (int i = 0; i < entityCount; i++)
{
String entryString = inputStream.readUTF();
Entry newEntry = Entry.deserialize(entryString, levelWrapper);
// Required check because of an underlying issue where the BiomeWrapper and BlockStateWrapper get null ILevelWrappers
if (newEntry != null) {
newMap.entryList.add(newEntry);
}
newMap.entryList.add(newEntry);
if (RUN_SERIALIZATION_DUPLICATE_VALIDATION)
{
if (dataPointEntryBySerialization.containsKey(entryString))
@@ -212,9 +210,9 @@ public class FullDataPointIdMap
dataPointEntryBySerialization.put(entryString, newEntry);
}
}
LOGGER.trace("deserialized "+pos+" "+newMap.entryList.size()+"-"+entityCount);
LOGGER.trace("deserialized " + pos + " " + newMap.entryList.size() + "-" + entityCount);
return newMap;
}
@@ -246,10 +244,10 @@ public class FullDataPointIdMap
public final IBlockStateWrapper blockState;
private Integer hashCode = null;
// constructor //
public Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
{
this.biome = biome;
@@ -257,9 +255,9 @@ public class FullDataPointIdMap
}
// methods //
@Override
public int hashCode()
{
@@ -268,7 +266,7 @@ public class FullDataPointIdMap
{
this.hashCode = this.serialize().hashCode();
}
return this.hashCode;
}
@@ -282,30 +280,26 @@ public class FullDataPointIdMap
return false;
Entry other = (Entry) otherObj;
return other.biome.serialize().equals(this.biome.serialize())
&& other.blockState.serialize().equals(this.blockState.serialize());
}
@Override
public String toString() {
return this.serialize();
return other.biome.getSerialString().equals(this.biome.getSerialString())
&& other.blockState.getSerialString().equals(this.blockState.getSerialString());
}
public String serialize() {
return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize();
}
@Override
public String toString() { return this.serialize(); }
public String serialize() { return this.biome.getSerialString() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); }
public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException
{
String[] stringArray = str.split(BLOCK_STATE_SEPARATOR_STRING);
if (stringArray.length != 2)
{
// This situation should never occur, both the biome and the blockstate in the entry will always have a value, even if it's default
// The default values will be handled by the biome and blockstate's deserialize functions
throw new IOException("Failed to deserialize BiomeBlockStateEntry");
}
// Necessary to prevent issues with deserializing objects after the level has been closed
// necessary to prevent issues with deserializing objects after the level has been closed
if (Thread.interrupted())
{
throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted.");
@@ -315,5 +309,8 @@ public class FullDataPointIdMap
IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper);
return new Entry(biome, blockState);
}
}
}
@@ -152,10 +152,10 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor
}
public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
{
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
this.mapping.serialize(outputStream, levelWrapper);
this.mapping.serialize(outputStream);
}
public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
{
@@ -197,7 +197,7 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor
return;
}
this.writeIdMappings(outputStream, level.getLevelWrapper());
this.writeIdMappings(outputStream);
}
@@ -230,10 +230,10 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu
@Override
public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
{
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
this.mapping.serialize(outputStream, levelWrapper);
this.mapping.serialize(outputStream);
}
@Override
public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException
@@ -365,10 +365,10 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo
}
@Override
public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
{
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
this.mapping.serialize(outputStream, levelWrapper);
this.mapping.serialize(outputStream);
}
@Override
@@ -238,10 +238,10 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp
@Override
public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException
public void writeIdMappings(DhDataOutputStream outputStream) throws IOException
{
outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE);
this.mapping.serialize(outputStream, levelWrapper);
this.mapping.serialize(outputStream);
}
@Override
@@ -70,7 +70,7 @@ public interface IStreamableFullDataSource<SummaryDataType extends IStreamableFu
return;
}
this.writeIdMappings(outputStream, level.getLevelWrapper());
this.writeIdMappings(outputStream);
}
@@ -96,7 +96,7 @@ public interface IStreamableFullDataSource<SummaryDataType extends IStreamableFu
void setDataPoints(DataContainerType dataPoints);
void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException;
void writeIdMappings(DhDataOutputStream outputStream) throws IOException;
FullDataPointIdMap readIdMappings(DataContainerType dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException;
void setIdMapping(FullDataPointIdMap mappings);
@@ -224,22 +224,31 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer implements IDebugRe
{
boolean hasRendered = false;
renderContext.setupOffset(this.pos);
for (GLVertexBuffer vbo : this.vbosTransparent)
try
{
if (vbo == null)
{
continue;
}
// can throw an IllegalStateException if the GL program was freed before it should've been
renderContext.setupOffset(this.pos);
if (vbo.getVertexCount() == 0)
for (GLVertexBuffer vbo : this.vbosTransparent)
{
continue;
if (vbo == null)
{
continue;
}
if (vbo.getVertexCount() == 0)
{
continue;
}
hasRendered = true;
renderContext.drawVbo(vbo);
//LodRenderer.tickLogger.info("Vertex buffer: {}", vbo);
}
hasRendered = true;
renderContext.drawVbo(vbo);
//LodRenderer.tickLogger.info("Vertex buffer: {}", vbo);
}
catch (IllegalStateException e)
{
LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e);
}
return hasRendered;
@@ -23,7 +23,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import java.util.HashMap;
import java.util.HashSet;
/**
* Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource},
@@ -32,7 +32,6 @@ import java.util.HashMap;
public class FullDataToRenderDataTransformer
{
private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
private static final HashMap<String, ? extends IBlockStateWrapper> RENDERER_IGNORED_BLOCKS = WRAPPER_FACTORY.getRendererIgnoredBlocks();
@@ -292,6 +291,7 @@ public class FullDataToRenderDataTransformer
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
FullDataPointIdMap fullDataMapping = data.getMapping();
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper());
boolean isVoid = true;
int colorToApplyToNextBlock = -1;
@@ -308,10 +308,9 @@ public class FullDataToRenderDataTransformer
IBiomeWrapper biome = fullDataMapping.getBiomeWrapper(id);
IBlockStateWrapper block = fullDataMapping.getBlockStateWrapper(id);
if (RENDERER_IGNORED_BLOCKS.containsValue(block))
if (blockStatesToIgnore.contains(block))
{
// Don't render a block that's marked as ignored by the renderer
// This map should include air (if it doesn't, the map either got refactored or something is wrong)
// Don't render: air, barriers, light blocks, etc.
continue;
}
@@ -6,12 +6,10 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.I
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue;
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
import com.seibel.distanthorizons.core.level.DhLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhLodPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
@@ -80,8 +78,9 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
this.worldGenQueueRef.set(null);
incompleteDataSources.clear(); // clear the incomplete data sources
}
public void removeGenRequestIf(Function<DhSectionPos, Boolean> removeIf) {
public void removeGenRequestIf(Function<DhSectionPos, Boolean> removeIf)
{
HashSet<DhSectionPos> removedRequests = new HashSet<>();
this.incompleteDataSources.forEach((pos, dataSource) ->
@@ -119,7 +118,8 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler
//========//
@Nullable
private CompletableFuture<IFullDataSource> tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource) {
private CompletableFuture<IFullDataSource> tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource)
{
IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get();
// breaks down the missing positions into the desired detail level that the gen queue could accept
if (worldGenQueue != null && !file.genQueueChecked)
@@ -188,12 +188,12 @@ public class SubDimensionLevelMatcher implements AutoCloseable
{
// the chunk isn't empty but the LOD is...
String message = "Error: the chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!";
String message = "Error: the chunk at (" + playerChunkPos.x + "," + playerChunkPos.z + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!";
LOGGER.error(message);
}
else
{
String message = "Warning: The chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty.";
String message = "Warning: The chunk at (" + playerChunkPos.x + "," + playerChunkPos.z + ") is empty.";
LOGGER.warn(message);
}
return null;
@@ -264,7 +264,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable
// stop if the test chunk doesn't contain any data
if (!testLodDataExists)
{
String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. This is expected if the position is outside the sub-dimension's generated area.";
String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.x + "," + playerChunkPos.z + ") is empty. This is expected if the position is outside the sub-dimension's generated area.";
LOGGER.info(message);
continue;
}
@@ -272,7 +272,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable
// get the player data for this dimension folder
SubDimensionPlayerData testPlayerData = new SubDimensionPlayerData(testLevelFolder);
LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]");
LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.x + "," + testPlayerData.playerBlockPos.y + "," + testPlayerData.playerBlockPos.z + "]");
// check if the block positions are close
int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(playerData.playerBlockPos);
@@ -11,11 +11,13 @@ import java.util.concurrent.CompletableFuture;
public interface IWorldGenerationQueue extends Closeable
{
/** the largest numerical detail level */
byte largestDataDetail();
CompletableFuture<WorldGenResult> submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker);
void cancelGenTasks(Iterable<DhSectionPos> positions);
/** @param targetPos the position that world generation should be centered around, generally this will be the player's position. */
void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos);
int getWaitingTaskCount();
@@ -23,4 +25,5 @@ public interface IWorldGenerationQueue extends Closeable
CompletableFuture<Void> startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning);
void close();
}
@@ -51,7 +51,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
/** largest numerical detail level allowed */
public final byte largestDataDetail;
@Override public byte largestDataDetail() { return this.largestDataDetail; }
@Override
public byte largestDataDetail() { return this.largestDataDetail; }
/** lowest numerical detail level allowed */
public final byte smallestDataDetail;
@@ -159,7 +160,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender
@Override
public void cancelGenTasks(Iterable<DhSectionPos> positions)
{
// TODO Cancel gen tasks properly
// TODO Should we cancel generation of chunks that were loaded by the player?
}
//===============//
@@ -11,7 +11,7 @@ import java.util.regex.Pattern;
*
* @author HanSolo
* @author IMS
* @author coolGi2007
* @author coolGi
*/
public class DarkModeDetector
{
@@ -1,7 +1,7 @@
package com.seibel.distanthorizons.core.jar;
/**
* A simple OS getting util based off LWJGL's Platform at org.lwjgl.system.Platform. <br>
* A simple OS getting util based off LWJGL's Platform at {@link org.lwjgl.system.Platform}. <br>
* This version includes some extra utils that we need and removes stuff which we don't.
*
* @author coolGi
@@ -36,7 +36,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static class WorldGenState extends WorldGenModule.WorldGenState
private static class WorldGenState extends WorldGenModule.AbstractWorldGenState
{
WorldGenState(IDhClientLevel level, ClientNetworkState networkState)
{
@@ -42,7 +42,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS
LOGGER.warn("unable to create data folder.");
}
this.serverLevelWrapper = serverLevelWrapper;
this.serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure);
this.serverside = new ServerLevelModule(this, saveStructure);
this.clientside = new ClientLevelModule(this);
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
}
@@ -129,7 +129,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS
}
@Override
public IClientLevelWrapper getClientLevelWrapper() { return serverside.levelWrapper.tryGetClientLevelWrapper(); }
public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); }
@Override
public void clearRenderCache()
@@ -53,7 +53,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel
LOGGER.warn("unable to create data folder.");
}
this.serverLevelWrapper = serverLevelWrapper;
serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure);
this.serverside = new ServerLevelModule(this, saveStructure);
LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure);
this.remotePlayerConnectionHandler = remotePlayerConnectionHandler;
@@ -3,7 +3,7 @@ package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
public interface IDhServerLevel extends IDhWorldGenLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener
public interface IDhServerLevel extends IDhWorldGenLevel
{
void serverTick();
@@ -8,17 +8,48 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.generation.BatchGenerator;
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.WorldGeneratorInjector;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicReference;
public class ServerLevelModule
{
public static class WorldGenState extends WorldGenModule.WorldGenState
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final IDhServerLevel parentServerLevel;
public final AbstractSaveStructure saveStructure;
public final GeneratedFullDataFileHandler dataFileHandler;
public final AppliedConfigState<Boolean> worldGeneratorEnabledConfig;
public final WorldGenModule worldGenModule;
public ServerLevelModule(IDhServerLevel parentServerLevel, AbstractSaveStructure saveStructure)
{
this.parentServerLevel = parentServerLevel;
this.saveStructure = saveStructure;
this.dataFileHandler = new GeneratedFullDataFileHandler(parentServerLevel, saveStructure);
this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration);
this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parentServerLevel);
}
public void close()
{
// shutdown the world-gen
this.worldGenModule.close();
this.dataFileHandler.close();
}
//================//
// helper classes //
//================//
public static class WorldGenState extends WorldGenModule.AbstractWorldGenState
{
WorldGenState(IDhServerLevel level)
{
@@ -36,43 +67,4 @@ public class ServerLevelModule
}
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final IServerLevelWrapper levelWrapper;
public final IDhServerLevel parent;
public final AbstractSaveStructure saveStructure;
public final GeneratedFullDataFileHandler dataFileHandler;
public final AppliedConfigState<Boolean> worldGeneratorEnabledConfig;
public final WorldGenModule worldGenModule;
public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure)
{
this.parent = parent;
this.levelWrapper = levelWrapper;
this.saveStructure = saveStructure;
this.dataFileHandler = new GeneratedFullDataFileHandler(parent, saveStructure);
this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration);
this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parent);
}
//===============//
// data handling //
//===============//
public void close()
{
// shutdown the world-gen
this.worldGenModule.close();
dataFileHandler.close();
}
//=======================//
// misc helper functions //
//=======================//
public void dumpRamUsage()
{
//TODO
}
}
@@ -18,10 +18,127 @@ public class WorldGenModule implements Closeable
private final GeneratedFullDataFileHandler dataFileHandler;
private final GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener;
private final AtomicReference<WorldGenState> worldGenStateRef = new AtomicReference<>();
private final AtomicReference<AbstractWorldGenState> worldGenStateRef = new AtomicReference<>();
private final F3Screen.DynamicMessage worldGenF3Message;
public static abstract class WorldGenState
public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener)
{
this.dataFileHandler = dataFileHandler;
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
this.worldGenF3Message = new F3Screen.DynamicMessage(() ->
{
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
int waitingCount = worldGenState.worldGenerationQueue.getWaitingTaskCount();
int inProgressCount = worldGenState.worldGenerationQueue.getInProgressTaskCount();
return "World Gen Tasks: "+waitingCount+", (in progress: "+inProgressCount+")";
}
else
{
return "World Gen Disabled";
}
});
}
//===================//
// world gen control //
//===================//
public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, AbstractWorldGenState newWgs)
{
// create the new world generator
if (!this.worldGenStateRef.compareAndSet(null, newWgs))
{
LOGGER.warn("Failed to start world gen due to concurrency");
newWgs.closeAsync(false);
}
dataFileHandler.addWorldGenCompleteListener(this.onWorldGenCompleteListener);
dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue);
}
public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler)
{
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
LOGGER.warn("Attempted to stop world gen when it was not running");
return;
}
// shut down the world generator
while (!this.worldGenStateRef.compareAndSet(worldGenState, null))
{
worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
return;
}
}
dataFileHandler.clearGenerationQueue();
worldGenState.closeAsync(true).join(); //TODO: Make it async.
dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener);
}
/** @param targetPosForGeneration the position that world generation should be centered around */
public void worldGenTick(DhBlockPos2D targetPosForGeneration)
{
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
// queue new world generation requests
worldGenState.tick(targetPosForGeneration);
}
}
@Override
public void close()
{
// shutdown the world-gen
AbstractWorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
while (!this.worldGenStateRef.compareAndSet(worldGenState, null))
{
worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
break;
}
}
if (worldGenState != null)
{
worldGenState.closeAsync(true).join(); //TODO: Make it async.
}
}
this.dataFileHandler.close();
this.worldGenF3Message.close();
}
//=========//
// getters //
//=========//
public boolean isWorldGenRunning() { return this.worldGenStateRef.get() != null; }
//================//
// helper classes //
//================//
/** Handles the {@link IWorldGenerationQueue} and any other necessary world gen information. */
public static abstract class AbstractWorldGenState
{
public IWorldGenerationQueue worldGenerationQueue;
@@ -41,105 +158,8 @@ public class WorldGenModule implements Closeable
});
}
public void tick(DhBlockPos2D targetPosForGeneration)
{
worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration);
}
/** @param targetPosForGeneration the position that world generation should be centered around */
public void tick(DhBlockPos2D targetPosForGeneration) { this.worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); }
}
public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener)
{
this.dataFileHandler = dataFileHandler;
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
this.worldGenF3Message = new F3Screen.DynamicMessage(() ->
{
WorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount();
int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount();
return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")";
}
else
{
return "World Gen Disabled";
}
});
}
public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, WorldGenState newWgs)
{
// create the new world generator
if (!this.worldGenStateRef.compareAndSet(null, newWgs))
{
LOGGER.warn("Failed to start world gen due to concurrency");
newWgs.closeAsync(false);
}
dataFileHandler.addWorldGenCompleteListener(onWorldGenCompleteListener);
dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue);
}
public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler)
{
WorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
LOGGER.warn("Attempted to stop world gen when it was not running");
return;
}
// shut down the world generator
while (!this.worldGenStateRef.compareAndSet(worldGenState, null))
{
worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
return;
}
}
dataFileHandler.clearGenerationQueue();
worldGenState.closeAsync(true).join(); //TODO: Make it async.
dataFileHandler.removeWorldGenCompleteListener(onWorldGenCompleteListener);
}
public boolean isWorldGenRunning()
{
return this.worldGenStateRef.get() != null;
}
public void worldGenTick(DhBlockPos2D targetPosForGeneration)
{
WorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
// queue new world generation requests
worldGenState.tick(targetPosForGeneration);
}
}
public void close()
{
// shutdown the world-gen
WorldGenState worldGenState = this.worldGenStateRef.get();
if (worldGenState != null)
{
while (!this.worldGenStateRef.compareAndSet(worldGenState, null))
{
worldGenState = this.worldGenStateRef.get();
if (worldGenState == null)
{
break;
}
}
if (worldGenState != null)
{
worldGenState.closeAsync(true).join(); //TODO: Make it async.
}
}
dataFileHandler.close();
this.worldGenF3Message.close();
}
}
@@ -21,8 +21,6 @@ package com.seibel.distanthorizons.core.pos;
import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
import java.util.Objects;
public class DhChunkPos
{
public final int x; // Low 32 bits
@@ -32,6 +30,7 @@ public class DhChunkPos
public final int hashCode;
public DhChunkPos(int x, int z)
{
this.x = x;
@@ -54,29 +53,24 @@ public class DhChunkPos
{
this(((int)pos.x) >> 4, ((int)pos.z) >> 4);
}
public DhChunkPos(long packed) { this(getX(packed), getZ(packed)); }
public DhChunkPos(long packed) { this(getXFromPackedLong(packed), getZFromPackedLong(packed)); }
public DhBlockPos center() { return new DhBlockPos(8 + x << 4, 0, 8 + z << 4); }
public DhBlockPos corner() { return new DhBlockPos(x << 4, 0, z << 4); }
public DhBlockPos center() { return new DhBlockPos(8 + this.x << 4, 0, 8 + this.z << 4); }
public DhBlockPos corner() { return new DhBlockPos(this.x << 4, 0, this.z << 4); }
public static long toLong(int x, int z) { return ((long) x & 0xFFFFFFFFL) << 32 | (long) z & 0xFFFFFFFFL; }
public static int getX(long chunkPos) { return (int) (chunkPos >> 32); }
public static int getZ(long chunkPos) { return (int) (chunkPos & 0xFFFFFFFFL); }
private static int getXFromPackedLong(long chunkPos) { return (int) (chunkPos >> 32); }
private static int getZFromPackedLong(long chunkPos) { return (int) (chunkPos & 0xFFFFFFFFL); }
@Deprecated
public int getX() { return x; }
@Deprecated
public int getZ() { return z; }
public int getMinBlockX() { return this.x << 4; }
public int getMinBlockZ() { return this.z << 4; }
public int getMinBlockX() { return x << 4; }
public int getMinBlockZ() { return z << 4; }
public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(this.x << 4, this.z << 4); }
public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(x << 4, z << 4); }
public long getLong() { return toLong(x, z); }
public long getLong() { return toLong(this.x, this.z); }
public double distance(DhChunkPos other)
{
@@ -90,14 +84,14 @@ public class DhChunkPos
{
return true;
}
else if (obj == null || getClass() != obj.getClass())
else if (obj == null || this.getClass() != obj.getClass())
{
return false;
}
else
{
DhChunkPos that = (DhChunkPos) obj;
return x == that.x && z == that.z;
return this.x == that.x && this.z == that.z;
}
}
@@ -105,7 +99,7 @@ public class DhChunkPos
public int hashCode() { return this.hashCode; }
@Override
public String toString() { return "C[" + x + "," + z + "]"; }
public String toString() { return "C[" + this.x + "," + this.z + "]"; }
@@ -25,6 +25,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import com.seibel.distanthorizons.api.enums.config.EGLErrorHandlingMode;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.config.Config;
@@ -65,8 +66,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
*/
public class GLProxy
{
public static final boolean OVERRIDE_VANILLA_GL_LOGGER = ModInfo.IS_DEV_BUILD;
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
@@ -141,7 +140,7 @@ public class GLProxy
}
GL_LOGGER.info("minecraftGlCapabilities:\n" + getVersionInfo(minecraftGlCapabilities));
if (OVERRIDE_VANILLA_GL_LOGGER)
if (Config.Client.Advanced.Debugging.overrideVanillaGLLogger.get())
{
GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, vanillaDebugMessageBuilder), true));
}
@@ -461,29 +460,47 @@ public class GLProxy
private static void logMessage(GLMessage msg)
{
GLMessage.ESeverity s = msg.severity;
if (msg.type == GLMessage.EType.ERROR ||
msg.type == GLMessage.EType.UNDEFINED_BEHAVIOR)
EGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.glErrorHandlingMode.get();
if (errorHandlingMode == EGLErrorHandlingMode.IGNORE)
{
GL_LOGGER.error("GL ERROR {} from {}: {}", msg.id, msg.source, msg.message);
throw new RuntimeException("GL ERROR: " + msg.toString());
return;
}
RuntimeException e = new RuntimeException("GL MESSAGE: " + msg.toString());
switch (s)
if (msg.type == GLMessage.EType.ERROR || msg.type == GLMessage.EType.UNDEFINED_BEHAVIOR)
{
case HIGH:
GL_LOGGER.error("{}", e);
break;
case MEDIUM:
GL_LOGGER.warn("{}", e);
break;
case LOW:
GL_LOGGER.info("{}", e);
break;
case NOTIFICATION:
GL_LOGGER.debug("{}", e);
break;
// critical error
GL_LOGGER.error("GL ERROR " + msg.id + " from " + msg.source + ": " + msg.message);
if (errorHandlingMode == EGLErrorHandlingMode.LOG_THROW)
{
throw new RuntimeException("GL ERROR: " + msg);
}
}
else
{
// non-critical log
GLMessage.ESeverity severity = msg.severity;
RuntimeException ex = new RuntimeException("GL MESSAGE: " + msg);
switch (severity)
{
case HIGH:
GL_LOGGER.error("{}", ex);
break;
case MEDIUM:
GL_LOGGER.warn("{}", ex);
break;
case LOW:
GL_LOGGER.info("{}", ex);
break;
case NOTIFICATION:
GL_LOGGER.debug("{}", ex);
break;
}
}
}
@@ -50,6 +50,7 @@ import org.lwjgl.opengl.GL32;
import java.awt.*;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
/**
* This is where all the magic happens. <br>
@@ -72,11 +73,21 @@ public class LodRenderer
public static boolean transparencyEnabled = true;
public static boolean fakeOceanFloor = true;
public void setupOffset(DhBlockPos pos)
/** used to prevent cleaning up render resources while they are being used */
private static final ReentrantLock renderLock = new ReentrantLock();
public void setupOffset(DhBlockPos pos) throws IllegalStateException
{
Vec3d cam = MC_RENDER.getCameraExactPosition();
Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z));
if (!GL32.glIsProgram(this.shaderProgram.id))
{
throw new IllegalStateException("No GL program exists with the ID: ["+this.shaderProgram.id+"]. This either means a shader program was freed while it was still in use or was never created.");
}
this.shaderProgram.bind();
this.shaderProgram.setModelPos(modelPos);
}
@@ -138,13 +149,24 @@ public class LodRenderer
return;
}
EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "...");
this.rendererClosed = true;
GLProxy.getInstance().recordOpenGlCall(this::cleanup);
this.bufferHandler.close();
EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName());
// wait for the renderer to finish before closing (to prevent closing resources that are currently in use)
renderLock.lock();
try
{
EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "...");
this.cleanup();
this.bufferHandler.close();
EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName());
}
finally
{
renderLock.unlock();
}
}
public void drawLODs(Mat4f baseModelViewMatrix, Mat4f baseProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
@@ -156,169 +178,183 @@ public class LodRenderer
}
// get MC's shader program and save MC's render state so we can restore it later
LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher();
GLState minecraftGlState = new GLState();
if (ENABLE_DUMP_GL_STATE)
if (!renderLock.tryLock())
{
tickLogger.debug("Saving GL state: " + minecraftGlState);
// never lock the render thread, if the lock isn't available don't wait for it
return;
}
drawSaveGLState.end("drawSaveGLState");
// make sure everything has been initialized
GLProxy glProxy = GLProxy.getInstance();
//===================//
// draw params setup //
//===================//
profiler.push("LOD draw setup");
/*---------Set GL State--------*/
GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight());
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
try
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
//GL32.glDisable(GL32.GL_CULL_FACE);
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GL32.glEnable(GL32.GL_CULL_FACE);
}
GL32.glEnable(GL32.GL_DEPTH_TEST);
GL32.glDepthFunc(GL32.GL_LESS);
transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled;
GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent
GL32.glDepthMask(true);
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
/*---------Bind required objects--------*/
// Setup LodRenderProgram and the LightmapTexture if it has not yet been done
// also binds LightmapTexture, VAO, and ShaderProgram
if (!this.isSetupComplete)
{
this.setup();
}
else
{
LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable();
if (newFogConfig != null)
// get MC's shader program and save MC's render state so we can restore it later
LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher();
GLState minecraftGlState = new GLState();
if (ENABLE_DUMP_GL_STATE)
{
this.shaderProgram.free();
this.shaderProgram = new LodRenderProgram(newFogConfig);
FogShader.INSTANCE.free();
FogShader.INSTANCE = new FogShader(newFogConfig);
tickLogger.debug("Saving GL state: " + minecraftGlState);
}
this.shaderProgram.bind();
}
GL32.glActiveTexture(GL32.GL_TEXTURE0);
/*---------Get required data--------*/
int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks);
/*---------Fill uniform data--------*/
this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0,
MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance);
// Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one.
ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper();
lightmap.bind();
if (ENABLE_IBO)
{
this.quadIBO.bind();
}
this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector());
//===========//
// rendering //
//===========//
LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher();
profiler.popPush("LOD Opaque");
// TODO: Directional culling
this.bufferHandler.renderOpaque(this);
if (Config.Client.Advanced.Graphics.Quality.ssao.get())
{
profiler.popPush("LOD SSAO");
SSAORenderer.INSTANCE.render(partialTicks);
drawSaveGLState.end("drawSaveGLState");
// TODO: Fix this file (or check the result is the same) so that SSAORenderer could be deleted
//SSAOShader.INSTANCE.render(partialTicks); // For some reason this looks slightly different :/
}
profiler.popPush("LOD Fog");
// TODO add the model view/projection matrices to the render() function
FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix);
FogShader.INSTANCE.render(partialTicks);
// DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
// make sure everything has been initialized
GLProxy glProxy = GLProxy.getInstance();
GL32.glEnable(GL32.GL_BLEND);
GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA);
this.bufferHandler.renderTransparent(this);
GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it...
//===================//
// draw params setup //
//===================//
profiler.push("LOD draw setup");
/*---------Set GL State--------*/
GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight());
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
//GL32.glDisable(GL32.GL_CULL_FACE);
}
else
{
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
GL32.glEnable(GL32.GL_CULL_FACE);
}
GL32.glEnable(GL32.GL_DEPTH_TEST);
GL32.glDepthFunc(GL32.GL_LESS);
transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled;
GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent
GL32.glDepthMask(true);
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
/*---------Bind required objects--------*/
// Setup LodRenderProgram and the LightmapTexture if it has not yet been done
// also binds LightmapTexture, VAO, and ShaderProgram
if (!this.isSetupComplete)
{
this.setup();
}
else
{
LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable();
if (newFogConfig != null)
{
this.shaderProgram.free();
this.shaderProgram = new LodRenderProgram(newFogConfig);
FogShader.INSTANCE.free();
FogShader.INSTANCE = new FogShader(newFogConfig);
}
this.shaderProgram.bind();
}
GL32.glActiveTexture(GL32.GL_TEXTURE0);
/*---------Get required data--------*/
int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks);
/*---------Fill uniform data--------*/
this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0,
MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance);
// Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one.
ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper();
lightmap.bind();
if (ENABLE_IBO)
{
this.quadIBO.bind();
}
this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector());
//===========//
// rendering //
//===========//
LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher();
profiler.popPush("LOD Opaque");
// TODO: Directional culling
this.bufferHandler.renderOpaque(this);
if (Config.Client.Advanced.Graphics.Quality.ssao.get())
{
profiler.popPush("LOD SSAO");
SSAORenderer.INSTANCE.render(partialTicks);
// TODO: Fix this file (or check the result is the same) so that SSAORenderer could be deleted
//SSAOShader.INSTANCE.render(partialTicks); // For some reason this looks slightly different :/
}
profiler.popPush("LOD Fog");
// TODO add the model view/projection matrices to the render() function
FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix);
FogShader.INSTANCE.render(partialTicks);
}
drawLagSpikeCatcher.end("LodDraw");
//================//
// render cleanup //
//================//
profiler.popPush("LOD cleanup");
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
lightmap.unbind();
if (ENABLE_IBO)
{
this.quadIBO.unbind();
}
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
this.shaderProgram.unbind();
if (Config.Client.Advanced.Debugging.debugWireframeRendering.get())
{
profiler.popPush("Debug wireframes");
// Note: this can be very slow if a lot of boxes are being rendered
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
// DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
profiler.popPush("LOD Transparent");
GL32.glEnable(GL32.GL_BLEND);
GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA);
this.bufferHandler.renderTransparent(this);
GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it...
FogShader.INSTANCE.render(partialTicks);
}
drawLagSpikeCatcher.end("LodDraw");
//================//
// render cleanup //
//================//
profiler.popPush("LOD cleanup");
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
lightmap.unbind();
if (ENABLE_IBO)
{
this.quadIBO.unbind();
}
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
this.shaderProgram.unbind();
if (Config.Client.Advanced.Debugging.debugWireframeRendering.get())
{
profiler.popPush("Debug wireframes");
// Note: this can be very slow if a lot of boxes are being rendered
DebugRenderer.INSTANCE.render(modelViewProjectionMatrix);
profiler.popPush("LOD cleanup");
}
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
minecraftGlState.restore();
drawCleanup.end("LodDrawCleanup");
// end of internal LOD profiling
profiler.pop();
tickLogger.incLogTries();
}
finally
{
renderLock.unlock();
}
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
minecraftGlState.restore();
drawCleanup.end("LodDrawCleanup");
// end of internal LOD profiling
profiler.pop();
tickLogger.incLogTries();
}
@@ -376,7 +412,7 @@ public class LodRenderer
//======================//
/**
* cleanup and free all render objects. REQUIRES to be in render thread
* cleanup and free all render objects. MUST be on the render thread
* (Many objects are Native, outside of JVM, and need manual cleanup)
*/
private void cleanup()
@@ -393,13 +429,20 @@ public class LodRenderer
}
this.isSetupComplete = false;
EVENT_LOGGER.info("Renderer Cleanup Started");
this.shaderProgram.free();
if (this.quadIBO != null)
GLProxy.getInstance().recordOpenGlCall(() ->
{
this.quadIBO.destroy(false);
}
EVENT_LOGGER.info("Renderer Cleanup Complete");
EVENT_LOGGER.info("Renderer Cleanup Started");
this.shaderProgram.free();
this.shaderProgram = null;
if (this.quadIBO != null)
{
this.quadIBO.destroy(false);
}
EVENT_LOGGER.info("Renderer Cleanup Complete");
});
}
}
@@ -221,11 +221,11 @@ public class LodUtil
public Pos2D next()
{
DhChunkPos pos = posIter.next();
return new Pos2D(pos.getX(), pos.getZ());
return new Pos2D(pos.x, pos.z);
}
},
MC_CLIENT.getPlayerChunkPos().getX() - renderDist,
MC_CLIENT.getPlayerChunkPos().getZ() - renderDist,
MC_CLIENT.getPlayerChunkPos().x - renderDist,
MC_CLIENT.getPlayerChunkPos().z - renderDist,
renderDist * 2 + 1);
}
@@ -58,11 +58,11 @@ public class RenderUtil
*/
public static boolean isChunkPosInLoadedArea(DhChunkPos pos, DhChunkPos center)
{
return (pos.getX() >= center.getX() - MC_RENDER.getRenderDistance()
&& pos.getX() <= center.getX() + MC_RENDER.getRenderDistance())
return (pos.x >= center.x - MC_RENDER.getRenderDistance()
&& pos.x <= center.x + MC_RENDER.getRenderDistance())
&&
(pos.getZ() >= center.getZ() - MC_RENDER.getRenderDistance()
&& pos.getZ() <= center.getZ() + MC_RENDER.getRenderDistance());
(pos.z >= center.z - MC_RENDER.getRenderDistance()
&& pos.z <= center.z + MC_RENDER.getRenderDistance());
}
/**
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.Abstrac
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
/**
* This handles creating abstract wrapper objects.
@@ -42,7 +42,11 @@ public interface IWrapperFactory extends IBindable
IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException;
IBlockStateWrapper getAirBlockStateWrapper();
HashMap<String, ? extends IBlockStateWrapper> getRendererIgnoredBlocks();
/**
* Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered. <br>
* Generally this contains blocks like: air, barriers, light blocks, etc.
*/
HashSet<IBlockStateWrapper> getRendererIgnoredBlocks(ILevelWrapper levelWrapper);
/**
* Specifically designed to be used with the API.
@@ -6,9 +6,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
/** A Minecraft version independent way of handling Blocks. */
public interface IBlockStateWrapper extends IDhApiBlockStateWrapper
{
String serialize();
ILevelWrapper getLevelWrapper();
String getSerialString();
/**
* Returning a value of 0 means the block is completely transparent. <br.
@@ -113,8 +113,8 @@ public interface IMinecraftRenderWrapper extends IBindable
int chunkDist = this.getRenderDistance() + 1; // For some reason having '+1' is actually closer to real value
DhChunkPos centerChunkPos = mcWrapper.getPlayerChunkPos();
int centerChunkX = centerChunkPos.getX();
int centerChunkZ = centerChunkPos.getZ();
int centerChunkX = centerChunkPos.x;
int centerChunkZ = centerChunkPos.z;
int chunkDist2Mul4 = chunkDist * chunkDist * 4;
// add every position within render distance
@@ -29,6 +29,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
public interface IBiomeWrapper extends IDhApiBiomeWrapper, IBindable
{
String getName();
String serialize();
ILevelWrapper getLevelWrapper();
String getSerialString();
}
@@ -392,7 +392,16 @@
"Allow Unsafe UI Values",
"distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip":
"If enabled, very limited config input validation will be performed. \n\nWarning: enabling this can cause instability or crashing, use at your own risk. \nNote: this is option isn't saved between sessions.",
"distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger":
"Override Vanilla OpenGL Logger",
"distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger.@tooltip":
"Requires a reboot to change.",
"distanthorizons.config.client.advanced.debugging.glErrorHandlingMode":
"OpenGL Error Handling Mode",
"distanthorizons.config.client.advanced.debugging.glErrorHandlingMode.@tooltip":
"Defines how OpenGL errors are handled. \nMay incorrectly catch OpenGL errors thrown by other mods.",
"distanthorizons.config.client.advanced.buffers":
"Buffers",
"distanthorizons.config.client.advanced.buffers.gpuUploadMethod":
@@ -691,6 +700,13 @@
"distanthorizons.config.enum.EDebugRendering.SHOW_RENDER_SOURCE_FLAG":
"Show render source flag",
"distanthorizons.config.enum.EGLErrorHandlingMode.IGNORE":
"Ignore",
"distanthorizons.config.enum.EGLErrorHandlingMode.LOG":
"Log",
"distanthorizons.config.enum.EGLErrorHandlingMode.LOG_THROW":
"Log-Throw",
"distanthorizons.config.enum.ELoggerMode.DISABLED":
"Disabled",
"distanthorizons.config.enum.ELoggerMode.LOG_ALL_TO_FILE":
+1 -1
View File
@@ -19,7 +19,7 @@
<File name="important_logs_file" fileName="logs/important.log">
<Filters>
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
<PatternLayout>
<pattern>%d %p %c{1.} [%t] %m%n</pattern>