9
0
mirror of https://github.com/BX-Team/DivineMC.git synced 2025-12-22 08:19:19 +00:00

rollback to previous config system; fix patches

This commit is contained in:
NONPLAYT
2025-01-16 02:42:17 +03:00
parent 568f501624
commit dbb184d8af
30 changed files with 827 additions and 820 deletions

View File

@@ -1,12 +1,16 @@
package space.bxteam.divinemc.command.subcommands;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.permissions.PermissionDefault;
import space.bxteam.divinemc.command.DivineCommand;
import space.bxteam.divinemc.command.DivineSubCommandPermission;
import space.bxteam.divinemc.configuration.DivineConfig;
import java.io.File;
import static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
@@ -31,8 +35,12 @@ public final class ReloadCommand extends DivineSubCommandPermission {
Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED));
MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
server.divineConfigurations.reloadConfigs(server);
server.server.reloadCount++;
DivineConfig.init((File) server.options.valueOf("divinemc-settings"));
for (ServerLevel level : server.getAllLevels()) {
level.divinemcConfig.init();
level.resetBreedingCooldowns();
}
server.server.reloadCount++;
Command.broadcastCommandMessage(sender, text("DivineMC config reload complete.", GREEN));
}

View File

@@ -0,0 +1,203 @@
package space.bxteam.divinemc.configuration;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
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.List;
import java.util.Map;
import java.util.logging.Level;
@SuppressWarnings("unused")
public class DivineConfig {
private static final String HEADER = "This is the main configuration file for DivineMC.\n"
+ "If you need help with the configuration or have any questions related to DivineMC,\n"
+ "join us in our Discord server.\n"
+ "\n"
+ "Discord: https://discord.gg/p7cxhw7E2M \n"
+ "Docs: https://docs.bx-team.space/documentation/divinemc/about \n"
+ "New builds: https://github.com/DivineMC/DivineMC/releases/latest";
private static File CONFIG_FILE;
public static YamlConfiguration config;
private static Map<String, Command> commands;
public static int version;
static boolean verbose;
public static void init(File configFile) {
CONFIG_FILE = configFile;
config = new YamlConfiguration();
try {
config.load(CONFIG_FILE);
} catch (IOException ignore) {
} catch (InvalidConfigurationException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not load divinemc.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
config.options().copyDefaults(true);
verbose = getBoolean("verbose", false);
version = getInt("config-version", 4);
set("config-version", 4);
readConfig(DivineConfig.class, null);
Block.BLOCK_STATE_REGISTRY.forEach(BlockBehaviour.BlockStateBase::initCache);
}
protected static void log(String s) {
if (verbose) {
log(Level.INFO, s);
}
}
protected static void log(Level level, String s) {
Bukkit.getLogger().log(level, s);
}
static void readConfig(Class<?> clazz, Object instance) {
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) {
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
}
}
}
}
try {
config.save(CONFIG_FILE);
} catch (IOException ex) {
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex);
}
}
private static void set(String path, Object val) {
config.addDefault(path, val);
config.set(path, val);
}
private static String getString(String path, String def) {
config.addDefault(path, def);
return config.getString(path, config.getString(path));
}
private static boolean getBoolean(String path, boolean def) {
config.addDefault(path, def);
return config.getBoolean(path, config.getBoolean(path));
}
private static double getDouble(String path, double def) {
config.addDefault(path, def);
return config.getDouble(path, config.getDouble(path));
}
private static int getInt(String path, int def) {
config.addDefault(path, def);
return config.getInt(path, config.getInt(path));
}
private static <T> List getList(String path, T def) {
config.addDefault(path, def);
return config.getList(path, config.getList(path));
}
static Map<String, Object> getMap(String path, Map<String, Object> def) {
if (def != null && config.getConfigurationSection(path) == null) {
config.addDefault(path, def);
return def;
}
return toMap(config.getConfigurationSection(path));
}
private static Map<String, Object> toMap(ConfigurationSection section) {
ImmutableMap.Builder<String, Object> builder = ImmutableMap.builder();
if (section != null) {
for (String key : section.getKeys(false)) {
Object obj = section.get(key);
if (obj != null) {
builder.put(key, obj instanceof ConfigurationSection val ? toMap(val) : obj);
}
}
}
return builder.build();
}
public static boolean noChatSign = true;
private static void chatMessageSignatures() {
noChatSign = getBoolean("settings.no-chat-sign", noChatSign);
}
public static boolean optimizedDragonRespawn = true;
private static void optimizations() {
optimizedDragonRespawn = getBoolean("settings.optimizations.optimized-dragon-respawn", optimizedDragonRespawn);
}
public static boolean disableNonEditableSignWarning = true;
public static boolean removeVanillaUsernameCheck = false;
public static boolean disableMovedWronglyThreshold = false;
public static boolean enableSecureSeed = false;
private static void miscSettings() {
disableNonEditableSignWarning = getBoolean("settings.misc.disable-non-editable-sign-warning", disableNonEditableSignWarning);
removeVanillaUsernameCheck = getBoolean("settings.misc.remove-vanilla-username-check", removeVanillaUsernameCheck);
disableMovedWronglyThreshold = getBoolean("settings.misc.disable-moved-wrongly-threshold", disableMovedWronglyThreshold);
enableSecureSeed = getBoolean("settings.misc.enable-secure-seed", enableSecureSeed);
}
public static boolean asyncPathfinding = true;
public static int asyncPathfindingMaxThreads = 0;
public static int asyncPathfindingKeepalive = 60;
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);
if (asyncPathfindingMaxThreads < 0)
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
else if (asyncPathfindingMaxThreads == 0)
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
if (!asyncPathfinding)
asyncPathfindingMaxThreads = 0;
else
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
}
public static boolean multithreadedEnabled = false;
public static boolean multithreadedCompatModeEnabled = false;
public static int asyncEntityTrackerMaxThreads = 0;
public static int asyncEntityTrackerKeepalive = 60;
private static void multithreadedTracker() {
multithreadedEnabled = getBoolean("settings.multithreaded-tracker.enable", multithreadedEnabled);
multithreadedCompatModeEnabled = getBoolean("settings.multithreaded-tracker.compat-mode", multithreadedCompatModeEnabled);
asyncEntityTrackerMaxThreads = getInt("settings.multithreaded-tracker.max-threads", asyncEntityTrackerMaxThreads);
asyncEntityTrackerKeepalive = getInt("settings.multithreaded-tracker.keepalive", asyncEntityTrackerKeepalive);
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
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
}
}

View File

@@ -1,289 +0,0 @@
package space.bxteam.divinemc.configuration;
import com.google.common.collect.Table;
import com.mojang.logging.LogUtils;
import io.leangen.geantyref.TypeToken;
import io.papermc.paper.configuration.Configuration;
import io.papermc.paper.configuration.ConfigurationPart;
import io.papermc.paper.configuration.Configurations;
import io.papermc.paper.configuration.NestedSetting;
import io.papermc.paper.configuration.PaperConfigurations;
import io.papermc.paper.configuration.legacy.RequiresSpigotInitialization;
import io.papermc.paper.configuration.mapping.InnerClassFieldDiscoverer;
import io.papermc.paper.configuration.serializer.ComponentSerializer;
import io.papermc.paper.configuration.serializer.EnumValueSerializer;
import io.papermc.paper.configuration.serializer.PacketClassSerializer;
import io.papermc.paper.configuration.serializer.StringRepresentableSerializer;
import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer;
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
import io.papermc.paper.configuration.serializer.collections.TableSerializer;
import io.papermc.paper.configuration.serializer.registry.RegistryHolderSerializer;
import io.papermc.paper.configuration.serializer.registry.RegistryValueSerializer;
import io.papermc.paper.configuration.transformation.Transformations;
import io.papermc.paper.configuration.type.BooleanOrDefault;
import io.papermc.paper.configuration.type.Duration;
import io.papermc.paper.configuration.type.EngineMode;
import io.papermc.paper.configuration.type.fallback.FallbackValueSerializer;
import io.papermc.paper.configuration.type.number.DoubleOr;
import io.papermc.paper.configuration.type.number.IntOr;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Reference2LongMap;
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.spongepowered.configurate.ConfigurateException;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.ConfigurationOptions;
import org.spongepowered.configurate.NodePath;
import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
import org.spongepowered.configurate.transformation.TransformAction;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import static io.leangen.geantyref.GenericTypeReflector.erase;
@SuppressWarnings("Convert2Diamond")
public class DivineConfigurations extends Configurations<DivineGlobalConfiguration, DivineWorldConfiguration> {
private static final Logger LOGGER = LogUtils.getLogger();
static final String GLOBAL_CONFIG_FILE_NAME = "divinemc-global.yml";
static final String WORLD_DEFAULTS_CONFIG_FILE_NAME = "divinemc-world-defaults.yml";
static final String WORLD_CONFIG_FILE_NAME = "divinemc-world.yml";
public static final String CONFIG_DIR = "config";
private static final String GLOBAL_HEADER = String.format("""
This is the global configuration file for DivineMC.
If you need help with the configuration or have any questions related to DivineMC,
join us in our Discord, or check our Documentation website.
The world configuration options are inside
their respective world folder. The files are named %s
Documentation: https://docs.bx-team.space/documentation/divinemc/about
Discord: https://discord.gg/p7cxhw7E2M""", WORLD_CONFIG_FILE_NAME);
private static final String WORLD_DEFAULTS_HEADER = """
This is the world defaults configuration file for DivineMC.
If you need help with the configuration or have any questions related to DivineMC,
join us in our Discord, or check our Documentation website.
Configuration options here apply to all worlds, unless you specify overrides inside
the world-specific config file inside each world folder.
Documentation: https://docs.bx-team.space/documentation/divinemc/about
Discord: https://discord.gg/p7cxhw7E2M""";
private static final Function<ContextMap, String> WORLD_HEADER = map -> String.format("""
This is a world configuration file for DivineMC.
This file may start empty but can be filled with settings to override ones in the %s/%s
World: %s (%s)""",
CONFIG_DIR,
WORLD_DEFAULTS_CONFIG_FILE_NAME,
map.require(WORLD_NAME),
map.require(WORLD_KEY)
);
public DivineConfigurations(final Path globalFolder) {
super(globalFolder, DivineGlobalConfiguration.class, DivineWorldConfiguration.class, GLOBAL_CONFIG_FILE_NAME, WORLD_DEFAULTS_CONFIG_FILE_NAME, WORLD_CONFIG_FILE_NAME);
}
@Override
protected YamlConfigurationLoader.Builder createLoaderBuilder() {
return super.createLoaderBuilder()
.defaultOptions(DivineConfigurations::defaultOptions);
}
private static ConfigurationOptions defaultOptions(ConfigurationOptions options) {
return options.serializers(builder -> builder
.register(MapSerializer.TYPE, new MapSerializer(false))
.register(new EnumValueSerializer())
.register(new ComponentSerializer())
);
}
@Override
protected ObjectMapper.Factory.Builder createGlobalObjectMapperFactoryBuilder() {
return defaultGlobalFactoryBuilder(super.createGlobalObjectMapperFactoryBuilder());
}
private static ObjectMapper.Factory.Builder defaultGlobalFactoryBuilder(ObjectMapper.Factory.Builder builder) {
return builder.addDiscoverer(InnerClassFieldDiscoverer.globalConfig());
}
@Override
protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder(RegistryAccess registryAccess) {
return super.createGlobalLoaderBuilder(registryAccess)
.defaultOptions((options) -> defaultGlobalOptions(registryAccess, options));
}
private static ConfigurationOptions defaultGlobalOptions(RegistryAccess registryAccess, ConfigurationOptions options) {
return options
.header(GLOBAL_HEADER)
.serializers(builder -> builder.register(new PacketClassSerializer())
.register(new RegistryValueSerializer<>(new TypeToken<DataComponentType<?>>() {}, registryAccess, Registries.DATA_COMPONENT_TYPE, false))
);
}
@Override
public DivineGlobalConfiguration initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException {
DivineGlobalConfiguration configuration = super.initializeGlobalConfiguration(registryAccess);
DivineGlobalConfiguration.set(configuration);
return configuration;
}
@Override
protected ContextMap.Builder createDefaultContextMap(final RegistryAccess registryAccess) {
return super.createDefaultContextMap(registryAccess)
.put(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY, PaperConfigurations.SPIGOT_WORLD_DEFAULTS);
}
@Override
protected ObjectMapper.Factory.Builder createWorldObjectMapperFactoryBuilder(final ContextMap contextMap) {
return super.createWorldObjectMapperFactoryBuilder(contextMap)
.addNodeResolver(new RequiresSpigotInitialization.Factory(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get()))
.addNodeResolver(new NestedSetting.Factory())
.addDiscoverer(InnerClassFieldDiscoverer.divineWorldConfig(contextMap));
}
@Override
protected YamlConfigurationLoader.Builder createWorldConfigLoaderBuilder(final ContextMap contextMap) {
final RegistryAccess access = contextMap.require(REGISTRY_ACCESS);
return super.createWorldConfigLoaderBuilder(contextMap)
.defaultOptions(options -> options
.header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? WORLD_DEFAULTS_HEADER : WORLD_HEADER.apply(contextMap))
.serializers(serializers -> serializers
.register(new TypeToken<Reference2IntMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2IntMap<?>>(Reference2IntOpenHashMap::new, Integer.TYPE))
.register(new TypeToken<Reference2LongMap<?>>() {}, new FastutilMapSerializer.SomethingToPrimitive<Reference2LongMap<?>>(Reference2LongOpenHashMap::new, Long.TYPE))
.register(new TypeToken<Table<?, ?, ?>>() {}, new TableSerializer())
.register(new StringRepresentableSerializer())
.register(IntOr.Default.SERIALIZER)
.register(IntOr.Disabled.SERIALIZER)
.register(DoubleOr.Default.SERIALIZER)
.register(BooleanOrDefault.SERIALIZER)
.register(Duration.SERIALIZER)
.register(EngineMode.SERIALIZER)
.register(FallbackValueSerializer.create(contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get(), MinecraftServer::getServer))
.register(new RegistryValueSerializer<>(new TypeToken<EntityType<?>>() {}, access, Registries.ENTITY_TYPE, true))
.register(new RegistryValueSerializer<>(Item.class, access, Registries.ITEM, true))
.register(new RegistryHolderSerializer<>(new TypeToken<ConfiguredFeature<?, ?>>() {}, access, Registries.CONFIGURED_FEATURE, false))
.register(new RegistryHolderSerializer<>(Item.class, access, Registries.ITEM, true))
)
);
}
@Override
protected void applyWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode node, final @Nullable ConfigurationNode defaultsNode) throws ConfigurateException {
final ConfigurationNode version = node.node(Configuration.VERSION_FIELD);
final String world = contextMap.require(WORLD_NAME);
if (version.virtual()) {
LOGGER.warn("The DivineMC world config file for " + world + " didn't have a version set, assuming latest");
version.raw(DivineWorldConfiguration.CURRENT_VERSION);
}
if (DivineRemovedConfiguration.REMOVED_WORLD_PATHS.length > 0) {
ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
for (NodePath path : DivineRemovedConfiguration.REMOVED_WORLD_PATHS) {
builder.addAction(path, TransformAction.remove());
}
builder.build().apply(node);
}
// ADD FUTURE TRANSFORMS HERE
}
@Override
protected void applyGlobalConfigTransformations(ConfigurationNode node) throws ConfigurateException {
if (DivineRemovedConfiguration.REMOVED_GLOBAL_PATHS.length > 0) {
ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
for (NodePath path : DivineRemovedConfiguration.REMOVED_GLOBAL_PATHS) {
builder.addAction(path, TransformAction.remove());
}
builder.build().apply(node);
}
// ADD FUTURE TRANSFORMS HERE
}
private static final List<Transformations.DefaultsAware> DEFAULT_AWARE_TRANSFORMATIONS = Collections.emptyList();
@Override
protected void applyDefaultsAwareWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode worldNode, final ConfigurationNode defaultsNode) throws ConfigurateException {
final ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder();
// ADD FUTURE TRANSFORMS HERE (these transforms run after the defaults have been merged into the node)
DEFAULT_AWARE_TRANSFORMATIONS.forEach(transform -> transform.apply(builder, contextMap, defaultsNode));
ConfigurationTransformation transformation;
try {
transformation = builder.build(); // build throws IAE if no actions were provided (bad zml)
} catch (IllegalArgumentException ignored) {
return;
}
transformation.apply(worldNode);
}
@Override
public DivineWorldConfiguration createWorldConfig(final ContextMap contextMap) {
final String levelName = contextMap.require(WORLD_NAME);
try {
return super.createWorldConfig(contextMap);
} catch (IOException exception) {
throw new RuntimeException("Could not create DivineMC world config for " + levelName, exception);
}
}
@Override
protected boolean isConfigType(final Type type) {
return ConfigurationPart.class.isAssignableFrom(erase(type));
}
public void reloadConfigs(MinecraftServer server) {
try {
this.initializeGlobalConfiguration(server.registryAccess(), reloader(this.globalConfigClass, DivineGlobalConfiguration.get()));
this.initializeWorldDefaultsConfiguration(server.registryAccess());
for (ServerLevel level : server.getAllLevels()) {
this.createWorldConfig(PaperConfigurations.createWorldContextMap(level), reloader(this.worldConfigClass, level.divineConfig()));
}
} catch (Exception ex) {
throw new RuntimeException("Could not reload DivineMC configuration files", ex);
}
}
public static DivineConfigurations setup(final Path configDir) throws Exception {
try {
PaperConfigurations.createDirectoriesSymlinkAware(configDir);
return new DivineConfigurations(configDir);
} catch (final IOException ex) {
throw new RuntimeException("Could not setup DivineConfigurations", ex);
}
}
@Override
protected int globalConfigVersion() {
return DivineGlobalConfiguration.CURRENT_VERSION;
}
@Override
protected int worldConfigVersion() {
return getWorldConfigurationCurrentVersion();
}
@Override
public int getWorldConfigurationCurrentVersion() {
return DivineWorldConfiguration.CURRENT_VERSION;
}
}

View File

@@ -1,95 +0,0 @@
package space.bxteam.divinemc.configuration;
import io.papermc.paper.configuration.Configuration;
import io.papermc.paper.configuration.ConfigurationPart;
import org.bukkit.Bukkit;
import org.spongepowered.configurate.objectmapping.meta.PostProcess;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.logging.Level;
@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
public class DivineGlobalConfiguration extends ConfigurationPart {
static final int CURRENT_VERSION = 4;
private static DivineGlobalConfiguration instance;
public static DivineGlobalConfiguration get() {
return instance;
}
static void set(DivineGlobalConfiguration instance) {
DivineGlobalConfiguration.instance = instance;
}
@Setting(Configuration.VERSION_FIELD)
public int version = CURRENT_VERSION;
public AsyncPathfinding asyncPathfinding;
public class AsyncPathfinding extends ConfigurationPart {
public boolean asyncPathfinding = true;
public int asyncPathfindingMaxThreads = 0;
public int asyncPathfindingKeepalive = 60;
@PostProcess
public void init() {
if (asyncPathfindingMaxThreads < 0) {
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() + asyncPathfindingMaxThreads, 1);
} else if (asyncPathfindingMaxThreads == 0) {
asyncPathfindingMaxThreads = Math.max(Runtime.getRuntime().availableProcessors() / 4, 1);
}
if (!asyncPathfinding) {
asyncPathfindingMaxThreads = 0;
} else {
Bukkit.getLogger().log(Level.INFO, "Using " + asyncPathfindingMaxThreads + " threads for Async Pathfinding");
}
}
}
public MultithreadTracker multithreadTracker;
public class MultithreadTracker extends ConfigurationPart {
public boolean multithreadedEnabled = false;
public boolean multithreadedCompatModeEnabled = false;
public int asyncEntityTrackerMaxThreads = 0;
public int asyncEntityTrackerKeepalive = 60;
@PostProcess
public void init() {
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 {
Bukkit.getLogger().log(Level.INFO, "Using " + asyncEntityTrackerMaxThreads + " threads for Async Entity Tracker");
}
}
}
public Optimizations optimizations;
public class Optimizations extends ConfigurationPart {
public boolean optimizedDragonRespawn = true;
}
public Chat chat;
public class Chat extends ConfigurationPart {
public boolean noChatSign = true;
}
public Misc misc;
public class Misc extends ConfigurationPart {
public boolean disableNonEditableSignWarning = true;
public boolean removeVanillaUsernameCheck = false;
public boolean disableMovedWronglyThreshold = false;
public boolean enableSecureSeed = false;
}
}

View File

@@ -1,9 +0,0 @@
package space.bxteam.divinemc.configuration;
import org.spongepowered.configurate.NodePath;
interface DivineRemovedConfiguration {
NodePath[] REMOVED_WORLD_PATHS = {};
NodePath[] REMOVED_GLOBAL_PATHS = {};
}

View File

@@ -0,0 +1,94 @@
package space.bxteam.divinemc.configuration;
import org.apache.commons.lang.BooleanUtils;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static space.bxteam.divinemc.configuration.DivineConfig.log;
@SuppressWarnings("unused")
public class DivineWorldConfig {
private final String worldName;
private final World.Environment environment;
public DivineWorldConfig(String worldName, World.Environment environment) {
this.worldName = worldName;
this.environment = environment;
init();
}
public void init() {
log("-------- World Settings For [" + worldName + "] --------");
DivineConfig.readConfig(DivineWorldConfig.class, this);
}
private void set(String path, Object val) {
DivineConfig.config.addDefault("world-settings.default." + path, val);
DivineConfig.config.set("world-settings.default." + path, val);
if (DivineConfig.config.get("world-settings." + worldName + "." + path) != null) {
DivineConfig.config.addDefault("world-settings." + worldName + "." + path, val);
DivineConfig.config.set("world-settings." + worldName + "." + path, val);
}
}
private ConfigurationSection getConfigurationSection(String path) {
ConfigurationSection section = DivineConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
return section != null ? section : DivineConfig.config.getConfigurationSection("world-settings.default." + path);
}
private String getString(String path, String def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getString("world-settings." + worldName + "." + path, DivineConfig.config.getString("world-settings.default." + path));
}
private boolean getBoolean(String path, boolean def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getBoolean("world-settings." + worldName + "." + path, DivineConfig.config.getBoolean("world-settings.default." + path));
}
private boolean getBoolean(String path, Predicate<Boolean> predicate) {
String val = getString(path, "default").toLowerCase();
Boolean bool = BooleanUtils.toBooleanObject(val, "true", "false", "default");
return predicate.test(bool);
}
private double getDouble(String path, double def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getDouble("world-settings." + worldName + "." + path, DivineConfig.config.getDouble("world-settings.default." + path));
}
private int getInt(String path, int def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getInt("world-settings." + worldName + "." + path, DivineConfig.config.getInt("world-settings.default." + path));
}
private <T> List<?> getList(String path, T def) {
DivineConfig.config.addDefault("world-settings.default." + path, def);
return DivineConfig.config.getList("world-settings." + worldName + "." + path, DivineConfig.config.getList("world-settings.default." + path));
}
private Map<String, Object> getMap(String path, Map<String, Object> def) {
final Map<String, Object> fallback = DivineConfig.getMap("world-settings.default." + path, def);
final Map<String, Object> value = DivineConfig.getMap("world-settings." + worldName + "." + path, null);
return value.isEmpty() ? fallback : value;
}
public boolean despawnShulkerBulletsOnOwnerDeath = true;
private void despawnShulkerBulletsOnOwnerDeath() {
despawnShulkerBulletsOnOwnerDeath = getBoolean("gameplay-mechanics.mob.shulker.despawn-bullets-on-player-death", despawnShulkerBulletsOnOwnerDeath);
}
public boolean saveFireworks = false;
private void projectiles() {
saveFireworks = getBoolean("gameplay-mechanics.should-save-fireworks", saveFireworks);
}
public boolean suppressErrorsFromDirtyAttributes = true;
private void suppressErrorsFromDirtyAttributes() {
suppressErrorsFromDirtyAttributes = getBoolean("suppress-errors-from-dirty-attributes", suppressErrorsFromDirtyAttributes);
}
}

View File

@@ -1,61 +0,0 @@
package space.bxteam.divinemc.configuration;
import com.mojang.logging.LogUtils;
import io.papermc.paper.configuration.Configuration;
import io.papermc.paper.configuration.ConfigurationPart;
import io.papermc.paper.configuration.PaperConfigurations;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.spigotmc.SpigotWorldConfig;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import org.spongepowered.configurate.objectmapping.meta.Setting;
@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"})
public class DivineWorldConfiguration extends ConfigurationPart {
private static final Logger LOGGER = LogUtils.getLogger();
public static final int CURRENT_VERSION = 4;
private transient final SpigotWorldConfig spigotConfig;
private transient final ResourceLocation worldKey;
public DivineWorldConfiguration(SpigotWorldConfig spigotConfig, ResourceLocation worldKey) {
this.spigotConfig = spigotConfig;
this.worldKey = worldKey;
}
public boolean isDefault() {
return this.worldKey.equals(PaperConfigurations.WORLD_DEFAULTS_KEY);
}
@Setting(Configuration.VERSION_FIELD)
public int version = CURRENT_VERSION;
public Optimizations optimizations;
public class Optimizations extends ConfigurationPart {
public boolean suppressErrorsFromDirtyAttributes = true;
}
public GameplayMechanics gameplayMechanics;
public class GameplayMechanics extends ConfigurationPart {
public Mob mob;
public class Mob extends ConfigurationPart {
public Shulker shulker;
public class Shulker extends ConfigurationPart {
@Comment("If true, shulker bullets will despawn when their owner dies.")
public boolean despawnShulkerBulletsOnOwnerDeath = true;
}
}
public Projectiles projectiles;
public class Projectiles extends ConfigurationPart {
public boolean snowballCanKnockback = true;
public boolean eggCanKnockback = true;
public boolean saveFireworks = false;
}
}
}

View File

@@ -15,8 +15,8 @@ import java.util.function.Consumer;
public class AsyncPathProcessor {
private static final Executor pathProcessingExecutor = new ThreadPoolExecutor(
1,
space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().asyncPathfinding.asyncPathfindingMaxThreads,
space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().asyncPathfinding.asyncPathfindingKeepalive, TimeUnit.SECONDS,
space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingMaxThreads,
space.bxteam.divinemc.configuration.DivineConfig.asyncPathfindingKeepalive, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder()
.setNameFormat("DivineMC Async Pathfinding Thread - %d")

View File

@@ -0,0 +1,94 @@
package space.bxteam.divinemc.seed;
import com.google.common.collect.Iterables;
import net.minecraft.server.level.ServerLevel;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Optional;
public class Globals {
public static final int WORLD_SEED_LONGS = 16;
public static final int WORLD_SEED_BITS = WORLD_SEED_LONGS * 64;
public static final long[] worldSeed = new long[WORLD_SEED_LONGS];
public static final ThreadLocal<Integer> dimension = ThreadLocal.withInitial(() -> 0);
public enum Salt {
UNDEFINED,
BASTION_FEATURE,
WOODLAND_MANSION_FEATURE,
MINESHAFT_FEATURE,
BURIED_TREASURE_FEATURE,
NETHER_FORTRESS_FEATURE,
PILLAGER_OUTPOST_FEATURE,
GEODE_FEATURE,
NETHER_FOSSIL_FEATURE,
OCEAN_MONUMENT_FEATURE,
RUINED_PORTAL_FEATURE,
POTENTIONAL_FEATURE,
GENERATE_FEATURE,
JIGSAW_PLACEMENT,
STRONGHOLDS,
POPULATION,
DECORATION,
SLIME_CHUNK
}
public static void setupGlobals(ServerLevel world) {
if (!space.bxteam.divinemc.configuration.DivineConfig.enableSecureSeed) return;
long[] seed = world.getServer().getWorldData().worldGenOptions().featureSeed();
System.arraycopy(seed, 0, worldSeed, 0, WORLD_SEED_LONGS);
int worldIndex = Iterables.indexOf(world.getServer().levelKeys(), it -> it == world.dimension());
if (worldIndex == -1)
worldIndex = world.getServer().levelKeys().size(); // if we are in world construction it may not have been added to the map yet
dimension.set(worldIndex);
}
public static long[] createRandomWorldSeed() {
long[] seed = new long[WORLD_SEED_LONGS];
SecureRandom rand = new SecureRandom();
for (int i = 0; i < WORLD_SEED_LONGS; i++) {
seed[i] = rand.nextLong();
}
return seed;
}
// 1024-bit string -> 16 * 64 long[]
public static Optional<long[]> parseSeed(String seedStr) {
if (seedStr.isEmpty()) return Optional.empty();
if (seedStr.length() != WORLD_SEED_BITS) {
throw new IllegalArgumentException("Secure seed length must be " + WORLD_SEED_BITS + "-bit but found " + seedStr.length() + "-bit.");
}
long[] seed = new long[WORLD_SEED_LONGS];
for (int i = 0; i < WORLD_SEED_LONGS; i++) {
int start = i * 64;
int end = start + 64;
String seedSection = seedStr.substring(start, end);
BigInteger seedInDecimal = new BigInteger(seedSection, 2);
seed[i] = seedInDecimal.longValue();
}
return Optional.of(seed);
}
// 16 * 64 long[] -> 1024-bit string
public static String seedToString(long[] seed) {
StringBuilder sb = new StringBuilder();
for (long longV : seed) {
// Convert to 64-bit binary string per long
// Use format to keep 64-bit length, and use 0 to complete space
String binaryStr = String.format("%64s", Long.toBinaryString(longV)).replace(' ', '0');
sb.append(binaryStr);
}
return sb.toString();
}
}

View File

@@ -0,0 +1,73 @@
package space.bxteam.divinemc.seed;
public class Hashing {
// https://en.wikipedia.org/wiki/BLAKE_(hash_function)
// https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/digests/Blake2bDigest.java
private final static long[] blake2b_IV = {
0x6a09e667f3bcc908L, 0xbb67ae8584caa73bL, 0x3c6ef372fe94f82bL,
0xa54ff53a5f1d36f1L, 0x510e527fade682d1L, 0x9b05688c2b3e6c1fL,
0x1f83d9abfb41bd6bL, 0x5be0cd19137e2179L
};
private final static byte[][] blake2b_sigma = {
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}
};
public static long[] hashWorldSeed(long[] worldSeed) {
long[] result = blake2b_IV.clone();
result[0] ^= 0x01010040;
hash(worldSeed, result, new long[16], 0, false);
return result;
}
public static void hash(long[] message, long[] chainValue, long[] internalState, long messageOffset, boolean isFinal) {
assert message.length == 16;
assert chainValue.length == 8;
assert internalState.length == 16;
System.arraycopy(chainValue, 0, internalState, 0, chainValue.length);
System.arraycopy(blake2b_IV, 0, internalState, chainValue.length, 4);
internalState[12] = messageOffset ^ blake2b_IV[4];
internalState[13] = blake2b_IV[5];
if (isFinal) internalState[14] = ~blake2b_IV[6];
internalState[15] = blake2b_IV[7];
for (int round = 0; round < 12; round++) {
G(message[blake2b_sigma[round][0]], message[blake2b_sigma[round][1]], 0, 4, 8, 12, internalState);
G(message[blake2b_sigma[round][2]], message[blake2b_sigma[round][3]], 1, 5, 9, 13, internalState);
G(message[blake2b_sigma[round][4]], message[blake2b_sigma[round][5]], 2, 6, 10, 14, internalState);
G(message[blake2b_sigma[round][6]], message[blake2b_sigma[round][7]], 3, 7, 11, 15, internalState);
G(message[blake2b_sigma[round][8]], message[blake2b_sigma[round][9]], 0, 5, 10, 15, internalState);
G(message[blake2b_sigma[round][10]], message[blake2b_sigma[round][11]], 1, 6, 11, 12, internalState);
G(message[blake2b_sigma[round][12]], message[blake2b_sigma[round][13]], 2, 7, 8, 13, internalState);
G(message[blake2b_sigma[round][14]], message[blake2b_sigma[round][15]], 3, 4, 9, 14, internalState);
}
for (int i = 0; i < 8; i++) {
chainValue[i] ^= internalState[i] ^ internalState[i + 8];
}
}
private static void G(long m1, long m2, int posA, int posB, int posC, int posD, long[] internalState) {
internalState[posA] = internalState[posA] + internalState[posB] + m1;
internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 32);
internalState[posC] = internalState[posC] + internalState[posD];
internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 24); // replaces 25 of BLAKE
internalState[posA] = internalState[posA] + internalState[posB] + m2;
internalState[posD] = Long.rotateRight(internalState[posD] ^ internalState[posA], 16);
internalState[posC] = internalState[posC] + internalState[posD];
internalState[posB] = Long.rotateRight(internalState[posB] ^ internalState[posC], 63); // replaces 11 of BLAKE
}
}

View File

@@ -0,0 +1,159 @@
package space.bxteam.divinemc.seed;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
public class WorldgenCryptoRandom extends WorldgenRandom {
// hash the world seed to guard against badly chosen world seeds
private static final long[] HASHED_ZERO_SEED = Hashing.hashWorldSeed(new long[Globals.WORLD_SEED_LONGS]);
private static final ThreadLocal<long[]> LAST_SEEN_WORLD_SEED = ThreadLocal.withInitial(() -> new long[Globals.WORLD_SEED_LONGS]);
private static final ThreadLocal<long[]> HASHED_WORLD_SEED = ThreadLocal.withInitial(() -> HASHED_ZERO_SEED);
private final long[] worldSeed = new long[Globals.WORLD_SEED_LONGS];
private final long[] randomBits = new long[8];
private int randomBitIndex;
private static final int MAX_RANDOM_BIT_INDEX = 64 * 8;
private static final int LOG2_MAX_RANDOM_BIT_INDEX = 9;
private long counter;
private final long[] message = new long[16];
private final long[] cachedInternalState = new long[16];
public WorldgenCryptoRandom(int x, int z, Globals.Salt typeSalt, long salt) {
super(new LegacyRandomSource(0L));
if (typeSalt != null) {
this.setSecureSeed(x, z, typeSalt, salt);
}
}
public void setSecureSeed(int x, int z, Globals.Salt typeSalt, long salt) {
System.arraycopy(Globals.worldSeed, 0, this.worldSeed, 0, Globals.WORLD_SEED_LONGS);
message[0] = ((long) x << 32) | ((long) z & 0xffffffffL);
message[1] = ((long) Globals.dimension.get() << 32) | ((long) salt & 0xffffffffL);
message[2] = typeSalt.ordinal();
message[3] = counter = 0;
randomBitIndex = MAX_RANDOM_BIT_INDEX;
}
private long[] getHashedWorldSeed() {
if (!Arrays.equals(worldSeed, LAST_SEEN_WORLD_SEED.get())) {
HASHED_WORLD_SEED.set(Hashing.hashWorldSeed(worldSeed));
System.arraycopy(worldSeed, 0, LAST_SEEN_WORLD_SEED.get(), 0, Globals.WORLD_SEED_LONGS);
}
return HASHED_WORLD_SEED.get();
}
private void moreRandomBits() {
message[3] = counter++;
System.arraycopy(getHashedWorldSeed(), 0, randomBits, 0, 8);
Hashing.hash(message, randomBits, cachedInternalState, 64, true);
}
private long getBits(int count) {
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) {
moreRandomBits();
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
}
int alignment = randomBitIndex & 63;
if ((randomBitIndex >>> 6) == ((randomBitIndex + count) >>> 6)) {
long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << count) - 1);
randomBitIndex += count;
return result;
} else {
long result = (randomBits[randomBitIndex >>> 6] >>> alignment) & ((1L << (64 - alignment)) - 1);
randomBitIndex += count;
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX) {
moreRandomBits();
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
}
alignment = randomBitIndex & 63;
result <<= alignment;
result |= (randomBits[randomBitIndex >>> 6] >>> (64 - alignment)) & ((1L << alignment) - 1);
return result;
}
}
@Override
public @NotNull RandomSource fork() {
WorldgenCryptoRandom fork = new WorldgenCryptoRandom(0, 0, null, 0);
System.arraycopy(Globals.worldSeed, 0, fork.worldSeed, 0, Globals.WORLD_SEED_LONGS);
fork.message[0] = this.message[0];
fork.message[1] = this.message[1];
fork.message[2] = this.message[2];
fork.message[3] = this.message[3];
fork.randomBitIndex = this.randomBitIndex;
fork.counter = this.counter;
fork.nextLong();
return fork;
}
@Override
public int next(int bits) {
return (int) getBits(bits);
}
@Override
public void consumeCount(int count) {
randomBitIndex += count;
if (randomBitIndex >= MAX_RANDOM_BIT_INDEX * 2) {
randomBitIndex -= MAX_RANDOM_BIT_INDEX;
counter += randomBitIndex >>> LOG2_MAX_RANDOM_BIT_INDEX;
randomBitIndex &= MAX_RANDOM_BIT_INDEX - 1;
randomBitIndex += MAX_RANDOM_BIT_INDEX;
}
}
@Override
public int nextInt(int bound) {
int bits = Mth.ceillog2(bound);
int result;
do {
result = (int) getBits(bits);
} while (result >= bound);
return result;
}
@Override
public long nextLong() {
return getBits(64);
}
@Override
public double nextDouble() {
return getBits(53) * 0x1.0p-53;
}
@Override
public long setDecorationSeed(long worldSeed, int blockX, int blockZ) {
setSecureSeed(blockX, blockZ, Globals.Salt.POPULATION, 0);
return ((long) blockX << 32) | ((long) blockZ & 0xffffffffL);
}
@Override
public void setFeatureSeed(long populationSeed, int index, int step) {
setSecureSeed((int) (populationSeed >> 32), (int) populationSeed, Globals.Salt.DECORATION, index + 10000L * step);
}
@Override
public void setLargeFeatureSeed(long worldSeed, int chunkX, int chunkZ) {
super.setLargeFeatureSeed(worldSeed, chunkX, chunkZ);
}
@Override
public void setLargeFeatureWithSalt(long worldSeed, int regionX, int regionZ, int salt) {
super.setLargeFeatureWithSalt(worldSeed, regionX, regionZ, salt);
}
public static RandomSource seedSlimeChunk(int chunkX, int chunkZ) {
return new WorldgenCryptoRandom(chunkX, chunkZ, Globals.Salt.SLIME_CHUNK, 0);
}
}

View File

@@ -31,8 +31,8 @@ public class MultithreadedTracker {
private static final Executor trackerExecutor = new ThreadPoolExecutor(
1,
space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().multithreadTracker.asyncEntityTrackerMaxThreads,
space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().multithreadTracker.asyncEntityTrackerKeepalive, TimeUnit.SECONDS,
space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerMaxThreads,
space.bxteam.divinemc.configuration.DivineConfig.asyncEntityTrackerKeepalive, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new ThreadFactoryBuilder()
.setThreadFactory(
@@ -55,7 +55,7 @@ public class MultithreadedTracker {
public static void tick(ChunkSystemServerLevel level) {
try {
if (!space.bxteam.divinemc.configuration.DivineGlobalConfiguration.get().multithreadTracker.multithreadedCompatModeEnabled) {
if (!space.bxteam.divinemc.configuration.DivineConfig.multithreadedCompatModeEnabled) {
tickAsync(level);
} else {
tickAsyncWithCompatMode(level);