From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Wed, 23 Nov 2022 21:05:47 +0100 Subject: [PATCH] Gale configuration License: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) Gale - https://galemc.org This patch is based on the following patch: "Paper config files" By: Jake Potrebic As part of: Paper (https://github.com/PaperMC/Paper) Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index 7620c72a4c243cbeea245203ce03a97cbfa7d922..29796a404e210f7864f4b33b3367e02531bca2a0 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -240,7 +240,10 @@ public class TimingsExport extends Thread { parent.put("config", createObject( pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), - pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) + // Gale start - Gale configuration - include in timings + pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), + pair("gale", mapAsJSON(Bukkit.spigot().getGaleConfig(), null)) + // Gale end - Gale configuration - include in timings )); new TimingsExport(listeners, parent, history).start(); @@ -281,7 +284,7 @@ public class TimingsExport extends Thread { return timingsCost; } - private static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) { + public static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) { // Gale - Gale configuration JSONObject object = new JSONObject(); for (String key : config.getKeys(false)) { diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java index 87e5f614ba988547a827486740db217e28585773..38d99d73a33911afa71d02a456bb4df62ce75166 100644 --- a/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -7,8 +7,11 @@ import io.papermc.paper.configuration.constraint.Constraint; import io.papermc.paper.configuration.constraint.Constraints; import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.GameRules; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; import org.checkerframework.checker.nullness.qual.Nullable; import org.jetbrains.annotations.MustBeInvokedByOverriders; import org.slf4j.Logger; @@ -96,7 +99,7 @@ public abstract class Configurations { }; } - static CheckedFunction reloader(Class type, T instance) { + public static CheckedFunction reloader(Class type, T instance) { // Gale - Gale configuration return node -> { ObjectMapper.Factory factory = (ObjectMapper.Factory) Objects.requireNonNull(node.options().serializers().get(type)); ObjectMapper.Mutable mutable = (ObjectMapper.Mutable) factory.get(type); @@ -170,7 +173,7 @@ public abstract class Configurations { final YamlConfigurationLoader loader = result.loader(); final ConfigurationNode node = loader.load(); if (result.isNewFile()) { // add version to new files - node.node(Configuration.VERSION_FIELD).raw(this.worldConfigVersion()); + node.node(Configuration.VERSION_FIELD).raw(getWorldConfigurationCurrentVersion()); // Gale - Gale configuration } else { this.verifyWorldConfigVersion(contextMap, node); } @@ -232,7 +235,7 @@ public abstract class Configurations { .build(); final ConfigurationNode worldNode = worldLoader.load(); if (newFile) { // set the version field if new file - worldNode.node(Configuration.VERSION_FIELD).set(this.worldConfigVersion()); + worldNode.node(Configuration.VERSION_FIELD).set(getWorldConfigurationCurrentVersion()); // Gale - Gale configuration } else { this.verifyWorldConfigVersion(contextMap, worldNode); } @@ -358,4 +361,25 @@ public abstract class Configurations { return "ContextKey{" + this.name + "}"; } } + + // Gale start - Gale configuration + + public static final String legacyWorldsSectionKey = "__________WORLDS__________"; + public static final String legacyWorldDefaultsSectionKey = "__defaults__"; + + @Deprecated + public YamlConfiguration createLegacyObject(final MinecraftServer server) { + YamlConfiguration global = YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.globalConfigFileName).toFile()); + ConfigurationSection worlds = global.createSection(legacyWorldsSectionKey); + worlds.set(legacyWorldDefaultsSectionKey, YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.defaultWorldConfigFileName).toFile())); + for (ServerLevel level : server.getAllLevels()) { + worlds.set(level.getWorld().getName(), YamlConfiguration.loadConfiguration(getWorldConfigFile(level).toFile())); + } + return global; + } + + public abstract int getWorldConfigurationCurrentVersion(); + + // Gale end - Gale configuration + } diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java index 83a726bcf8b7dce73a361b0d79dbd63a0afc7a12..08ca3427acd2b37140a5af28dd61004188999408 100644 --- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java @@ -321,7 +321,7 @@ public class PaperConfigurations extends Configurations globalConfig() { + // Gale start - Gale configuration + public static FieldDiscoverer galeWorldConfig(Configurations.ContextMap contextMap) { + final Map, Object> overrides = Map.of( + GaleWorldConfiguration.class, new GaleWorldConfiguration( + contextMap.require(PaperConfigurations.SPIGOT_WORLD_CONFIG_CONTEXT_KEY).get(), + contextMap.require(Configurations.WORLD_KEY) + ) + ); + return new InnerClassFieldDiscoverer(overrides); + } + // Gale end - Gale configuration + + public static FieldDiscoverer globalConfig() { // Gale - Gale configuration return new InnerClassFieldDiscoverer(Collections.emptyMap()); } } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 1591b251f082eeeef6c86e3164570206ef82f6a8..d62cb22bbc061f37a5d798c8e3a48e747971a6dd 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -3,9 +3,6 @@ package net.minecraft.server; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; -import co.aikar.timings.Timings; -import com.destroystokyo.paper.event.server.PaperServerListPingEvent; -import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -149,6 +146,9 @@ import net.minecraft.world.level.levelgen.WorldOptions; import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import net.minecraft.world.level.storage.WorldData; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import org.galemc.gale.configuration.GaleConfigurations; import org.slf4j.Logger; // CraftBukkit start @@ -317,6 +317,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public public static Services create(YggdrasilAuthenticationService authenticationService, File rootDirectory, File userCacheFile, joptsimple.OptionSet optionSet) throws Exception { // Paper - add optionset to load paper config files; add userCacheFile parameter @@ -34,7 +40,10 @@ public record Services( final java.nio.file.Path legacyConfigPath = ((File) optionSet.valueOf("paper-settings")).toPath(); final java.nio.file.Path configDirPath = ((File) optionSet.valueOf("paper-settings-directory")).toPath(); io.papermc.paper.configuration.PaperConfigurations paperConfigurations = io.papermc.paper.configuration.PaperConfigurations.setup(legacyConfigPath, configDirPath, rootDirectory.toPath(), (File) optionSet.valueOf("spigot-settings")); - return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, gameProfileCache, paperConfigurations); + // Gale start - Gale configuration + GaleConfigurations galeConfigurations = GaleConfigurations.setup(configDirPath); + return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, gameProfileCache, paperConfigurations, galeConfigurations); + // Gale end - Gale configuration // Paper end - load paper config files from cli options } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 7d82cc6b847124cf4225428ba310309544928148..ac703c9ef504779dffbd8d7e676dbb51fb94dd29 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -226,6 +226,10 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess()); this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); // Paper end - initialize global and world-defaults configuration + // Gale start - Gale configuration + galeConfigurations.initializeGlobalConfiguration(this.registryAccess()); + galeConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess()); + // Gale end - Gale configuration // Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save if (this.convertOldUsers()) { this.getProfileCache().save(false); // Paper diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 798016774df02c3f7ebf909c9cc125f8427a39be..11f0017a8557f7528e379285bbb55e37c533463f 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -502,7 +502,7 @@ public class ServerLevel extends Level implements WorldGenLevel, ca.spottedleaf. // Holder holder = worlddimension.type(); // CraftBukkit - decompile error // Objects.requireNonNull(minecraftserver); // CraftBukkit - decompile error - super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor + super(iworlddataserver, resourcekey, minecraftserver.registryAccess(), worlddimension.type(), minecraftserver::getProfiler, false, flag, i, minecraftserver.getMaxChainedNeighborUpdates(), gen, biomeProvider, env, spigotConfig -> minecraftserver.paperConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), spigotConfig -> minecraftserver.galeConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig, minecraftserver.registryAccess(), iworlddataserver.getGameRules())), executor); // Paper - create paper world configs; Async-Anti-Xray: Pass executor // Gale - Gale configuration this.pvpMode = minecraftserver.isPvpAllowed(); this.convertable = convertable_conversionsession; this.uuid = WorldUUID.getUUID(convertable_conversionsession.levelDirectory.path().toFile()); diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java index fee155c81df385faa474e3aec777a30375ecb07d..676969f0a0fe21dae1c30d63a8bb4b0e9b33d057 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -12,7 +12,6 @@ import java.util.function.Supplier; import javax.annotation.Nullable; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; @@ -42,8 +41,6 @@ import net.minecraft.world.TickRateManager; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSources; import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.boss.EnderDragonPart; -import net.minecraft.world.entity.boss.enderdragon.EnderDragon; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; @@ -100,6 +97,7 @@ import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.util.CraftSpawnCategory; import org.bukkit.entity.SpawnCategory; import org.bukkit.event.block.BlockPhysicsEvent; +import org.galemc.gale.configuration.GaleWorldConfiguration; // CraftBukkit end public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedleaf.moonrise.patches.chunk_system.level.ChunkSystemLevel, ca.spottedleaf.moonrise.patches.chunk_system.world.ChunkSystemEntityGetter { // Paper - rewrite chunk system @@ -170,6 +168,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl return this.paperConfig; } // Paper end - add paper world config + // Gale start - Gale configuration + private final GaleWorldConfiguration galeConfig; + public GaleWorldConfiguration galeConfig() { + return this.galeConfig; + } + // Gale end - Gale configuration public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public final co.aikar.timings.WorldTimingsHandler timings; // Paper @@ -258,9 +262,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl } // Paper end - rewrite chunk system - protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config & Anti-Xray + protected Level(WritableLevelData worlddatamutable, ResourceKey resourcekey, RegistryAccess iregistrycustom, Holder holder, Supplier supplier, boolean flag, boolean flag1, long i, int j, org.bukkit.generator.ChunkGenerator gen, org.bukkit.generator.BiomeProvider biomeProvider, org.bukkit.World.Environment env, java.util.function.Function paperWorldConfigCreator, java.util.function.Function galeWorldConfigCreator, java.util.concurrent.Executor executor) { // Paper - create paper world config & Anti-Xray // Gale - Gale configuration this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper - create paper world config + this.galeConfig = galeWorldConfigCreator.apply(this.spigotConfig); // Gale - Gale configuration this.generator = gen; this.world = new CraftWorld((ServerLevel) this, gen, biomeProvider, env); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index caf6ff33b42472d30f28629470e12889f50490cc..eb4c9014afe2b59e991a2812c55eaa226925272c 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -1059,6 +1059,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) this.console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); + this.console.galeConfigurations.reloadConfigs(this.console); // Gale - Gale configuration for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty world.setSpawnSettings(world.serverLevelData.getDifficulty() != Difficulty.PEACEFUL && config.spawnMonsters, config.spawnAnimals); // Paper - per level difficulty (from MinecraftServer#setDifficulty(ServerLevel, Difficulty, boolean)) @@ -2978,6 +2979,14 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } + // Gale start - Gale configuration - API + @Override + public YamlConfiguration getGaleConfig() + { + return CraftServer.this.console.galeConfigurations.createLegacyObject(CraftServer.this.console); + } + // Gale end - Gale configuration - API + @Override public void restart() { org.spigotmc.RestartCommand.restart(); diff --git a/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..de25d0b4001363e63873af8f0f2165e5449786df --- /dev/null +++ b/src/main/java/org/galemc/gale/configuration/GaleConfigurations.java @@ -0,0 +1,304 @@ +// Gale - Gale configuration + +package org.galemc.gale.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.GlobalConfiguration; +import io.papermc.paper.configuration.WorldConfiguration; +import io.papermc.paper.configuration.mapping.InnerClassFieldDiscoverer; +import io.papermc.paper.configuration.NestedSetting; +import io.papermc.paper.configuration.PaperConfigurations; +import io.papermc.paper.configuration.legacy.RequiresSpigotInitialization; +import io.papermc.paper.configuration.serializer.ComponentSerializer; +import io.papermc.paper.configuration.serializer.EnumValueSerializer; +import io.papermc.paper.configuration.serializer.collections.FastutilMapSerializer; +import io.papermc.paper.configuration.serializer.PacketClassSerializer; +import io.papermc.paper.configuration.serializer.StringRepresentableSerializer; +import io.papermc.paper.configuration.serializer.collections.TableSerializer; +import io.papermc.paper.configuration.serializer.collections.MapSerializer; +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.number.DoubleOr; +import io.papermc.paper.configuration.type.Duration; +import io.papermc.paper.configuration.type.EngineMode; +import io.papermc.paper.configuration.type.number.IntOr; +import io.papermc.paper.configuration.type.fallback.FallbackValueSerializer; +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.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 GaleConfigurations extends Configurations { + + private static final Logger LOGGER = LogUtils.getLogger(); + static final String GLOBAL_CONFIG_FILE_NAME = "gale-global.yml"; + static final String WORLD_DEFAULTS_CONFIG_FILE_NAME = "gale-world-defaults.yml"; + static final String WORLD_CONFIG_FILE_NAME = "gale-world.yml"; + public static final String CONFIG_DIR = "config"; + + private static final String GLOBAL_HEADER = String.format(""" + This is the global configuration file for Gale. + As you can see, there's a lot to configure. Some options may impact gameplay, so use + with caution, and make sure you know what each option does before configuring. + + If you need help with the configuration or have any questions related to Gale, + join us in our Discord, or check the GitHub Wiki pages. + + The world configuration options are inside + their respective world folder. The files are named %s + + Wiki: https://github.com/GaleMC/Gale/wiki + Discord: https://discord.gg/gwezNT8c24""", WORLD_CONFIG_FILE_NAME); + + private static final String WORLD_DEFAULTS_HEADER = """ + This is the world defaults configuration file for Gale. + As you can see, there's a lot to configure. Some options may impact gameplay, so use + with caution, and make sure you know what each option does before configuring. + + If you need help with the configuration or have any questions related to Gale, + join us in our Discord, or check the GitHub Wiki pages. + + Configuration options here apply to all worlds, unless you specify overrides inside + the world-specific config file inside each world folder. + + Wiki: https://github.com/GaleMC/Gale/wiki + Discord: https://discord.gg/gwezNT8c24"""; + + private static final Function WORLD_HEADER = map -> String.format(""" + This is a world configuration file for Gale. + 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) + ); + + private static final String MOVED_NOTICE = """ + The global and world default configuration files have moved to %s + and the world-specific configuration file has been moved inside + the respective world folder. + + See https://github.com/GaleMC/Gale/wiki for more information. + """; + + public GaleConfigurations(final Path globalFolder) { + super(globalFolder, GaleGlobalConfiguration.class, GaleWorldConfiguration.class, GLOBAL_CONFIG_FILE_NAME, WORLD_DEFAULTS_CONFIG_FILE_NAME, WORLD_CONFIG_FILE_NAME); + } + + @Override + protected YamlConfigurationLoader.Builder createLoaderBuilder() { + return super.createLoaderBuilder() + .defaultOptions(GaleConfigurations::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() { + return super.createGlobalLoaderBuilder() + .defaultOptions(GaleConfigurations::defaultGlobalOptions); + } + + private static ConfigurationOptions defaultGlobalOptions(ConfigurationOptions options) { + return options + .header(GLOBAL_HEADER) + .serializers(builder -> builder.register(new PacketClassSerializer())); + } + + @Override + public GaleGlobalConfiguration initializeGlobalConfiguration(final RegistryAccess registryAccess) throws ConfigurateException { + GaleGlobalConfiguration configuration = super.initializeGlobalConfiguration(registryAccess); + GaleGlobalConfiguration.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.galeWorldConfig(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>() {}, new FastutilMapSerializer.SomethingToPrimitive>(Reference2IntOpenHashMap::new, Integer.TYPE)) + .register(new TypeToken>() {}, new FastutilMapSerializer.SomethingToPrimitive>(Reference2LongOpenHashMap::new, Long.TYPE)) + .register(new TypeToken>() {}, 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>() {}, access, Registries.ENTITY_TYPE, true)) + .register(new RegistryValueSerializer<>(Item.class, access, Registries.ITEM, true)) + .register(new RegistryHolderSerializer<>(new TypeToken>() {}, 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 Gale world config file for " + world + " didn't have a version set, assuming latest"); + version.raw(GaleWorldConfiguration.CURRENT_VERSION); + } + if (GaleRemovedConfigurations.REMOVED_WORLD_PATHS.length > 0) { + ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + for (NodePath path : GaleRemovedConfigurations.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 (GaleRemovedConfigurations.REMOVED_GLOBAL_PATHS.length > 0) { + ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + for (NodePath path : GaleRemovedConfigurations.REMOVED_GLOBAL_PATHS) { + builder.addAction(path, TransformAction.remove()); + } + builder.build().apply(node); + } + // ADD FUTURE TRANSFORMS HERE + } + + private static final List 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 GaleWorldConfiguration 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 Gale 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(reloader(this.globalConfigClass, GaleGlobalConfiguration.get())); + this.initializeWorldDefaultsConfiguration(server.registryAccess()); + for (ServerLevel level : server.getAllLevels()) { + this.createWorldConfig(PaperConfigurations.createWorldContextMap(level), reloader(this.worldConfigClass, level.galeConfig())); + } + } catch (Exception ex) { + throw new RuntimeException("Could not reload Gale configuration files", ex); + } + } + + public static GaleConfigurations setup(final Path configDir) throws Exception { + try { + PaperConfigurations.createDirectoriesSymlinkAware(configDir); + return new GaleConfigurations(configDir); + } catch (final IOException ex) { + throw new RuntimeException("Could not setup GaleConfigurations", ex); + } + } + + @Override + protected int globalConfigVersion() { + return GaleGlobalConfiguration.CURRENT_VERSION; + } + + @Override + protected int worldConfigVersion() { + return getWorldConfigurationCurrentVersion(); + } + + @Override + public int getWorldConfigurationCurrentVersion() { + return GaleWorldConfiguration.CURRENT_VERSION; + } + +} diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..8f8fd98f96cd390ba43033521982a13044df91cf --- /dev/null +++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java @@ -0,0 +1,30 @@ +// Gale - Gale configuration + +package org.galemc.gale.configuration; + +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) +public class GaleGlobalConfiguration extends ConfigurationPart { + static final int CURRENT_VERSION = 1; + private static GaleGlobalConfiguration instance; + public static GaleGlobalConfiguration get() { + return instance; + } + static void set(GaleGlobalConfiguration instance) { + GaleGlobalConfiguration.instance = instance; + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public SmallOptimizations smallOptimizations; + public class SmallOptimizations extends ConfigurationPart { + + public int dummyValue = 0; + + } + +} diff --git a/src/main/java/org/galemc/gale/configuration/GaleRemovedConfigurations.java b/src/main/java/org/galemc/gale/configuration/GaleRemovedConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..9db322100dacbf2343fbb86e3e83d99febfa9d9d --- /dev/null +++ b/src/main/java/org/galemc/gale/configuration/GaleRemovedConfigurations.java @@ -0,0 +1,13 @@ +// Gale - Gale configuration + +package org.galemc.gale.configuration; + +import org.spongepowered.configurate.NodePath; + +interface GaleRemovedConfigurations { + + NodePath[] REMOVED_WORLD_PATHS = {}; + + NodePath[] REMOVED_GLOBAL_PATHS = {}; + +} diff --git a/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..b82bb95b524c95cdefb81abef906eded0717e9a1 --- /dev/null +++ b/src/main/java/org/galemc/gale/configuration/GaleWorldConfiguration.java @@ -0,0 +1,40 @@ +// Gale - Gale configuration + +package org.galemc.gale.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.Setting; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "NotNullFieldNotInitialized", "InnerClassMayBeStatic"}) +public class GaleWorldConfiguration extends ConfigurationPart { + private static final Logger LOGGER = LogUtils.getLogger(); + public static final int CURRENT_VERSION = 1; + + private transient final SpigotWorldConfig spigotConfig; + private transient final ResourceLocation worldKey; + public GaleWorldConfiguration(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 SmallOptimizations smallOptimizations; + public class SmallOptimizations extends ConfigurationPart { + + public int dummyValue = 0; + + } + +} diff --git a/src/main/java/org/galemc/gale/configuration/timingsexport/GaleConfigurationTimingsExport.java b/src/main/java/org/galemc/gale/configuration/timingsexport/GaleConfigurationTimingsExport.java new file mode 100644 index 0000000000000000000000000000000000000000..579c2e69d8f6ce8398eb1297d1d1ead98c9068a5 --- /dev/null +++ b/src/main/java/org/galemc/gale/configuration/timingsexport/GaleConfigurationTimingsExport.java @@ -0,0 +1,19 @@ +// Gale - Gale configuration + +package org.galemc.gale.configuration.timingsexport; + +import co.aikar.timings.TimingsExport; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import org.json.simple.JSONObject; + +public final class GaleConfigurationTimingsExport { + + private GaleConfigurationTimingsExport() {} + + public static @NotNull JSONObject get() { + var json = TimingsExport.mapAsJSON(Bukkit.spigot().getGaleConfig(), null); + return json; + } + +}