9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-21 07:49:18 +00:00

rework config sections

This commit is contained in:
NONPLAYT
2025-06-11 18:52:05 +03:00
parent beb51ae149
commit 3894dc0c97
56 changed files with 1136 additions and 989 deletions

View File

@@ -15,7 +15,7 @@ import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import java.util.Map;
@@ -32,7 +32,7 @@ public class PufferfishSentryAppender extends AbstractAppender {
@Override
public void append(LogEvent logEvent) {
if (logEvent.getLevel().isMoreSpecificThan(logLevel) && (logEvent.getThrown() != null || !DivineConfig.onlyLogThrown)) {
if (logEvent.getLevel().isMoreSpecificThan(logLevel) && (logEvent.getThrown() != null || !DivineConfig.MiscCategory.onlyLogThrown)) {
try {
logException(logEvent);
} catch (Exception e) {

View File

@@ -4,7 +4,7 @@ import io.sentry.Sentry;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
public class SentryManager {
private static final Logger LOGGER = LogManager.getLogger("SentryManager");
@@ -26,7 +26,7 @@ public class SentryManager {
initialized = true;
Sentry.init(options -> {
options.setDsn(DivineConfig.sentryDsn);
options.setDsn(DivineConfig.MiscCategory.sentryDsn);
options.setMaxBreadcrumbs(100);
});

View File

@@ -1,583 +0,0 @@
package org.bxteam.divinemc;
import com.google.common.base.Throwables;
import gg.pufferfish.pufferfish.simd.SIMDDetection;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import org.bxteam.divinemc.entity.pathfinding.PathfindTaskRejectPolicy;
import org.bxteam.divinemc.region.LinearImplementation;
import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
import org.bxteam.divinemc.server.chunk.ChunkTaskPriority;
import org.jetbrains.annotations.Nullable;
import org.simpleyaml.configuration.comments.CommentType;
import org.simpleyaml.configuration.file.YamlFile;
import org.simpleyaml.exceptions.InvalidConfigurationException;
import org.bxteam.divinemc.region.RegionFileFormat;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@SuppressWarnings({"unused", "SameParameterValue"})
public class DivineConfig {
private static final String HEADER = """
This is the main configuration file for DivineMC.
If you need help with the configuration or have any questions related to DivineMC,
join us in our Discord server.
Discord: https://discord.gg/qNyybSSPm5
Docs: https://bxteam.org/docs/divinemc
Downloads: https://github.com/BX-Team/DivineMC/releases""";
public static final Logger LOGGER = LogManager.getLogger(DivineConfig.class.getSimpleName());
public static final int CONFIG_VERSION = 5;
private static File configFile;
public static final YamlFile config = new YamlFile();
private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
ConfigurationSection newSection = new MemoryConfiguration();
for (String key : section.getKeys(false)) {
if (section.isConfigurationSection(key)) {
newSection.set(key, convertToBukkit(section.getConfigurationSection(key)));
} else {
newSection.set(key, section.get(key));
}
}
return newSection;
}
public static ConfigurationSection getConfigCopy() {
return convertToBukkit(config);
}
public static void init(File configFile) throws IOException {
DivineConfig.configFile = configFile;
if (configFile.exists()) {
try {
config.load(configFile);
} catch (InvalidConfigurationException e) {
throw new IOException(e);
}
}
getInt("version", CONFIG_VERSION);
config.options().header(HEADER);
readConfig(DivineConfig.class, null);
}
static void readConfig(Class<?> clazz, Object instance) throws IOException {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers())) {
if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
LOGGER.error("Error invoking {}", method, ex);
}
}
}
}
config.save(configFile);
}
private static void setComment(String key, String... comment) {
if (config.contains(key)) {
config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
}
}
private static void ensureDefault(String key, Object defaultValue, String... comment) {
if (!config.contains(key)) config.set(key, defaultValue);
if (comment.length > 0) config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
}
private static boolean getBoolean(String key, boolean defaultValue, String... comment) {
return getBoolean(key, null, defaultValue, comment);
}
private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getBoolean(key, defaultValue);
}
private static int getInt(String key, int defaultValue, String... comment) {
return getInt(key, null, defaultValue, comment);
}
private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getInt(key, defaultValue);
}
private static double getDouble(String key, double defaultValue, String... comment) {
return getDouble(key, null, defaultValue, comment);
}
private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getDouble(key, defaultValue);
}
private static long getLong(String key, long defaultValue, String... comment) {
return getLong(key, null, defaultValue, comment);
}
private static long getLong(String key, @Nullable String oldKey, long defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getLong(key, defaultValue);
}
private static String getString(String key, String defaultValue, String... comment) {
return getOldString(key, null, defaultValue, comment);
}
private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getString(key, defaultValue);
}
private static List<String> getStringList(String key, List<String> defaultValue, String... comment) {
return getStringList(key, null, defaultValue, comment);
}
private static List<String> getStringList(String key, @Nullable String oldKey, List<String> defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getStringList(key);
}
public static boolean enableParallelWorldTicking = false;
public static int parallelThreadCount = 4;
public static boolean logContainerCreationStacktraces = false;
public static boolean disableHardThrow = false;
public static boolean pwtCompatabilityMode = false;
private static void parallelWorldTicking() {
enableParallelWorldTicking = getBoolean("settings.parallel-world-ticking.enable", enableParallelWorldTicking,
"Enables Parallel World Ticking, which executes each worlds tick in a separate thread while ensuring that all worlds complete their tick before the next cycle begins.",
"",
"Read more info about this feature at https://bxteam.org/docs/divinemc/features/parallel-world-ticking");
parallelThreadCount = getInt("settings.parallel-world-ticking.thread-count", parallelThreadCount);
logContainerCreationStacktraces = getBoolean("settings.parallel-world-ticking.log-container-creation-stacktraces", logContainerCreationStacktraces);
disableHardThrow = getBoolean("settings.parallel-world-ticking.disable-hard-throw", disableHardThrow,
"Disables annoying 'not on main thread' throws. But, THIS IS NOT RECOMMENDED because you SHOULD FIX THE ISSUES THEMSELVES instead of RISKING DATA CORRUPTION! If you lose something, take the blame on yourself.");
pwtCompatabilityMode = getBoolean("settings.parallel-world-ticking.compatability-mode", pwtCompatabilityMode,
"Enables compatibility mode for plugins that are not compatible with Parallel World Ticking. This makes all async tasks run synchronously.");
}
public static long chunkDataCacheSoftLimit = 8192L;
public static long chunkDataCacheLimit = 32678L;
public static int maxViewDistance = 32;
public static int playerNearChunkDetectionRange = 128;
public static ChunkSystemAlgorithms chunkWorkerAlgorithm = ChunkSystemAlgorithms.C2ME;
public static ChunkTaskPriority chunkTaskPriority = ChunkTaskPriority.EUCLIDEAN_CIRCLE_PATTERN;
public static int threadPoolPriority = Thread.NORM_PRIORITY + 1;
public static boolean asyncChunkSendingEnabled = true;
public static boolean enableSecureSeed = false;
public static boolean smoothBedrockLayer = false;
public static boolean slopesVisualFix = false;
public static boolean enableDensityFunctionCompiler = false;
public static boolean enableStructureLayoutOptimizer = true;
public static boolean deduplicateShuffledTemplatePoolElementList = false;
private static void chunkSettings() {
chunkDataCacheSoftLimit = getLong("settings.chunks.chunk-data-cache-soft-limit", chunkDataCacheSoftLimit);
chunkDataCacheLimit = getLong("settings.chunks.chunk-data-cache-limit", chunkDataCacheLimit);
maxViewDistance = getInt("settings.chunks.max-view-distance", maxViewDistance,
"Changes the maximum view distance for the server, allowing clients to have render distances higher than 32");
playerNearChunkDetectionRange = getInt("settings.chunks.player-near-chunk-detection-range", playerNearChunkDetectionRange,
"In certain checks, like if a player is near a chunk(primarily used for spawning), it checks if the player is within a certain",
"circular range of the chunk. This configuration allows configurability of the distance(in blocks) the player must be to pass the check.",
"",
"This value is used in the calculation 'range/16' to get the distance in chunks any player must be to allow the check to pass.",
"By default, this range is computed to 8, meaning a player must be within an 8 chunk radius of a chunk position to pass.",
"Keep in mind the result is rounded to the nearest whole number.");
if (playerNearChunkDetectionRange < 0) {
LOGGER.warn("Invalid player near chunk detection range: {}, resetting to default (128)", playerNearChunkDetectionRange);
playerNearChunkDetectionRange = 128;
}
chunkWorkerAlgorithm = ChunkSystemAlgorithms.valueOf(getString("settings.chunks.chunk-worker-algorithm", chunkWorkerAlgorithm.name(),
"Modifies what algorithm the chunk system will use to define thread counts.",
"Valid values:",
" - MOONRISE: Default algorithm, used by default in Paper",
" - C2ME: Algorithm used by C2ME (old)",
" - C2ME_NEW: Modern algorithm used by C2ME"));
chunkTaskPriority = ChunkTaskPriority.valueOf(getString("settings.chunks.chunk-task-priority", chunkTaskPriority.name(),
"Sets the algorithm for determining chunk task priorities (generation, loading and etc.).",
"Valid values:",
" - EUCLIDEAN_CIRCLE_PATTERN: Euclidean distance squared algorithm, chunk priorities will be ordered in a circle pattern",
" - DEFAULT_DIAMOND_PATTERN: Default one, chunk priorities will be ordered in a diamond pattern"));
threadPoolPriority = getInt("settings.chunks.thread-pool-priority", threadPoolPriority,
"Sets the priority of the thread pool used for chunk generation");
enableSecureSeed = getBoolean("settings.chunks.enable-secure-seed", enableSecureSeed,
"This feature is based on Secure Seed mod by Earthcomputer.",
"",
"Terrain and biome generation remains the same, but all the ores and structures are generated with 1024-bit seed, instead of the usual 64-bit seed.",
"This seed is almost impossible to crack, and there are no weird links between structures.");
asyncChunkSendingEnabled = getBoolean("settings.chunks.enable-async-chunk-sending", asyncChunkSendingEnabled,
"Makes chunk sending asynchronous, which can significantly reduce main thread load when many players are loading chunks.");
smoothBedrockLayer = getBoolean("settings.chunks.smooth-bedrock-layer", smoothBedrockLayer,
"Smoothens the bedrock layer at the bottom of overworld, and on the top of nether during the world generation.");
slopesVisualFix = getBoolean("settings.chunks.slopes-visual-fix", slopesVisualFix,
"Fixes MC-258859, fixing slopes visual bug in biomes like Snowy Slopes, Frozen Peaks, Jagged Peaks, and including Terralith.");
enableDensityFunctionCompiler = getBoolean("settings.chunks.experimental.enable-density-function-compiler", enableDensityFunctionCompiler,
"Whether to use density function compiler to accelerate world generation",
"",
"Density function: https://minecraft.wiki/w/Density_function",
"",
"This functionality compiles density functions from world generation",
"datapacks (including vanilla generation) to JVM bytecode to increase",
"performance by allowing JVM JIT to better optimize the code.",
"All functions provided by vanilla are implemented.");
enableStructureLayoutOptimizer = getBoolean("settings.chunks.experimental.enable-structure-layout-optimizer", enableStructureLayoutOptimizer,
"Enables a port of the mod StructureLayoutOptimizer, which optimizes general Jigsaw structure generation");
deduplicateShuffledTemplatePoolElementList = getBoolean("settings.chunks.experimental.deduplicate-shuffled-template-pool-element-list", deduplicateShuffledTemplatePoolElementList,
"Whether to use an alternative strategy to make structure layouts generate slightly even faster than",
"the default optimization this mod has for template pool weights. This alternative strategy works by",
"changing the list of pieces that structures collect from the template pool to not have duplicate entries.",
"",
"This will not break the structure generation, but it will make the structure layout different than",
"if this config was off (breaking vanilla seed parity). The cost of speed may be worth it in large",
"modpacks where many structure mods are using very high weight values in their template pools.");
}
public static boolean skipUselessSecondaryPoiSensor = true;
public static boolean clumpOrbs = true;
public static boolean ignoreMovedTooQuicklyWhenLagging = true;
public static boolean alwaysAllowWeirdMovement = true;
public static boolean updateSuppressionCrashFix = true;
public static boolean enableSuffocationOptimization = true;
public static boolean useCompactBitStorage = false;
public static boolean fixIncorrectBounceLogic = false;
public static boolean forceMinecraftCommand = false;
public static boolean disableLeafDecay = false;
public static boolean commandBlockParseResultsCaching = true;
public static boolean enableAsyncSpawning = true;
public static boolean sheepOptimization = true;
public static boolean hopperThrottleWhenFull = false;
public static int hopperThrottleSkipTicks = 0;
private static void miscSettings() {
skipUselessSecondaryPoiSensor = getBoolean("settings.misc.skip-useless-secondary-poi-sensor", skipUselessSecondaryPoiSensor);
clumpOrbs = getBoolean("settings.misc.clump-orbs", clumpOrbs,
"Clumps experience orbs together to reduce entity count");
ignoreMovedTooQuicklyWhenLagging = getBoolean("settings.misc.ignore-moved-too-quickly-when-lagging", ignoreMovedTooQuicklyWhenLagging,
"Improves general gameplay experience of the player when the server is lagging, as they won't get lagged back (message 'moved too quickly')");
alwaysAllowWeirdMovement = getBoolean("settings.misc.always-allow-weird-movement", alwaysAllowWeirdMovement,
"Means ignoring messages like 'moved too quickly' and 'moved wrongly'");
updateSuppressionCrashFix = getBoolean("settings.misc.update-suppression-crash-fix", updateSuppressionCrashFix);
enableSuffocationOptimization = getBoolean("settings.misc.enable-suffocation-optimization", enableSuffocationOptimization,
"Optimizes the suffocation check by selectively skipping the check in a way that still appears vanilla.",
"This option should be left enabled on most servers, but is provided as a configuration option if the vanilla deviation is undesirable.");
useCompactBitStorage = getBoolean("settings.misc.use-compact-bit-storage", useCompactBitStorage,
"Fixes memory waste caused by sending empty chunks as if they contain blocks. Can significantly reduce memory usage.");
fixIncorrectBounceLogic = getBoolean("settings.misc.fix-incorrect-bounce-logic", fixIncorrectBounceLogic,
"Fixes incorrect bounce logic in SlimeBlock.");
forceMinecraftCommand = getBoolean("settings.misc.force-minecraft-command", forceMinecraftCommand,
"Whether to force the use of vanilla commands over plugin commands.");
disableLeafDecay = getBoolean("settings.misc.disable-leaf-decay", disableLeafDecay,
"Disables leaf block decay.");
commandBlockParseResultsCaching = getBoolean("settings.misc.command-block-parse-results-caching", commandBlockParseResultsCaching,
"Caches the parse results of command blocks, can significantly reduce performance impact.");
enableAsyncSpawning = getBoolean("settings.misc.enable-async-spawning", enableAsyncSpawning,
"Enables optimization that will offload much of the computational effort involved with spawning new mobs to a different thread.");
sheepOptimization = getBoolean("settings.misc.sheep-optimization", sheepOptimization,
"Enables optimization from Carpet Fixes mod, using a prebaked list of all the possible colors and combinations for sheep.");
hopperThrottleWhenFull = getBoolean("settings.misc.hopper-throttle-when-full.enabled", hopperThrottleWhenFull,
"When enabled, hoppers will throttle if target container is full.");
hopperThrottleSkipTicks = getInt("settings.misc.hopper-throttle-when-full.skip-ticks", hopperThrottleSkipTicks,
"The amount of ticks to skip when the hopper is throttled.");
}
public static String sentryDsn = "";
public static String logLevel = "WARN";
public static boolean onlyLogThrown = true;
private static void sentrySettings() {
sentryDsn = getString("settings.sentry.dsn", sentryDsn,
"The DSN for Sentry, a service that provides real-time crash reporting that helps you monitor and fix crashes in real time. Leave blank to disable. Obtain link at https://sentry.io");
logLevel = getString("settings.sentry.log-level", logLevel,
"Logs with a level higher than or equal to this level will be recorded.");
onlyLogThrown = getBoolean("settings.sentry.only-log-thrown", onlyLogThrown,
"Only log Throwable exceptions to Sentry.");
if (sentryDsn != null && !sentryDsn.isBlank()) gg.pufferfish.pufferfish.sentry.SentryManager.init(Level.getLevel(logLevel));
}
public static boolean disableDisconnectSpam = false;
public static boolean gracefulTeleportHandling = false;
public static boolean dontRespondPingBeforeStart = true;
public static boolean playerProfileResultCachingEnabled = true;
public static int playerProfileResultCachingTimeout = 1440;
private static void networkSettings() {
disableDisconnectSpam = getBoolean("settings.network.disable-disconnect-spam", disableDisconnectSpam,
"Prevents players being disconnected by 'disconnect.spam' when sending too many chat packets");
gracefulTeleportHandling = getBoolean("settings.network.graceful-teleport-handling", gracefulTeleportHandling ,
"Disables being disconnected from 'multiplayer.disconnect.invalid_player_movement' (also declines the packet handling).");
dontRespondPingBeforeStart = getBoolean("settings.network.dont-respond-ping-before-start", dontRespondPingBeforeStart,
"Prevents the server from responding to pings before the server is fully booted.");
playerProfileResultCachingEnabled = getBoolean("settings.network.player-profile-result-caching.enabled", playerProfileResultCachingEnabled,
"Enables caching of player profile results on first join.");
playerProfileResultCachingTimeout = getInt("settings.network.player-profile-result-caching.timeout", playerProfileResultCachingTimeout,
"The amount of time in minutes to cache player profile results.");
}
public static boolean enableFasterTntOptimization = true;
public static boolean explosionNoBlockDamage = false;
public static double tntRandomRange = -1;
private static void tntOptimization() {
enableFasterTntOptimization = getBoolean("settings.tnt-optimization.enable-faster-tnt-optimization", enableFasterTntOptimization);
explosionNoBlockDamage = getBoolean("settings.tnt-optimization.explosion-no-block-damage", explosionNoBlockDamage);
tntRandomRange = getDouble("settings.tnt-optimization.tnt-random-range", tntRandomRange);
}
public static boolean lagCompensationEnabled = true;
public static boolean blockEntityAcceleration = false;
public static boolean blockBreakingAcceleration = true;
public static boolean eatingAcceleration = true;
public static boolean potionEffectAcceleration = true;
public static boolean fluidAcceleration = true;
public static boolean pickupAcceleration = true;
public static boolean portalAcceleration = true;
public static boolean timeAcceleration = true;
public static boolean randomTickSpeedAcceleration = true;
private static void lagCompensation() {
lagCompensationEnabled = getBoolean("settings.lag-compensation.enabled", lagCompensationEnabled, "Improves the player experience when TPS is low");
blockEntityAcceleration = getBoolean("settings.lag-compensation.block-entity-acceleration", blockEntityAcceleration);
blockBreakingAcceleration = getBoolean("settings.lag-compensation.block-breaking-acceleration", blockBreakingAcceleration);
eatingAcceleration = getBoolean("settings.lag-compensation.eating-acceleration", eatingAcceleration);
potionEffectAcceleration = getBoolean("settings.lag-compensation.potion-effect-acceleration", potionEffectAcceleration);
fluidAcceleration = getBoolean("settings.lag-compensation.fluid-acceleration", fluidAcceleration);
pickupAcceleration = getBoolean("settings.lag-compensation.pickup-acceleration", pickupAcceleration);
portalAcceleration = getBoolean("settings.lag-compensation.portal-acceleration", portalAcceleration);
timeAcceleration = getBoolean("settings.lag-compensation.time-acceleration", timeAcceleration);
randomTickSpeedAcceleration = getBoolean("settings.lag-compensation.random-tick-speed-acceleration", randomTickSpeedAcceleration);
}
public static boolean noChatReportsEnabled = false;
public static boolean noChatReportsAddQueryData = true;
public static boolean noChatReportsConvertToGameMessage = true;
public static boolean noChatReportsDebugLog = false;
public static boolean noChatReportsDemandOnClient = false;
public static String noChatReportsDisconnectDemandOnClientMessage = "You do not have No Chat Reports, and this server is configured to require it on client!";
private static void noChatReports() {
noChatReportsEnabled = getBoolean("settings.no-chat-reports.enabled", noChatReportsEnabled,
"Enables or disables the No Chat Reports feature");
noChatReportsAddQueryData = getBoolean("settings.no-chat-reports.add-query-data", noChatReportsAddQueryData,
"Should server include extra query data to help clients know that your server is secure");
noChatReportsConvertToGameMessage = getBoolean("settings.no-chat-reports.convert-to-game-message", noChatReportsConvertToGameMessage,
"Should the server convert all player messages to system messages");
noChatReportsDebugLog = getBoolean("settings.no-chat-reports.debug-log", noChatReportsDebugLog);
noChatReportsDemandOnClient = getBoolean("settings.no-chat-reports.demand-on-client", noChatReportsDemandOnClient,
"Should the server require No Chat Reports on the client side");
noChatReportsDisconnectDemandOnClientMessage = getString("settings.no-chat-reports.disconnect-demand-on-client-message", noChatReportsDisconnectDemandOnClientMessage,
"Message to send to the client when they are disconnected for not having No Chat Reports");
}
public static boolean virtualThreadsEnabled = false;
public static boolean virtualBukkitScheduler = false;
public static boolean virtualChatScheduler = false;
public static boolean virtualAuthenticatorScheduler = false;
public static boolean virtualTabCompleteScheduler = false;
public static boolean virtualAsyncExecutor = false;
public static boolean virtualCommandBuilderScheduler = false;
public static boolean virtualProfileLookupPool = false;
public static boolean virtualServerTextFilterPool = false;
private static void virtualThreads() {
virtualThreadsEnabled = getBoolean("settings.virtual-threads.enabled", virtualThreadsEnabled,
"Enables use of virtual threads that was added in Java 21");
virtualBukkitScheduler = getBoolean("settings.virtual-threads.bukkit-scheduler", virtualBukkitScheduler,
"Uses virtual threads for the Bukkit scheduler.");
virtualChatScheduler = getBoolean("settings.virtual-threads.chat-scheduler", virtualChatScheduler,
"Uses virtual threads for the Chat scheduler.");
virtualAuthenticatorScheduler = getBoolean("settings.virtual-threads.authenticator-scheduler", virtualAuthenticatorScheduler,
"Uses virtual threads for the Authenticator scheduler.");
virtualTabCompleteScheduler = getBoolean("settings.virtual-threads.tab-complete-scheduler", virtualTabCompleteScheduler,
"Uses virtual threads for the Tab Complete scheduler.");
virtualAsyncExecutor = getBoolean("settings.virtual-threads.async-executor", virtualAsyncExecutor,
"Uses virtual threads for the MCUtil async executor.");
virtualCommandBuilderScheduler = getBoolean("settings.virtual-threads.command-builder-scheduler", virtualCommandBuilderScheduler,
"Uses virtual threads for the Async Command Builder Thread Pool.");
virtualProfileLookupPool = getBoolean("settings.virtual-threads.profile-lookup-pool", virtualProfileLookupPool,
"Uses virtual threads for the Profile Lookup Pool, that is used for fetching player profiles.");
virtualServerTextFilterPool = getBoolean("settings.virtual-threads.server-text-filter-pool", virtualServerTextFilterPool,
"Uses virtual threads for the server text filter pool.");
}
public static boolean dabEnabled = true;
public static int dabStartDistance = 12;
public static int dabStartDistanceSquared;
public static int dabMaximumActivationFrequency = 20;
public static int dabActivationDistanceMod = 8;
public static boolean dabDontEnableIfInWater = false;
public static List<String> dabBlackedEntities = new ArrayList<>();
private static void dab() {
dabEnabled = getBoolean("settings.dab.enabled", dabEnabled,
"Enables DAB feature");
dabStartDistance = getInt("settings.dab.start-distance", dabStartDistance,
"This value determines how far away an entity has to be");
dabStartDistanceSquared = dabStartDistance * dabStartDistance;
dabMaximumActivationFrequency = getInt("settings.dab.maximum-activation-frequency", dabMaximumActivationFrequency,
"How often in ticks, the furthest entity will get their pathfinders and behaviors ticked.");
dabActivationDistanceMod = getInt("settings.dab.activation-distance-mod", dabActivationDistanceMod,
"Modifies an entity's tick frequency.",
"The exact calculation to obtain the tick frequency for an entity is: freq = (distanceToPlayer^2) / (2^value), where value is this configuration setting.",
"Large servers may want to reduce the value to 7, but this value should never be reduced below 6. If you want further away entities to tick more often, set the value to 9");
dabDontEnableIfInWater = getBoolean("settings.dab.dont-enable-if-in-water", dabDontEnableIfInWater,
"When this is enabled, non-aquatic entities in the water will not be affected by DAB.");
dabBlackedEntities = getStringList("settings.dab.blacked-entities", dabBlackedEntities,
"Use this configuration option to specify that certain entities should not be impacted by DAB.");
setComment("settings.dab",
"DAB is an optimization that reduces the frequency of brain ticks. Brain ticks are very intensive, which is why they",
"are limited. DAB can be tuned to meet your preferred performance-experience tradeoff. The farther away entities",
"are from players, the less frequently their brains will be ticked. While DAB does impact the AI goal selector",
"behavior of all entities, the only entities who's brain ticks are limited are: Villager, Axolotl, Hoglin, Zombified Piglin and Goat");
for (EntityType<?> entityType : BuiltInRegistries.ENTITY_TYPE) {
entityType.dabEnabled = true;
}
final String DEFAULT_PREFIX = ResourceLocation.DEFAULT_NAMESPACE + ResourceLocation.NAMESPACE_SEPARATOR;
for (String name : dabBlackedEntities) {
String lowerName = name.toLowerCase(Locale.ROOT);
String typeId = lowerName.startsWith(DEFAULT_PREFIX) ? lowerName : DEFAULT_PREFIX + lowerName;
EntityType.byString(typeId).ifPresentOrElse(entityType -> entityType.dabEnabled = false, () -> LOGGER.warn("Unknown entity {}, in {}", name, "settings.dab.blacked-entities"));
}
}
public static boolean asyncPathfinding = true;
public static int asyncPathfindingMaxThreads = 2;
public static int asyncPathfindingKeepalive = 60;
public static int asyncPathfindingQueueSize = 0;
public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL;
private static void asyncPathfinding() {
asyncPathfinding = getBoolean("settings.async-pathfinding.enable", asyncPathfinding);
asyncPathfindingMaxThreads = getInt("settings.async-pathfinding.max-threads", asyncPathfindingMaxThreads);
asyncPathfindingKeepalive = getInt("settings.async-pathfinding.keepalive", asyncPathfindingKeepalive);
asyncPathfindingQueueSize = getInt("settings.async-pathfinding.queue-size", asyncPathfindingQueueSize);
final int maxThreads = Runtime.getRuntime().availableProcessors();
if (asyncPathfindingMaxThreads < 0) {
asyncPathfindingMaxThreads = Math.max(maxThreads + asyncPathfindingMaxThreads, 1);
} else if (asyncPathfindingMaxThreads == 0) {
asyncPathfindingMaxThreads = Math.max(maxThreads / 4, 1);
}
if (!asyncPathfinding) {
asyncPathfindingMaxThreads = 0;
} else {
LOGGER.info("Using {} threads for Async Pathfinding", asyncPathfindingMaxThreads);
}
if (asyncPathfindingQueueSize <= 0) asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256;
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(getString("settings.async-pathfinding.reject-policy", maxThreads >= 12 && asyncPathfindingQueueSize < 512 ? PathfindTaskRejectPolicy.FLUSH_ALL.toString() : PathfindTaskRejectPolicy.CALLER_RUNS.toString(),
"The policy to use when the queue is full and a new task is submitted.",
"FLUSH_ALL: All pending tasks will be run on server thread.",
"CALLER_RUNS: Newly submitted task will be run on server thread."));
}
public static boolean multithreadedEnabled = true;
public static boolean multithreadedCompatModeEnabled = false;
public static int asyncEntityTrackerMaxThreads = 1;
public static int asyncEntityTrackerKeepalive = 60;
public static int asyncEntityTrackerQueueSize = 0;
private static void multithreadedTracker() {
multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled,
"Make entity tracking saving asynchronously, can improve performance significantly,",
"especially in some massive entities in small area situations.");
multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled,
"Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed.",
"Compat mode fixes visible issues with player type NPCs of Citizens.",
"But we recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC and etc.");
asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
asyncEntityTrackerQueueSize = getInt("settings.multithreaded-tracker.queue-size", asyncEntityTrackerQueueSize);
if (asyncEntityTrackerMaxThreads < 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
} else if (asyncEntityTrackerMaxThreads == 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
}
if (!multithreadedEnabled) {
asyncEntityTrackerMaxThreads = 0;
} else {
LOGGER.info("Using {} threads for Async Entity Tracker", asyncEntityTrackerMaxThreads);
}
if (asyncEntityTrackerQueueSize <= 0) asyncEntityTrackerQueueSize = asyncEntityTrackerMaxThreads * 384;
}
public static RegionFileFormat regionFormatTypeName = RegionFileFormat.ANVIL;
public static LinearImplementation linearImplementation = LinearImplementation.V2;
public static int linearFlushMaxThreads = 4;
public static int linearFlushDelay = 100;
public static boolean linearUseVirtualThread = false;
public static int linearCompressionLevel = 1;
private static void linearRegionFormat() {
regionFormatTypeName = RegionFileFormat.fromName(getString("settings.linear-region-format.type", regionFormatTypeName.name(),
"The type of region file format to use for storing chunk data.",
"Valid values:",
" - LINEAR: Linear region file format",
" - ANVIL: Anvil region file format (default)"));
linearImplementation = LinearImplementation.valueOf(getString("settings.linear-region-format.implementation", linearImplementation.name(),
"The implementation of the linear region file format to use.",
"Valid values:",
" - V1: Basic and default linear implementation",
" - V2: Introduces a grid-based compression scheme for better data management and flexibility (default)"));
linearFlushMaxThreads = getInt("settings.linear-region-format.flush-max-threads", linearFlushMaxThreads,
"The maximum number of threads to use for flushing linear region files.",
"If this value is less than or equal to 0, it will be set to the number of available processors + this value.");
linearFlushDelay = getInt("settings.linear-region-format.flush-delay", linearFlushDelay,
"The delay in milliseconds to wait before flushing next files.");
linearUseVirtualThread = getBoolean("settings.linear-region-format.use-virtual-thread", linearUseVirtualThread,
"Whether to use virtual threads for flushing.");
linearCompressionLevel = getInt("settings.linear-region-format.compression-level", linearCompressionLevel,
"The compression level to use for the linear region file format.");
setComment("settings.linear-region-format",
"The linear region file format is a custom region file format that is designed to be more efficient than the ANVIL format.",
"It uses uses ZSTD compression instead of ZLIB. This format saves about 50% of disk space.",
"Read more information about linear region format at https://github.com/xymb-endcrystalme/LinearRegionFileFormatTools",
"WARNING: If you are want to use this format, make sure to create backup of your world before switching to it, there is potential risk to lose chunk data.");
if (regionFormatTypeName == RegionFileFormat.UNKNOWN) {
LOGGER.error("Unknown region file type: {}, falling back to ANVIL format.", regionFormatTypeName);
regionFormatTypeName = RegionFileFormat.ANVIL;
}
if (linearFlushMaxThreads <= 0) {
linearFlushMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + linearFlushMaxThreads, 1);
}
if (linearCompressionLevel > 22 || linearCompressionLevel < 1) {
LOGGER.warn("Invalid linear compression level: {}, resetting to default (1)", playerNearChunkDetectionRange);
linearCompressionLevel = 1;
}
}
}

View File

@@ -6,7 +6,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.PermissionDefault;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.bxteam.divinemc.command.DivineCommand;
import org.bxteam.divinemc.command.DivineSubCommandPermission;
import org.checkerframework.checker.nullness.qual.NonNull;
@@ -35,7 +35,7 @@ public final class MSPTCommand extends DivineSubCommandPermission {
@Override
public boolean execute(CommandSender sender, String subCommand, String[] args) {
if (!DivineConfig.enableParallelWorldTicking) {
if (!DivineConfig.AsyncCategory.enableParallelWorldTicking) {
sender.sendMessage(Component.text("Per-world MSPT tracking is only available when parallel world ticking is enabled.", RED));
sender.sendMessage(Component.text("Please enable it in divinemc.yml to use this command.", GRAY));
return true;
@@ -160,7 +160,7 @@ public final class MSPTCommand extends DivineSubCommandPermission {
@Override
public List<String> tabComplete(CommandSender sender, String subCommand, String[] args) {
if (!DivineConfig.enableParallelWorldTicking) {
if (!DivineConfig.AsyncCategory.enableParallelWorldTicking) {
return Collections.emptyList();
}
if (args.length == 1) {

View File

@@ -9,7 +9,7 @@ import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.permissions.PermissionDefault;
import org.bxteam.divinemc.command.DivineCommand;
import org.bxteam.divinemc.command.DivineSubCommandPermission;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.framework.qual.DefaultQualifier;

View File

@@ -0,0 +1,19 @@
package org.bxteam.divinemc.config;
public enum ConfigCategory {
ASYNC("async"),
PERFORMANCE("performance"),
FIXES("fixes"),
NETWORK("network"),
MISC("misc");
private final String name;
ConfigCategory(String name) {
this.name = name;
}
public String key(String sub) {
return name + "." + sub;
}
}

View File

@@ -0,0 +1,711 @@
package org.bxteam.divinemc.config;
import com.google.common.base.Throwables;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.MemoryConfiguration;
import org.bxteam.divinemc.entity.pathfinding.PathfindTaskRejectPolicy;
import org.bxteam.divinemc.region.LinearImplementation;
import org.bxteam.divinemc.server.chunk.ChunkSystemAlgorithms;
import org.bxteam.divinemc.server.chunk.ChunkTaskPriority;
import org.jetbrains.annotations.Nullable;
import org.simpleyaml.configuration.comments.CommentType;
import org.simpleyaml.configuration.file.YamlFile;
import org.simpleyaml.exceptions.InvalidConfigurationException;
import org.bxteam.divinemc.region.RegionFileFormat;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@SuppressWarnings({"unused", "SameParameterValue"})
public class DivineConfig {
private static final String HEADER = """
This is the main configuration file for DivineMC.
If you need help with the configuration or have any questions related to DivineMC,
join us in our Discord server.
Discord: https://discord.gg/qNyybSSPm5
Docs: https://bxteam.org/docs/divinemc
Downloads: https://github.com/BX-Team/DivineMC/releases""";
public static final Logger LOGGER = LogManager.getLogger(DivineConfig.class.getSimpleName());
public static final int CONFIG_VERSION = 6;
private static File configFile;
public static final YamlFile config = new YamlFile();
private static ConfigurationSection convertToBukkit(org.simpleyaml.configuration.ConfigurationSection section) {
ConfigurationSection newSection = new MemoryConfiguration();
for (String key : section.getKeys(false)) {
if (section.isConfigurationSection(key)) {
newSection.set(key, convertToBukkit(section.getConfigurationSection(key)));
} else {
newSection.set(key, section.get(key));
}
}
return newSection;
}
public static ConfigurationSection getConfigCopy() {
return convertToBukkit(config);
}
public static void init(File configFile) throws IOException {
DivineConfig.configFile = configFile;
if (configFile.exists()) {
try {
config.load(configFile);
} catch (InvalidConfigurationException e) {
throw new IOException(e);
}
}
getInt("version", CONFIG_VERSION);
config.options().header(HEADER);
readConfig(DivineConfig.class, null);
}
static void readConfig(Class<?> clazz, Object instance) throws IOException {
for (Method method : clazz.getDeclaredMethods()) {
if (Modifier.isPrivate(method.getModifiers()) &&
method.getParameterTypes().length == 0 &&
method.getReturnType() == Void.TYPE) {
try {
method.setAccessible(true);
method.invoke(instance);
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
LOGGER.error("Error invoking {}", method, ex);
}
}
}
for (Class<?> innerClass : clazz.getDeclaredClasses()) {
if (Modifier.isStatic(innerClass.getModifiers())) {
try {
Object innerInstance = null;
Method loadMethod = null;
try {
loadMethod = innerClass.getDeclaredMethod("load");
} catch (NoSuchMethodException ignored) {
readConfig(innerClass, null);
continue;
}
if (loadMethod != null) {
try {
innerInstance = innerClass.getDeclaredConstructor().newInstance();
} catch (NoSuchMethodException e) {
innerInstance = null;
}
loadMethod.setAccessible(true);
loadMethod.invoke(innerInstance);
}
} catch (Exception ex) {
LOGGER.error("Error processing inner class {}", innerClass.getName(), ex);
}
}
}
config.save(configFile);
}
private static void setComment(String key, String... comment) {
if (config.contains(key)) {
config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
}
}
private static void ensureDefault(String key, Object defaultValue, String... comment) {
if (!config.contains(key)) config.set(key, defaultValue);
if (comment.length > 0) config.setComment(key, String.join("\n", comment), CommentType.BLOCK);
}
private static boolean getBoolean(String key, boolean defaultValue, String... comment) {
return getBoolean(key, null, defaultValue, comment);
}
private static boolean getBoolean(String key, @Nullable String oldKey, boolean defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getBoolean(key, defaultValue);
}
private static int getInt(String key, int defaultValue, String... comment) {
return getInt(key, null, defaultValue, comment);
}
private static int getInt(String key, @Nullable String oldKey, int defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getInt(key, defaultValue);
}
private static double getDouble(String key, double defaultValue, String... comment) {
return getDouble(key, null, defaultValue, comment);
}
private static double getDouble(String key, @Nullable String oldKey, double defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getDouble(key, defaultValue);
}
private static long getLong(String key, long defaultValue, String... comment) {
return getLong(key, null, defaultValue, comment);
}
private static long getLong(String key, @Nullable String oldKey, long defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getLong(key, defaultValue);
}
private static String getString(String key, String defaultValue, String... comment) {
return getOldString(key, null, defaultValue, comment);
}
private static String getOldString(String key, @Nullable String oldKey, String defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getString(key, defaultValue);
}
private static List<String> getStringList(String key, List<String> defaultValue, String... comment) {
return getStringList(key, null, defaultValue, comment);
}
private static List<String> getStringList(String key, @Nullable String oldKey, List<String> defaultValue, String... comment) {
ensureDefault(key, defaultValue, comment);
return config.getStringList(key);
}
public static class AsyncCategory {
// Parallel world ticking settings
public static boolean enableParallelWorldTicking = false;
public static int parallelThreadCount = 4;
public static boolean logContainerCreationStacktraces = false;
public static boolean disableHardThrow = false;
public static boolean pwtCompatabilityMode = false;
// Async pathfinding settings
public static boolean asyncPathfinding = true;
public static int asyncPathfindingMaxThreads = 2;
public static int asyncPathfindingKeepalive = 60;
public static int asyncPathfindingQueueSize = 0;
public static PathfindTaskRejectPolicy asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.FLUSH_ALL;
// Multithreaded tracker settings
public static boolean multithreadedEnabled = true;
public static boolean multithreadedCompatModeEnabled = false;
public static int asyncEntityTrackerMaxThreads = 1;
public static int asyncEntityTrackerKeepalive = 60;
public static int asyncEntityTrackerQueueSize = 0;
// Async chunk sending settings
public static boolean asyncChunkSendingEnabled = true;
// Async mob spawning settings
public static boolean enableAsyncSpawning = true;
public static void load() {
parallelWorldTicking();
asyncPathfinding();
multithreadedTracker();
asyncChunkSending();
asyncMobSpawning();
}
private static void parallelWorldTicking() {
enableParallelWorldTicking = getBoolean(ConfigCategory.ASYNC.key("parallel-world-ticking.enable"), enableParallelWorldTicking,
"Enables Parallel World Ticking, which executes each world's tick in a separate thread while ensuring that all worlds complete their tick before the next cycle begins.",
"",
"Read more info about this feature at https://bxteam.org/docs/divinemc/features/parallel-world-ticking");
parallelThreadCount = getInt(ConfigCategory.ASYNC.key("parallel-world-ticking.thread-count"), parallelThreadCount);
logContainerCreationStacktraces = getBoolean(ConfigCategory.ASYNC.key("parallel-world-ticking.log-container-creation-stacktraces"), logContainerCreationStacktraces);
disableHardThrow = getBoolean(ConfigCategory.ASYNC.key("parallel-world-ticking.disable-hard-throw"), disableHardThrow,
"Disables annoying 'not on main thread' throws. But, THIS IS NOT RECOMMENDED because you SHOULD FIX THE ISSUES THEMSELVES instead of RISKING DATA CORRUPTION! If you lose something, take the blame on yourself.");
pwtCompatabilityMode = getBoolean(ConfigCategory.ASYNC.key("parallel-world-ticking.compatability-mode"), pwtCompatabilityMode,
"Enables compatibility mode for plugins that are not compatible with Parallel World Ticking. This makes all async tasks run synchronously.");
}
private static void asyncPathfinding() {
asyncPathfinding = getBoolean(ConfigCategory.ASYNC.key("pathfinding.enable"), asyncPathfinding);
asyncPathfindingMaxThreads = getInt(ConfigCategory.ASYNC.key("pathfinding.max-threads"), asyncPathfindingMaxThreads);
asyncPathfindingKeepalive = getInt(ConfigCategory.ASYNC.key("pathfinding.keepalive"), asyncPathfindingKeepalive);
asyncPathfindingQueueSize = getInt(ConfigCategory.ASYNC.key("pathfinding.queue-size"), asyncPathfindingQueueSize);
final int maxThreads = Runtime.getRuntime().availableProcessors();
if (asyncPathfindingMaxThreads < 0) {
asyncPathfindingMaxThreads = Math.max(maxThreads + asyncPathfindingMaxThreads, 1);
} else if (asyncPathfindingMaxThreads == 0) {
asyncPathfindingMaxThreads = Math.max(maxThreads / 4, 1);
}
if (!asyncPathfinding) {
asyncPathfindingMaxThreads = 0;
} else {
LOGGER.info("Using {} threads for Async Pathfinding", asyncPathfindingMaxThreads);
}
if (asyncPathfindingQueueSize <= 0) asyncPathfindingQueueSize = asyncPathfindingMaxThreads * 256;
asyncPathfindingRejectPolicy = PathfindTaskRejectPolicy.fromString(getString(ConfigCategory.ASYNC.key("pathfinding.reject-policy"), maxThreads >= 12 && asyncPathfindingQueueSize < 512 ? PathfindTaskRejectPolicy.FLUSH_ALL.toString() : PathfindTaskRejectPolicy.CALLER_RUNS.toString(),
"The policy to use when the queue is full and a new task is submitted.",
"FLUSH_ALL: All pending tasks will be run on server thread.",
"CALLER_RUNS: Newly submitted task will be run on server thread."));
}
private static void multithreadedTracker() {
multithreadedEnabled = getBoolean(ConfigCategory.ASYNC.key("multithreaded-tracker.enable"), multithreadedEnabled,
"Make entity tracking saving asynchronously, can improve performance significantly,",
"especially in some massive entities in small area situations.");
multithreadedCompatModeEnabled = getBoolean(ConfigCategory.ASYNC.key("multithreaded-tracker.compat-mode"), multithreadedCompatModeEnabled,
"Enable compat mode ONLY if Citizens or NPC plugins using real entity has installed.",
"Compat mode fixes visible issues with player type NPCs of Citizens.",
"But we recommend to use packet based / virtual entity NPC plugin, e.g. ZNPC Plus, Adyeshach, Fancy NPC and etc.");
asyncEntityTrackerMaxThreads = getInt(ConfigCategory.ASYNC.key("multithreaded-tracker.max-threads"), asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = getInt(ConfigCategory.ASYNC.key("multithreaded-tracker.keepalive"), asyncEntityTrackerKeepalive);
asyncEntityTrackerQueueSize = getInt(ConfigCategory.ASYNC.key("multithreaded-tracker.queue-size"), asyncEntityTrackerQueueSize);
if (asyncEntityTrackerMaxThreads < 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncEntityTrackerMaxThreads, 1);
} else if (asyncEntityTrackerMaxThreads == 0) {
asyncEntityTrackerMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
}
if (!multithreadedEnabled) {
asyncEntityTrackerMaxThreads = 0;
} else {
LOGGER.info("Using {} threads for Async Entity Tracker", asyncEntityTrackerMaxThreads);
}
if (asyncEntityTrackerQueueSize <= 0) asyncEntityTrackerQueueSize = asyncEntityTrackerMaxThreads * 384;
}
private static void asyncChunkSending() {
asyncChunkSendingEnabled = getBoolean(ConfigCategory.ASYNC.key("chunk-sending.enable"), asyncChunkSendingEnabled,
"Makes chunk sending asynchronous, which can significantly reduce main thread load when many players are loading chunks.");
}
private static void asyncMobSpawning() {
enableAsyncSpawning = getBoolean(ConfigCategory.ASYNC.key("mob-spawning.enable"), enableAsyncSpawning,
"Enables optimization that will offload much of the computational effort involved with spawning new mobs to a different thread.");
}
}
public static class PerformanceCategory {
// Chunk settings
public static long chunkDataCacheSoftLimit = 8192L;
public static long chunkDataCacheLimit = 32678L;
public static int maxViewDistance = 32;
public static int playerNearChunkDetectionRange = 128;
public static ChunkSystemAlgorithms chunkWorkerAlgorithm = ChunkSystemAlgorithms.C2ME;
public static ChunkTaskPriority chunkTaskPriority = ChunkTaskPriority.EUCLIDEAN_CIRCLE_PATTERN;
public static int threadPoolPriority = Thread.NORM_PRIORITY + 1;
public static boolean smoothBedrockLayer = false;
public static boolean enableDensityFunctionCompiler = false;
public static boolean enableStructureLayoutOptimizer = true;
public static boolean deduplicateShuffledTemplatePoolElementList = false;
// TNT optimization
public static boolean enableFasterTntOptimization = true;
public static boolean explosionNoBlockDamage = false;
public static double tntRandomRange = -1;
// General optimizations
public static boolean skipUselessSecondaryPoiSensor = true;
public static boolean clumpOrbs = true;
public static boolean enableSuffocationOptimization = true;
public static boolean useCompactBitStorage = false;
public static boolean commandBlockParseResultsCaching = true;
public static boolean sheepOptimization = true;
public static boolean hopperThrottleWhenFull = false;
public static int hopperThrottleSkipTicks = 0;
// DAB settings
public static boolean dabEnabled = true;
public static int dabStartDistance = 12;
public static int dabStartDistanceSquared;
public static int dabMaximumActivationFrequency = 20;
public static int dabActivationDistanceMod = 8;
public static boolean dabDontEnableIfInWater = false;
public static List<String> dabBlackedEntities = new ArrayList<>();
// Virtual threads
public static boolean virtualThreadsEnabled = false;
public static boolean virtualBukkitScheduler = false;
public static boolean virtualChatScheduler = false;
public static boolean virtualAuthenticatorScheduler = false;
public static boolean virtualTabCompleteScheduler = false;
public static boolean virtualAsyncExecutor = false;
public static boolean virtualCommandBuilderScheduler = false;
public static boolean virtualProfileLookupPool = false;
public static boolean virtualServerTextFilterPool = false;
public static void load() {
chunkSettings();
tntOptimization();
optimizationSettings();
dab();
virtualThreads();
}
private static void chunkSettings() {
chunkDataCacheSoftLimit = getLong(ConfigCategory.PERFORMANCE.key("chunks.chunk-data-cache-soft-limit"), chunkDataCacheSoftLimit);
chunkDataCacheLimit = getLong(ConfigCategory.PERFORMANCE.key("chunks.chunk-data-cache-limit"), chunkDataCacheLimit);
maxViewDistance = getInt(ConfigCategory.PERFORMANCE.key("chunks.max-view-distance"), maxViewDistance,
"Changes the maximum view distance for the server, allowing clients to have render distances higher than 32");
playerNearChunkDetectionRange = getInt(ConfigCategory.PERFORMANCE.key("chunks.player-near-chunk-detection-range"), playerNearChunkDetectionRange,
"In certain checks, like if a player is near a chunk(primarily used for spawning), it checks if the player is within a certain",
"circular range of the chunk. This configuration allows configurability of the distance(in blocks) the player must be to pass the check.",
"",
"This value is used in the calculation 'range/16' to get the distance in chunks any player must be to allow the check to pass.",
"By default, this range is computed to 8, meaning a player must be within an 8 chunk radius of a chunk position to pass.",
"Keep in mind the result is rounded to the nearest whole number.");
if (playerNearChunkDetectionRange < 0) {
LOGGER.warn("Invalid player near chunk detection range: {}, resetting to default (128)", playerNearChunkDetectionRange);
playerNearChunkDetectionRange = 128;
}
chunkWorkerAlgorithm = ChunkSystemAlgorithms.valueOf(getString(ConfigCategory.PERFORMANCE.key("chunks.chunk-worker-algorithm"), chunkWorkerAlgorithm.name(),
"Modifies what algorithm the chunk system will use to define thread counts.",
"Valid values:",
" - MOONRISE: Default algorithm, used by default in Paper",
" - C2ME: Algorithm used by C2ME (old)",
" - C2ME_NEW: Modern algorithm used by C2ME"));
chunkTaskPriority = ChunkTaskPriority.valueOf(getString(ConfigCategory.PERFORMANCE.key("chunks.chunk-task-priority"), chunkTaskPriority.name(),
"Sets the algorithm for determining chunk task priorities (generation, loading and etc.).",
"Valid values:",
" - EUCLIDEAN_CIRCLE_PATTERN: Euclidean distance squared algorithm, chunk priorities will be ordered in a circle pattern",
" - DEFAULT_DIAMOND_PATTERN: Default one, chunk priorities will be ordered in a diamond pattern"));
threadPoolPriority = getInt(ConfigCategory.PERFORMANCE.key("chunks.thread-pool-priority"), threadPoolPriority,
"Sets the priority of the thread pool used for chunk generation");
smoothBedrockLayer = getBoolean(ConfigCategory.PERFORMANCE.key("chunks.smooth-bedrock-layer"), smoothBedrockLayer,
"Smoothens the bedrock layer at the bottom of overworld, and on the top of nether during the world generation.");
enableDensityFunctionCompiler = getBoolean(ConfigCategory.PERFORMANCE.key("chunks.experimental.enable-density-function-compiler"), enableDensityFunctionCompiler,
"Whether to use density function compiler to accelerate world generation",
"",
"Density function: https://minecraft.wiki/w/Density_function",
"",
"This functionality compiles density functions from world generation",
"datapacks (including vanilla generation) to JVM bytecode to increase",
"performance by allowing JVM JIT to better optimize the code.",
"All functions provided by vanilla are implemented.");
enableStructureLayoutOptimizer = getBoolean(ConfigCategory.PERFORMANCE.key("chunks.experimental.enable-structure-layout-optimizer"), enableStructureLayoutOptimizer,
"Enables a port of the mod StructureLayoutOptimizer, which optimizes general Jigsaw structure generation");
deduplicateShuffledTemplatePoolElementList = getBoolean(ConfigCategory.PERFORMANCE.key("chunks.experimental.deduplicate-shuffled-template-pool-element-list"), deduplicateShuffledTemplatePoolElementList,
"Whether to use an alternative strategy to make structure layouts generate slightly even faster than",
"the default optimization this mod has for template pool weights. This alternative strategy works by",
"changing the list of pieces that structures collect from the template pool to not have duplicate entries.",
"",
"This will not break the structure generation, but it will make the structure layout different than",
"if this config was off (breaking vanilla seed parity). The cost of speed may be worth it in large",
"modpacks where many structure mods are using very high weight values in their template pools.");
}
private static void tntOptimization() {
enableFasterTntOptimization = getBoolean(ConfigCategory.PERFORMANCE.key("tnt-optimization.enable-faster-tnt-optimization"), enableFasterTntOptimization);
explosionNoBlockDamage = getBoolean(ConfigCategory.PERFORMANCE.key("tnt-optimization.explosion-no-block-damage"), explosionNoBlockDamage);
tntRandomRange = getDouble(ConfigCategory.PERFORMANCE.key("tnt-optimization.tnt-random-range"), tntRandomRange);
}
private static void optimizationSettings() {
skipUselessSecondaryPoiSensor = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.skip-useless-secondary-poi-sensor"), skipUselessSecondaryPoiSensor);
clumpOrbs = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.clump-orbs"), clumpOrbs,
"Clumps experience orbs together to reduce entity count");
enableSuffocationOptimization = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.enable-suffocation-optimization"), enableSuffocationOptimization,
"Optimizes the suffocation check by selectively skipping the check in a way that still appears vanilla.",
"This option should be left enabled on most servers, but is provided as a configuration option if the vanilla deviation is undesirable.");
useCompactBitStorage = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.use-compact-bit-storage"), useCompactBitStorage,
"Fixes memory waste caused by sending empty chunks as if they contain blocks. Can significantly reduce memory usage.");
commandBlockParseResultsCaching = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.command-block-parse-results-caching"), commandBlockParseResultsCaching,
"Caches the parse results of command blocks, can significantly reduce performance impact.");
sheepOptimization = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.sheep-optimization"), sheepOptimization,
"Enables optimization from Carpet Fixes mod, using a prebaked list of all the possible colors and combinations for sheep.");
hopperThrottleWhenFull = getBoolean(ConfigCategory.PERFORMANCE.key("optimizations.hopper-throttle-when-full.enabled"), hopperThrottleWhenFull,
"When enabled, hoppers will throttle if target container is full.");
hopperThrottleSkipTicks = getInt(ConfigCategory.PERFORMANCE.key("optimizations.hopper-throttle-when-full.skip-ticks"), hopperThrottleSkipTicks,
"The amount of ticks to skip when the hopper is throttled.");
}
private static void dab() {
dabEnabled = getBoolean(ConfigCategory.PERFORMANCE.key("dab.enabled"), dabEnabled,
"Enables DAB feature");
dabStartDistance = getInt(ConfigCategory.PERFORMANCE.key("dab.start-distance"), dabStartDistance,
"This value determines how far away an entity has to be");
dabStartDistanceSquared = dabStartDistance * dabStartDistance;
dabMaximumActivationFrequency = getInt(ConfigCategory.PERFORMANCE.key("dab.maximum-activation-frequency"), dabMaximumActivationFrequency,
"How often in ticks, the furthest entity will get their pathfinders and behaviors ticked.");
dabActivationDistanceMod = getInt(ConfigCategory.PERFORMANCE.key("dab.activation-distance-mod"), dabActivationDistanceMod,
"Modifies an entity's tick frequency.",
"The exact calculation to obtain the tick frequency for an entity is: freq = (distanceToPlayer^2) / (2^value), where value is this configuration setting.",
"Large servers may want to reduce the value to 7, but this value should never be reduced below 6. If you want further away entities to tick more often, set the value to 9");
dabDontEnableIfInWater = getBoolean(ConfigCategory.PERFORMANCE.key("dab.dont-enable-if-in-water"), dabDontEnableIfInWater,
"When this is enabled, non-aquatic entities in the water will not be affected by DAB.");
dabBlackedEntities = getStringList(ConfigCategory.PERFORMANCE.key("dab.blacked-entities"), dabBlackedEntities,
"Use this configuration option to specify that certain entities should not be impacted by DAB.");
setComment(ConfigCategory.PERFORMANCE.key("dab"),
"DAB is an optimization that reduces the frequency of brain ticks. Brain ticks are very intensive, which is why they",
"are limited. DAB can be tuned to meet your preferred performance-experience tradeoff. The farther away entities",
"are from players, the less frequently their brains will be ticked. While DAB does impact the AI goal selector",
"behavior of all entities, the only entities who's brain ticks are limited are: Villager, Axolotl, Hoglin, Zombified Piglin and Goat");
for (EntityType<?> entityType : BuiltInRegistries.ENTITY_TYPE) {
entityType.dabEnabled = true;
}
final String DEFAULT_PREFIX = ResourceLocation.DEFAULT_NAMESPACE + ResourceLocation.NAMESPACE_SEPARATOR;
for (String name : dabBlackedEntities) {
String lowerName = name.toLowerCase(Locale.ROOT);
String typeId = lowerName.startsWith(DEFAULT_PREFIX) ? lowerName : DEFAULT_PREFIX + lowerName;
EntityType.byString(typeId).ifPresentOrElse(entityType -> entityType.dabEnabled = false, () -> LOGGER.warn("Unknown entity {}, in {}", name, ConfigCategory.PERFORMANCE.key("dab.blacked-entities")));
}
}
private static void virtualThreads() {
virtualThreadsEnabled = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.enabled"), virtualThreadsEnabled,
"Enables use of virtual threads that was added in Java 21");
virtualBukkitScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.bukkit-scheduler"), virtualBukkitScheduler,
"Uses virtual threads for the Bukkit scheduler.");
virtualChatScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.chat-scheduler"), virtualChatScheduler,
"Uses virtual threads for the Chat scheduler.");
virtualAuthenticatorScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.authenticator-scheduler"), virtualAuthenticatorScheduler,
"Uses virtual threads for the Authenticator scheduler.");
virtualTabCompleteScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.tab-complete-scheduler"), virtualTabCompleteScheduler,
"Uses virtual threads for the Tab Complete scheduler.");
virtualAsyncExecutor = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.async-executor"), virtualAsyncExecutor,
"Uses virtual threads for the MCUtil async executor.");
virtualCommandBuilderScheduler = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.command-builder-scheduler"), virtualCommandBuilderScheduler,
"Uses virtual threads for the Async Command Builder Thread Pool.");
virtualProfileLookupPool = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.profile-lookup-pool"), virtualProfileLookupPool,
"Uses virtual threads for the Profile Lookup Pool, that is used for fetching player profiles.");
virtualServerTextFilterPool = getBoolean(ConfigCategory.PERFORMANCE.key("virtual-threads.server-text-filter-pool"), virtualServerTextFilterPool,
"Uses virtual threads for the server text filter pool.");
}
}
public static class FixesCategory {
// Gameplay fixes
public static boolean fixIncorrectBounceLogic = false;
public static boolean updateSuppressionCrashFix = true;
public static boolean ignoreMovedTooQuicklyWhenLagging = true;
public static boolean alwaysAllowWeirdMovement = true;
// Miscellaneous fixes
public static boolean forceMinecraftCommand = false;
public static boolean disableLeafDecay = false;
// Bug fixes (MC-*)
public static boolean slopesVisualFix = false;
public static void load() {
gameplayFixes();
miscFixes();
bugFixes();
}
private static void gameplayFixes() {
fixIncorrectBounceLogic = getBoolean(ConfigCategory.FIXES.key("gameplay.fix-incorrect-bounce-logic"), fixIncorrectBounceLogic,
"Fixes incorrect bounce logic in SlimeBlock.");
updateSuppressionCrashFix = getBoolean(ConfigCategory.FIXES.key("gameplay.update-suppression-crash-fix"), updateSuppressionCrashFix);
ignoreMovedTooQuicklyWhenLagging = getBoolean(ConfigCategory.FIXES.key("gameplay.ignore-moved-too-quickly-when-lagging"), ignoreMovedTooQuicklyWhenLagging,
"Improves general gameplay experience of the player when the server is lagging, as they won't get lagged back (message 'moved too quickly')");
alwaysAllowWeirdMovement = getBoolean(ConfigCategory.FIXES.key("gameplay.always-allow-weird-movement"), alwaysAllowWeirdMovement,
"Means ignoring messages like 'moved too quickly' and 'moved wrongly'");
}
private static void miscFixes() {
forceMinecraftCommand = getBoolean(ConfigCategory.FIXES.key("misc.force-minecraft-command"), forceMinecraftCommand,
"Whether to force the use of vanilla commands over plugin commands.");
disableLeafDecay = getBoolean(ConfigCategory.FIXES.key("misc.disable-leaf-decay"), disableLeafDecay,
"Disables leaf block decay.");
}
private static void bugFixes() {
slopesVisualFix = getBoolean(ConfigCategory.FIXES.key("bug.fix-mc-258859"), slopesVisualFix,
"Fixes MC-258859, fixing slopes visual bug in biomes like Snowy Slopes, Frozen Peaks, Jagged Peaks, and including Terralith.");
}
}
public static class MiscCategory {
// Secure seed
public static boolean enableSecureSeed = false;
// Lag compensation
public static boolean lagCompensationEnabled = true;
public static boolean blockEntityAcceleration = false;
public static boolean blockBreakingAcceleration = true;
public static boolean eatingAcceleration = true;
public static boolean potionEffectAcceleration = true;
public static boolean fluidAcceleration = true;
public static boolean pickupAcceleration = true;
public static boolean portalAcceleration = true;
public static boolean timeAcceleration = true;
public static boolean randomTickSpeedAcceleration = true;
// Region format
public static RegionFileFormat regionFormatTypeName = RegionFileFormat.ANVIL;
public static LinearImplementation linearImplementation = LinearImplementation.V2;
public static int linearFlushMaxThreads = 4;
public static int linearFlushDelay = 100;
public static boolean linearUseVirtualThread = false;
public static int linearCompressionLevel = 1;
// Sentry
public static String sentryDsn = "";
public static String logLevel = "WARN";
public static boolean onlyLogThrown = true;
public static void load() {
secureSeed();
lagCompensation();
linearRegionFormat();
sentrySettings();
}
private static void secureSeed() {
enableSecureSeed = getBoolean(ConfigCategory.MISC.key("secure-seed.enable"), enableSecureSeed,
"This feature is based on Secure Seed mod by Earthcomputer.",
"",
"Terrain and biome generation remains the same, but all the ores and structures are generated with 1024-bit seed, instead of the usual 64-bit seed.",
"This seed is almost impossible to crack, and there are no weird links between structures.");
}
private static void lagCompensation() {
lagCompensationEnabled = getBoolean(ConfigCategory.MISC.key("lag-compensation.enabled"), lagCompensationEnabled,
"Improves the player experience when TPS is low");
blockEntityAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.block-entity-acceleration"), blockEntityAcceleration);
blockBreakingAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.block-breaking-acceleration"), blockBreakingAcceleration);
eatingAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.eating-acceleration"), eatingAcceleration);
potionEffectAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.potion-effect-acceleration"), potionEffectAcceleration);
fluidAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.fluid-acceleration"), fluidAcceleration);
pickupAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.pickup-acceleration"), pickupAcceleration);
portalAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.portal-acceleration"), portalAcceleration);
timeAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.time-acceleration"), timeAcceleration);
randomTickSpeedAcceleration = getBoolean(ConfigCategory.MISC.key("lag-compensation.random-tick-speed-acceleration"), randomTickSpeedAcceleration);
}
private static void linearRegionFormat() {
regionFormatTypeName = RegionFileFormat.fromName(getString(ConfigCategory.MISC.key("region-format.type"), regionFormatTypeName.name(),
"The type of region file format to use for storing chunk data.",
"Valid values:",
" - LINEAR: Linear region file format",
" - ANVIL: Anvil region file format (default)"));
linearImplementation = LinearImplementation.valueOf(getString(ConfigCategory.MISC.key("region-format.implementation"), linearImplementation.name(),
"The implementation of the linear region file format to use.",
"Valid values:",
" - V1: Basic and default linear implementation",
" - V2: Introduces a grid-based compression scheme for better data management and flexibility (default)"));
linearFlushMaxThreads = getInt(ConfigCategory.MISC.key("region-format.flush-max-threads"), linearFlushMaxThreads,
"The maximum number of threads to use for flushing linear region files.",
"If this value is less than or equal to 0, it will be set to the number of available processors + this value.");
linearFlushDelay = getInt(ConfigCategory.MISC.key("region-format.flush-delay"), linearFlushDelay,
"The delay in milliseconds to wait before flushing next files.");
linearUseVirtualThread = getBoolean(ConfigCategory.MISC.key("region-format.use-virtual-thread"), linearUseVirtualThread,
"Whether to use virtual threads for flushing.");
linearCompressionLevel = getInt(ConfigCategory.MISC.key("region-format.compression-level"), linearCompressionLevel,
"The compression level to use for the linear region file format.");
setComment(ConfigCategory.MISC.key("region-format"),
"The linear region file format is a custom region file format that is designed to be more efficient than the ANVIL format.",
"It uses uses ZSTD compression instead of ZLIB. This format saves about 50% of disk space.",
"Read more information about linear region format at https://github.com/xymb-endcrystalme/LinearRegionFileFormatTools",
"WARNING: If you are want to use this format, make sure to create backup of your world before switching to it, there is potential risk to lose chunk data.");
if (regionFormatTypeName == RegionFileFormat.UNKNOWN) {
LOGGER.error("Unknown region file type: {}, falling back to ANVIL format.", regionFormatTypeName);
regionFormatTypeName = RegionFileFormat.ANVIL;
}
if (linearFlushMaxThreads <= 0) {
linearFlushMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + linearFlushMaxThreads, 1);
}
if (linearCompressionLevel > 22 || linearCompressionLevel < 1) {
LOGGER.warn("Invalid linear compression level: {}, resetting to default (1)", linearCompressionLevel);
linearCompressionLevel = 1;
}
}
private static void sentrySettings() {
sentryDsn = getString(ConfigCategory.MISC.key("sentry.dsn"), sentryDsn,
"The DSN for Sentry, a service that provides real-time crash reporting that helps you monitor and fix crashes in real time. Leave blank to disable. Obtain link at https://sentry.io");
logLevel = getString(ConfigCategory.MISC.key("sentry.log-level"), logLevel,
"Logs with a level higher than or equal to this level will be recorded.");
onlyLogThrown = getBoolean(ConfigCategory.MISC.key("sentry.only-log-thrown"), onlyLogThrown,
"Only log Throwable exceptions to Sentry.");
if (sentryDsn != null && !sentryDsn.isBlank()) gg.pufferfish.pufferfish.sentry.SentryManager.init(Level.getLevel(logLevel));
}
}
public static class NetworkCategory {
// General network settings
public static boolean disableDisconnectSpam = false;
public static boolean gracefulTeleportHandling = false;
public static boolean dontRespondPingBeforeStart = true;
public static boolean playerProfileResultCachingEnabled = true;
public static int playerProfileResultCachingTimeout = 1440;
// No chat reports
public static boolean noChatReportsEnabled = false;
public static boolean noChatReportsAddQueryData = true;
public static boolean noChatReportsConvertToGameMessage = true;
public static boolean noChatReportsDebugLog = false;
public static boolean noChatReportsDemandOnClient = false;
public static String noChatReportsDisconnectDemandOnClientMessage = "You do not have No Chat Reports, and this server is configured to require it on client!";
public static void load() {
networkSettings();
noChatReports();
}
private static void networkSettings() {
disableDisconnectSpam = getBoolean(ConfigCategory.NETWORK.key("general.disable-disconnect-spam"), disableDisconnectSpam,
"Prevents players being disconnected by 'disconnect.spam' when sending too many chat packets");
gracefulTeleportHandling = getBoolean(ConfigCategory.NETWORK.key("general.graceful-teleport-handling"), gracefulTeleportHandling,
"Disables being disconnected from 'multiplayer.disconnect.invalid_player_movement' (also declines the packet handling).");
dontRespondPingBeforeStart = getBoolean(ConfigCategory.NETWORK.key("general.dont-respond-ping-before-start"), dontRespondPingBeforeStart,
"Prevents the server from responding to pings before the server is fully booted.");
playerProfileResultCachingEnabled = getBoolean(ConfigCategory.NETWORK.key("player-profile-result-caching.enabled"), playerProfileResultCachingEnabled,
"Enables caching of player profile results on first join.");
playerProfileResultCachingTimeout = getInt(ConfigCategory.NETWORK.key("player-profile-result-caching.timeout"), playerProfileResultCachingTimeout,
"The amount of time in minutes to cache player profile results.");
}
private static void noChatReports() {
noChatReportsEnabled = getBoolean(ConfigCategory.NETWORK.key("no-chat-reports.enabled"), noChatReportsEnabled,
"Enables or disables the No Chat Reports feature");
noChatReportsAddQueryData = getBoolean(ConfigCategory.NETWORK.key("no-chat-reports.add-query-data"), noChatReportsAddQueryData,
"Should server include extra query data to help clients know that your server is secure");
noChatReportsConvertToGameMessage = getBoolean(ConfigCategory.NETWORK.key("no-chat-reports.convert-to-game-message"), noChatReportsConvertToGameMessage,
"Should the server convert all player messages to system messages");
noChatReportsDebugLog = getBoolean(ConfigCategory.NETWORK.key("no-chat-reports.debug-log"), noChatReportsDebugLog);
noChatReportsDemandOnClient = getBoolean(ConfigCategory.NETWORK.key("no-chat-reports.demand-on-client"), noChatReportsDemandOnClient,
"Should the server require No Chat Reports on the client side");
noChatReportsDisconnectDemandOnClientMessage = getString(ConfigCategory.NETWORK.key("no-chat-reports.disconnect-demand-on-client-message"), noChatReportsDisconnectDemandOnClientMessage,
"Message to send to the client when they are disconnected for not having No Chat Reports");
}
}
}

View File

@@ -1,4 +1,4 @@
package org.bxteam.divinemc;
package org.bxteam.divinemc.config;
import org.bukkit.World;
import org.jetbrains.annotations.Nullable;

View File

@@ -5,7 +5,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.pathfinder.Path;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.bxteam.divinemc.util.NamedAgnosticThreadFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,8 +26,8 @@ public class AsyncPathProcessor {
private static long lastWarnMillis = System.currentTimeMillis();
private static final ThreadPoolExecutor pathProcessingExecutor = new ThreadPoolExecutor(
1,
DivineConfig.asyncPathfindingMaxThreads,
DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS,
DivineConfig.AsyncCategory.asyncPathfindingMaxThreads,
DivineConfig.AsyncCategory.asyncPathfindingKeepalive, TimeUnit.SECONDS,
getQueueImpl(),
new NamedAgnosticThreadFactory<>(THREAD_PREFIX, TickThread::new, Thread.NORM_PRIORITY - 2),
new RejectedTaskHandler()
@@ -38,7 +38,7 @@ public class AsyncPathProcessor {
public void rejectedExecution(Runnable rejectedTask, ThreadPoolExecutor executor) {
BlockingQueue<Runnable> workQueue = executor.getQueue();
if (!executor.isShutdown()) {
switch (DivineConfig.asyncPathfindingRejectPolicy) {
switch (DivineConfig.AsyncCategory.asyncPathfindingRejectPolicy) {
case FLUSH_ALL -> {
if (!workQueue.isEmpty()) {
List<Runnable> pendingTasks = new ArrayList<>(workQueue.size());
@@ -92,7 +92,7 @@ public class AsyncPathProcessor {
}
private static BlockingQueue<Runnable> getQueueImpl() {
final int queueCapacity = DivineConfig.asyncPathfindingQueueSize;
final int queueCapacity = DivineConfig.AsyncCategory.asyncPathfindingQueueSize;
return new LinkedBlockingQueue<>(queueCapacity);
}

View File

@@ -1,6 +1,6 @@
package org.bxteam.divinemc.entity.pathfinding;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import java.util.Locale;
public enum PathfindTaskRejectPolicy {

View File

@@ -12,7 +12,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.bxteam.divinemc.util.NamedAgnosticThreadFactory;
import org.jetbrains.annotations.NotNull;
@@ -46,7 +46,7 @@ public class MultithreadedTracker {
public static void tick(ChunkSystemServerLevel level) {
try {
if (!DivineConfig.multithreadedCompatModeEnabled) {
if (!DivineConfig.AsyncCategory.multithreadedCompatModeEnabled) {
tickAsync(level);
} else {
tickAsyncWithCompatMode(level);
@@ -135,15 +135,15 @@ public class MultithreadedTracker {
}
private static int getMaxPoolSize() {
return DivineConfig.asyncEntityTrackerMaxThreads;
return DivineConfig.AsyncCategory.asyncEntityTrackerMaxThreads;
}
private static long getKeepAliveTime() {
return DivineConfig.asyncEntityTrackerKeepalive;
return DivineConfig.AsyncCategory.asyncEntityTrackerKeepalive;
}
private static BlockingQueue<Runnable> getQueueImpl() {
final int queueCapacity = DivineConfig.asyncEntityTrackerQueueSize;
final int queueCapacity = DivineConfig.AsyncCategory.asyncEntityTrackerQueueSize;
return new LinkedBlockingQueue<>(queueCapacity);
}

View File

@@ -10,7 +10,7 @@ import net.jpountz.lz4.LZ4FastDecompressor;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.ChunkPos;
import net.openhft.hashing.LongHashFunction;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.bxteam.divinemc.spark.ThreadDumperRegistry;
import org.jspecify.annotations.Nullable;
import org.slf4j.Logger;
@@ -75,7 +75,7 @@ public class LinearRegionFile implements IRegionFile {
Runnable flushCheck = () -> {
while (!close) {
synchronized (SAVE_LOCK) {
if (markedToSave && activeSaveThreads < DivineConfig.linearFlushMaxThreads) {
if (markedToSave && activeSaveThreads < DivineConfig.MiscCategory.linearFlushMaxThreads) {
activeSaveThreads++;
Runnable flushOperation = () -> {
try {
@@ -88,7 +88,7 @@ public class LinearRegionFile implements IRegionFile {
}
}
};
Thread saveThread = DivineConfig.linearUseVirtualThread
Thread saveThread = DivineConfig.MiscCategory.linearUseVirtualThread
? Thread.ofVirtual().name("Linear IO - " + this.hashCode()).unstarted(flushOperation)
: Thread.ofPlatform().name("Linear IO - " + this.hashCode()).unstarted(flushOperation);
saveThread.setPriority(Thread.NORM_PRIORITY - 3);
@@ -96,10 +96,10 @@ public class LinearRegionFile implements IRegionFile {
ThreadDumperRegistry.REGISTRY.add(saveThread.getName());
}
}
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(DivineConfig.linearFlushDelay));
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(DivineConfig.MiscCategory.linearFlushDelay));
}
};
this.schedulingThread = DivineConfig.linearUseVirtualThread
this.schedulingThread = DivineConfig.MiscCategory.linearUseVirtualThread
? Thread.ofVirtual().unstarted(flushCheck)
: Thread.ofPlatform().unstarted(flushCheck);
this.schedulingThread.setName("Linear IO Schedule - " + this.hashCode());

View File

@@ -3,7 +3,7 @@ package org.bxteam.divinemc.region;
import net.minecraft.world.level.chunk.storage.RegionFile;
import net.minecraft.world.level.chunk.storage.RegionFileVersion;
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -23,7 +23,7 @@ public class RegionFileFactory {
final String extensionName = fullNameSplit[fullNameSplit.length - 1];
switch (RegionFileFormat.fromExtension(extensionName)) {
case LINEAR -> {
return new LinearRegionFile(path, DivineConfig.linearImplementation, DivineConfig.linearCompressionLevel);
return new LinearRegionFile(path, DivineConfig.MiscCategory.linearImplementation, DivineConfig.MiscCategory.linearCompressionLevel);
}
default -> {

View File

@@ -2,7 +2,7 @@ package su.plo.matter;
import com.google.common.collect.Iterables;
import net.minecraft.server.level.ServerLevel;
import org.bxteam.divinemc.DivineConfig;
import org.bxteam.divinemc.config.DivineConfig;
import java.math.BigInteger;
import java.security.SecureRandom;
@@ -37,7 +37,7 @@ public class Globals {
}
public static void setupGlobals(ServerLevel world) {
if (!DivineConfig.enableSecureSeed) return;
if (!DivineConfig.MiscCategory.enableSecureSeed) return;
long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed();
System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS);