From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: AlphaKR93 Date: Wed, 11 Jan 2023 02:24:51 +0900 Subject: [PATCH] Plazma Configurations diff --git a/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java b/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java index 7a4a7a654fe2516ed894a68f2657344df9d70f4c..ae51ab3c895b1b98d768e52b7c446bd6a3e89b9f 100644 --- a/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java +++ b/src/main/java/io/papermc/paper/configuration/ConfigurationPart.java @@ -1,6 +1,6 @@ package io.papermc.paper.configuration; -abstract class ConfigurationPart { +public abstract class ConfigurationPart { // Plazma - package -> public public static abstract class Post extends ConfigurationPart { diff --git a/src/main/java/io/papermc/paper/configuration/Configurations.java b/src/main/java/io/papermc/paper/configuration/Configurations.java index 9ef6712c70fcd8912a79f3f61e351aac09572cf3..b7f44c74089058f261163430762f027aa9a5623a 100644 --- a/src/main/java/io/papermc/paper/configuration/Configurations.java +++ b/src/main/java/io/papermc/paper/configuration/Configurations.java @@ -88,7 +88,7 @@ public abstract class Configurations { }; } - static CheckedFunction reloader(Class type, T instance) { + public static CheckedFunction reloader(Class type, T instance) { // Plazma - package -> public return node -> { ObjectMapper.Factory factory = (ObjectMapper.Factory) Objects.requireNonNull(node.options().serializers().get(type)); ObjectMapper.Mutable mutable = (ObjectMapper.Mutable) factory.get(type); @@ -148,7 +148,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(WorldConfiguration.CURRENT_VERSION); + node.node(Configuration.VERSION_FIELD).raw(getWorldConfigurationCurrentVersion()); // Plazma } this.applyWorldConfigTransformations(contextMap, node); final W instance = node.require(this.worldConfigClass); @@ -207,7 +207,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(WorldConfiguration.CURRENT_VERSION); + worldNode.node(Configuration.VERSION_FIELD).set(getWorldConfigurationCurrentVersion()); // Plazma } this.applyWorldConfigTransformations(contextMap, worldNode); this.applyDefaultsAwareWorldConfigTransformations(contextMap, worldNode, defaultsNode); @@ -232,6 +232,21 @@ public abstract class Configurations { return level.convertable.levelDirectory.path().resolve(this.worldConfigFileName); } + // Plazma start + @Deprecated + public org.bukkit.configuration.file.YamlConfiguration createLegacyObject(final net.minecraft.server.MinecraftServer server) { + org.bukkit.configuration.file.YamlConfiguration global = org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.globalConfigFileName).toFile()); + org.bukkit.configuration.ConfigurationSection worlds = global.createSection("__________WORLDS__________"); + worlds.set("__defaults__", org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(this.globalFolder.resolve(this.defaultWorldConfigFileName).toFile())); + for (ServerLevel level : server.getAllLevels()) { + worlds.set(level.getWorld().getName(), org.bukkit.configuration.file.YamlConfiguration.loadConfiguration(getWorldConfigFile(level).toFile())); + } + return global; + } + + protected abstract int getWorldConfigurationCurrentVersion(); + // Plazma end + public static class ContextMap { private static final Object VOID = new Object(); diff --git a/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java b/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java index a0aa1f1a7adf986d500a2135aa42e138aa3c4f08..28a1d21900dbff4b9d1887b9aa4e68f4703b778f 100644 --- a/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java +++ b/src/main/java/io/papermc/paper/configuration/InnerClassFieldDiscoverer.java @@ -17,7 +17,7 @@ import java.util.Map; import static io.leangen.geantyref.GenericTypeReflector.erase; -final class InnerClassFieldDiscoverer implements FieldDiscoverer> { +public final class InnerClassFieldDiscoverer implements FieldDiscoverer> { // Plazma - package -> public private final Map, Object> instanceMap = new HashMap<>(); private final Map, Object> overrides; @@ -136,7 +136,19 @@ final class InnerClassFieldDiscoverer implements FieldDiscoverer globalConfig() { + public static FieldDiscoverer globalConfig() { // Plazma - package -> public return new InnerClassFieldDiscoverer(Collections.emptyMap()); } + + // Plazma start + public static FieldDiscoverer plazmaLevelConfiguration(Configurations.ContextMap contextMap) { + final Map, Object> overrides = Map.of( + org.plazmamc.plazma.configurations.LevelConfigurations.class, + new org.plazmamc.plazma.configurations.LevelConfigurations( + contextMap.require(Configurations.WORLD_KEY) + ) + ); + return new InnerClassFieldDiscoverer(overrides); + } + // Plazma end } diff --git a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java index 9b5c2abaa28fa60cedd9f0111e5eb018f93a0561..c09baea03786075d1a6e3f53d7f9820e23cc57cf 100644 --- a/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java +++ b/src/main/java/io/papermc/paper/configuration/PaperConfigurations.java @@ -136,7 +136,7 @@ public class PaperConfigurations extends Configurations> SPIGOT_WORLD_CONFIG_CONTEXT_KEY = new ContextKey<>(new TypeToken>() {}, "spigot world config"); + public static final ContextKey> SPIGOT_WORLD_CONFIG_CONTEXT_KEY = new ContextKey<>(new TypeToken>() {}, "spigot world config"); // Plazma - package -> public public PaperConfigurations(final Path globalFolder) { @@ -308,7 +308,7 @@ public class PaperConfigurations extends Configurations public return createWorldContextMap(level.convertable.levelDirectory.path(), level.serverLevelData.getLevelName(), level.dimension().location(), level.spigotConfig); } @@ -409,17 +409,6 @@ public class PaperConfigurations extends Configurations public if (!Files.isDirectory(path)) { Files.createDirectories(path); } } + + // Plazma start + protected int getWorldConfigurationCurrentVersion() { + return WorldConfiguration.CURRENT_VERSION; + } + // Plazma end } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index cf9f7d52b07cffd41293ba5a6fab3a7f77a2cf03..11de2f237e6dd950b8ddba3d5cfe9066d09f0903 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -301,6 +301,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop public + // Plazma start + @Override + public org.plazmamc.plazma.configurations.PlazmaConfigurations plazmaConfigurations() { + return java.util.Objects.requireNonNull(this.plazmaConfigurations); + } + // Plazma end + public static Services create(YggdrasilAuthenticationService authenticationService, File rootDirectory, File userCacheFile, joptsimple.OptionSet optionSet) throws Exception { // Paper MinecraftSessionService minecraftSessionService = authenticationService.createMinecraftSessionService(); GameProfileRepository gameProfileRepository = authenticationService.createProfileRepository(); @@ -32,7 +39,11 @@ public record Services(MinecraftSessionService sessionService, ServicesKeySet se 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); + // Plazma start + final java.nio.file.Path plazmaConfigurationDirPath = ((File) optionSet.valueOf("plazma-configurations-directory")).toPath(); + org.plazmamc.plazma.configurations.PlazmaConfigurations plazmaConfigurations = org.plazmamc.plazma.configurations.PlazmaConfigurations.setup(plazmaConfigurationDirPath); + return new Services(minecraftSessionService, authenticationService.getServicesKeySet(), gameProfileRepository, gameProfileCache, paperConfigurations, plazmaConfigurations); + // Plazma end // Paper end } diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index a9f74eeb5b6300a0b7a940da9c17bd7582b0925d..a970e00482952318c258fc406cb7c39a229b65bc 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -209,6 +209,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc. paperConfigurations.initializeGlobalConfiguration(); paperConfigurations.initializeWorldDefaultsConfiguration(); + plazmaConfigurations.initializeGlobalConfiguration(); // Plazma + plazmaConfigurations.initializeWorldDefaultsConfiguration(); // Plazma // Paper start - moved up to right after PlayerList creation but before file load/save if (this.convertOldUsers()) { this.getProfileCache().save(false); // Paper @@ -218,6 +220,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface org.spigotmc.WatchdogThread.doStart(org.spigotmc.SpigotConfig.timeoutTime, org.spigotmc.SpigotConfig.restartOnCrash); thread.start(); // Paper - start console thread after MinecraftServer.console & PaperConfig are initialized io.papermc.paper.command.PaperCommands.registerCommands(this); + org.plazmamc.plazma.commands.PlazmaCommands.registerCommands(this); // Plazma com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Purpur start try { diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java index 670d7649d25f7704b071d27157173a3139481207..e7efdd572716e50ecca217898b8a368e5829f925 100644 --- a/src/main/java/net/minecraft/server/level/ServerLevel.java +++ b/src/main/java/net/minecraft/server/level/ServerLevel.java @@ -694,7 +694,7 @@ public class ServerLevel extends Level implements WorldGenLevel { // 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)), executor); // Paper - 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)), spigotConfig -> minecraftserver.plazmaConfigurations.createWorldConfig(io.papermc.paper.configuration.PaperConfigurations.createWorldContextMap(convertable_conversionsession.levelDirectory.path(), iworlddataserver.getLevelName(), resourcekey.location(), spigotConfig)), executor); // Paper - Async-Anti-Xray - Pass executor // Plazma 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 0325eada6271f021b4cc6e398b6e3e75b915f8aa..e45a5282dc0ea3a35da24c7c3a0c7cda9a773f0c 100644 --- a/src/main/java/net/minecraft/world/level/Level.java +++ b/src/main/java/net/minecraft/world/level/Level.java @@ -174,7 +174,12 @@ public abstract class Level implements LevelAccessor, AutoCloseable { return this.paperConfig; } // Paper end - + // Plazma start + private final org.plazmamc.plazma.configurations.LevelConfigurations plazmaLevelConfiguration; + public org.plazmamc.plazma.configurations.LevelConfigurations plazmaLevelConfiguration() { + return this.plazmaLevelConfiguration; + } + // Plazma end public final com.destroystokyo.paper.antixray.ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray public final org.purpurmc.purpur.PurpurWorldConfig purpurConfig; // Purpur public final co.aikar.timings.WorldTimingsHandler timings; // Paper @@ -254,9 +259,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable { public abstract ResourceKey getTypeKey(); - 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 - Async-Anti-Xray - Pass executor + 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 plazmaLevelConfigurationCreator, java.util.concurrent.Executor executor) { // Paper - Async-Anti-Xray - Pass executor // Plazma this.spigotConfig = new org.spigotmc.SpigotWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName()); // Spigot this.paperConfig = paperWorldConfigCreator.apply(this.spigotConfig); // Paper + this.plazmaLevelConfiguration = plazmaLevelConfigurationCreator.apply(this.spigotConfig); // Plazma this.purpurConfig = new org.purpurmc.purpur.PurpurWorldConfig(((net.minecraft.world.level.storage.PrimaryLevelData) worlddatamutable).getLevelName(), env); // Purpur this.playerBreedingCooldowns = this.getNewBreedingCooldownCache(); // Purpur this.generator = gen; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 005c854a10f343cd7488b282de002249bf5c4ced..3e8f42811e9254567f2522e68b6018704bd63ae2 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -1053,6 +1053,7 @@ public final class CraftServer implements Server { org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot this.console.paperConfigurations.reloadConfigs(this.console); + this.console.plazmaConfigurations.reloadConfigurations(this.console); // Plazma org.purpurmc.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty @@ -2986,6 +2987,13 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } + // Plazma start + @Override + public YamlConfiguration getPlazmaConfiguration() { + return CraftServer.this.console.plazmaConfigurations.createLegacyObject(CraftServer.this.console); + } + // Plazma end + // Purpur start @Override public YamlConfiguration getPurpurConfig() { diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java index e006351f07356fb42e5858bb3b8a74ffee215b35..8ecf47b458ace51df2bb9f76e1015bcefe882d7c 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -173,6 +173,14 @@ public class Main { .describedAs("Jar file"); // Paper end + // Plazma start + acceptsAll(asList("plazma-dir", "plazma-configurations-directory"), "Directory for Plazma configurations") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File(io.papermc.paper.configuration.PaperConfigurations.CONFIG_DIR)) + .describedAs("Config directory"); + // Plazma end + // Purpur Start acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings") .withRequiredArg() diff --git a/src/main/java/org/plazmamc/plazma/commands/PlazmaCommand.java b/src/main/java/org/plazmamc/plazma/commands/PlazmaCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..9fb819b3745ec98d81a53650d9095ded4e9aa914 --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/commands/PlazmaCommand.java @@ -0,0 +1,127 @@ +package org.plazmamc.plazma.commands; + +import io.papermc.paper.command.CommandUtil; +import it.unimi.dsi.fastutil.Pair; +import net.kyori.adventure.text.format.NamedTextColor; +import net.minecraft.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import org.bukkit.plugin.PluginManager; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.framework.qual.DefaultQualifier; +import org.plazmamc.plazma.commands.subcommands.PlazmaSubCommand; +import org.plazmamc.plazma.commands.subcommands.ReloadCommand; +import org.plazmamc.plazma.commands.subcommands.VersionCommand; + +import java.util.*; +import java.util.stream.Collectors; + +import static net.kyori.adventure.text.Component.text; + +@DefaultQualifier(NonNull.class) +public class PlazmaCommand extends Command { + + private static final Map SUB_COMMANDS = Util.make(() -> { + final Map, PlazmaSubCommand> commands = new HashMap<>(); + + commands.put(Set.of("reload"), new ReloadCommand()); + commands.put(Set.of("version"), new VersionCommand()); + + return commands.entrySet().stream() + .flatMap(entry -> entry.getKey().stream().map(key -> Map.entry(key, entry.getValue()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + + private static final Map ALIASES = Util.make(() -> { + final Map> aliases = new HashMap<>(); + + aliases.put("reload", Set.of("rl")); + aliases.put("version", Set.of("ver")); + + return aliases.entrySet().stream() + .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey()))) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + }); + + public PlazmaCommand(final String name) { + super(name); + + final PluginManager pluginManager = Bukkit.getServer().getPluginManager(); + + final List permissions = new ArrayList<>(); + permissions.add("bukkit.command.plazma"); + permissions.addAll(SUB_COMMANDS.keySet().stream().map(s -> "bukkit.command.plazma." + s).toList()); + + this.description = "Plazma related commands"; + this.usageMessage = String.format("/plazma [%s]", String.join("|", SUB_COMMANDS.keySet())); + this.setPermission(String.join(";", permissions)); + + for (final String perm : permissions) + pluginManager.addPermission(new Permission(perm, PermissionDefault.OP)); + } + + @Override + public List tabComplete(final CommandSender sender, final String aliases, final String[] args) throws IllegalArgumentException { + if (args.length <= 1) + return CommandUtil.getListMatchingLast(sender, args, SUB_COMMANDS.keySet()); + + final @Nullable Pair subCommand = resolveSubCommand(args[0]); + if (subCommand != null) + return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length)); + + return Collections.emptyList(); + } + + @Override + public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) { + if (!testPermission(sender)) return true; + + if (args.length == 0) { + sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); + return false; + } + + final @Nullable Pair subCommand = resolveSubCommand(args[0]); + + if (subCommand == null) { + sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); + return false; + } + + if (!testPermission(sender, subCommand.first())) return true; + + final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length); + return subCommand.second().execute(sender, subCommand.first(), choppedArgs); + } + + private static @Nullable Pair resolveSubCommand(String label) { + label = label.toLowerCase(Locale.ENGLISH); + @Nullable PlazmaSubCommand subCommand = SUB_COMMANDS.get(label); + + if (subCommand == null) { + final @Nullable String command = ALIASES.get(label); + if (command != null) { + label = command; + subCommand = SUB_COMMANDS.get(command); + } + } + + if (subCommand != null) + return Pair.of(label, subCommand); + + return null; + } + + private static boolean testPermission(final CommandSender sender, final String permission) { + if (sender.hasPermission("bukkit.command.plazma." + permission) || sender.hasPermission("bukkit.command.plazma")) + return true; + + sender.sendMessage(Bukkit.permissionMessage()); + return false; + } +} diff --git a/src/main/java/org/plazmamc/plazma/commands/PlazmaCommands.java b/src/main/java/org/plazmamc/plazma/commands/PlazmaCommands.java new file mode 100644 index 0000000000000000000000000000000000000000..4223faf9cdeb76cbafbb9b5b402ca3293c2afc1d --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/commands/PlazmaCommands.java @@ -0,0 +1,25 @@ +package org.plazmamc.plazma.commands; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import java.util.HashMap; +import java.util.Map; + +@DefaultQualifier(NonNull.class) +public final class PlazmaCommands { + + private PlazmaCommands() {} + + private static final Map COMMANDS = new HashMap<>(); + static { + COMMANDS.put("plazma", new PlazmaCommand("plazma")); + } + + public static void registerCommands(final MinecraftServer server) { + COMMANDS.forEach((s, command) -> server.server.getCommandMap().register(s, "Plazma", command)); + } + +} diff --git a/src/main/java/org/plazmamc/plazma/commands/subcommands/PlazmaSubCommand.java b/src/main/java/org/plazmamc/plazma/commands/subcommands/PlazmaSubCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..32f2f9c933069c32c321f71ec2c57e2261f7ad1e --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/commands/subcommands/PlazmaSubCommand.java @@ -0,0 +1,17 @@ +package org.plazmamc.plazma.commands.subcommands; + +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.framework.qual.DefaultQualifier; + +import java.util.Collections; +import java.util.List; + +@DefaultQualifier(NonNull.class) +public interface PlazmaSubCommand { + boolean execute(CommandSender sender, String subCommand, String[] args); + + default List tabComplete(final CommandSender sender, final String subCommand, final String[] args) { + return Collections.emptyList(); + } +} diff --git a/src/main/java/org/plazmamc/plazma/commands/subcommands/ReloadCommand.java b/src/main/java/org/plazmamc/plazma/commands/subcommands/ReloadCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..9b1964bf2c05e71cac633fd75f887a5e516623df --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/commands/subcommands/ReloadCommand.java @@ -0,0 +1,29 @@ +package org.plazmamc.plazma.commands.subcommands; + +import net.kyori.adventure.text.format.NamedTextColor; +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.craftbukkit.CraftServer; + +import static net.kyori.adventure.text.Component.text; + +public class ReloadCommand implements PlazmaSubCommand { + + @Override + public boolean execute(CommandSender sender, String subCommand, String[] args) { + this.doReload(sender); + return true; + } + + private void doReload(final CommandSender sender) { + Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", NamedTextColor.RED)); + Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", NamedTextColor.RED)); + + MinecraftServer server = ((CraftServer) sender.getServer()).getServer(); + server.plazmaConfigurations.reloadConfigurations(server); + server.server.reloadCount++; + + Command.broadcastCommandMessage(sender, text("Successfully reloaded Plazma configuration files.", NamedTextColor.GREEN)); + } +} diff --git a/src/main/java/org/plazmamc/plazma/commands/subcommands/VersionCommand.java b/src/main/java/org/plazmamc/plazma/commands/subcommands/VersionCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..85002144e0b350c4ae044e1a4a4c1734cc27c059 --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/commands/subcommands/VersionCommand.java @@ -0,0 +1,15 @@ +package org.plazmamc.plazma.commands.subcommands; + +import net.minecraft.server.MinecraftServer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class VersionCommand implements PlazmaSubCommand { + @Override + public boolean execute(CommandSender sender, String subCommand, String[] args) { + final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (ver != null) ver.execute(sender, "plazma", new String[0]); + return true; + } +} diff --git a/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..92524f24bdb56a861b06fffc900ff3c118bdb4ee --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/configurations/GlobalConfiguration.java @@ -0,0 +1,33 @@ +package org.plazmamc.plazma.configurations; + +import io.papermc.paper.configuration.Configuration; +import io.papermc.paper.configuration.ConfigurationPart; +import org.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"CanBeFinal", "FieldCanBeLocal", "FieldMayBeFinal", "InnerClassMayBeStatic"}) +public class GlobalConfiguration extends ConfigurationPart { + static final int CURRENT_VERSION = 1; + static final boolean DO_OPTIMIZE = PlazmaConfigurations.doOptimize(); + private static GlobalConfiguration instance; + + public static GlobalConfiguration get() { + return instance; + } + + static void set(GlobalConfiguration instance) { + GlobalConfiguration.instance = instance; + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public Misc misc; + public class Misc extends ConfigurationPart { + + } + + public Player player; + public class Player extends ConfigurationPart { + + } +} diff --git a/src/main/java/org/plazmamc/plazma/configurations/LevelConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/LevelConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..5e31a23ed4f31cc06562aa7814812242a04ee086 --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/configurations/LevelConfigurations.java @@ -0,0 +1,40 @@ +package org.plazmamc.plazma.configurations; + +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.spongepowered.configurate.objectmapping.meta.Setting; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal", "InnerClassMayBeStatic"}) +public class LevelConfigurations extends ConfigurationPart { + public static final int CURRENT_VERSION = 1; + private static final boolean DO_OPTIMIZE = PlazmaConfigurations.doOptimize(); + + private transient final ResourceLocation worldKey; + public LevelConfigurations(ResourceLocation worldKey) { + this.worldKey = worldKey; + } + + public boolean isDefault() { + return this.worldKey.equals(PaperConfigurations.WORLD_DEFAULTS_KEY); + } + + @Setting(Configuration.VERSION_FIELD) + public int version = CURRENT_VERSION; + + public GlobalConfiguration.Misc misc; + public class Misc extends ConfigurationPart { + + } + + public Structure structure; + public class Structure extends ConfigurationPart { + + public NetherPortal netherPortal; + public class NetherPortal extends ConfigurationPart { + + } + + } +} diff --git a/src/main/java/org/plazmamc/plazma/configurations/PlazmaConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/PlazmaConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..39db772735d57e12bbb26a6505ca7a5e360cdd78 --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/configurations/PlazmaConfigurations.java @@ -0,0 +1,309 @@ +package org.plazmamc.plazma.configurations; + +import com.google.common.collect.Table; +import com.mojang.logging.LogUtils; +import io.leangen.geantyref.TypeToken; +import io.papermc.paper.configuration.*; +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.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.*; +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.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.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.VisibleForTesting; +import org.slf4j.Logger; +import org.spongepowered.configurate.*; +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.File; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +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 PlazmaConfigurations extends Configurations { + + private static final Logger LOGGER = LogUtils.getLogger(); + static final String GLOBAL_CONFIGURATION_FILE_NAME= "plazma-global.yml"; + static final String LEVEL_DEFAULT_CONFIGURATION_FILE_NAME = "plazma-level-defaults.yml"; + static final String LEVEL_CONFIGURATION_FILE_NAME = "plazma-configuration.yml"; + static final boolean OPTIMIZE_CONFIG = !Boolean.getBoolean("Plazma.disableConfigOptimization"); + + private static final String HEADER_START = """ + # English + This is the %s configuration file for Plazma. + 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 Plazma, + join us in our Discord for Plazma, or check the GitHub Wiki pages. + + %s + + # 한국어 + 본 파일은 Plazma의 %s 구성 파일입니다. + 보시다시피, 굉장히 많은 설정이 있습니다. 몇몇 설정은 게임플레이에 영향을 줄 수 있으므로, + 주의해서 사용하시기 바라며, 각 설정이 어떠한 역할을 하는지 알고 사용하시기 바랍니다. + + 만약 구성에 관한 도움이 필요하거나 Plazma에 관련한 질문이 있으시다면, + Discord 또는 네이버 카페에 가입하거나, GitHub 위키 페이지를 참고하시기 바랍니다. + + %s + + Wiki: https://github.com/PlazmaMC/Plazma/wiki + Discord: https://discord.gg/MmfC52K8A8 + """; + + private static final Function LEVEL_SPECIFIC_HEADER = map -> String.format(""" + # English + This is a level specific configuration file for Plazma. + This file may start empty, but can be filled with settings to override ones in the %s/%s + + If you need help with the configuration or have any questions related to Plazma, + join us in our Discord for Plazma, or check the GitHub Wiki pages. + + + # 한국어 + 본 파일은 Plazma의 레벨별 구성 파일입니다. + 이 파일은 비어있을 수 있지만, %s/%s 파일의 설정을 덮어쓰기 위해 설정을 채울 수 있습니다. + + 만약 구성에 관한 도움이 필요하거나 Plazma에 관련한 질문이 있으시다면, + Discord 또는 네이버 카페에 가입하거나, GitHub 위키 페이지를 참고하시기 바랍니다. + + + Level: %s (%s) + + Wiki: https://github.com/PlazmaMC/Plazma/wiki + Discord: https://discord.gg/MmfC52K8A8 + """, PaperConfigurations.CONFIG_DIR, LEVEL_DEFAULT_CONFIGURATION_FILE_NAME, PaperConfigurations.CONFIG_DIR, LEVEL_DEFAULT_CONFIGURATION_FILE_NAME, + map.require(WORLD_NAME), map.require(WORLD_KEY)); + + private static final String GLOBAL_HEADER = String.format(HEADER_START, + "global", String.format(""" + The level configuration options are inside their respective level folder. + The files are named %s + """, LEVEL_CONFIGURATION_FILE_NAME), + "전역", String.format(""" + 레벨 구성 옵션은 각각의 레벨 폴더 안에 있으며, 파일 이름은 %s 입니다. + """, LEVEL_CONFIGURATION_FILE_NAME) + ); + + private static final String LEVEL_DEFAULTS_HEADER = String.format(HEADER_START, + "level defaults", """ + Configuration options here apply to all levels, unless you specify overrides inside + the level-specific config file inside each level folder. + """, + "기본 레벨", """ + 이 구성 파일의 설정은 각 레벨 폴더 안 구성 파일에서 덮어쓰기 되지 않는 한 모든 레벨에 적용됩니다. + """ + ); + + private static final List DEFAULTS_AWARE_TRANSFORMATIONS = Collections.emptyList(); + + public PlazmaConfigurations(final Path globalFolder) { + super(globalFolder, GlobalConfiguration.class, LevelConfigurations.class, GLOBAL_CONFIGURATION_FILE_NAME, LEVEL_DEFAULT_CONFIGURATION_FILE_NAME, LEVEL_CONFIGURATION_FILE_NAME); + } + + // Create Loader Builder + private static ConfigurationOptions defaultOptions(@NotNull ConfigurationOptions options) { + return options.serializers(builder -> builder + .register(MapSerializer.TYPE, new MapSerializer(false)) + .register(new EnumValueSerializer()) + .register(new ComponentSerializer()) + ); + } + + @Override + protected YamlConfigurationLoader.Builder createLoaderBuilder() { + return super.createLoaderBuilder().defaultOptions(PlazmaConfigurations::defaultOptions); + } + + // Create Global Object Mapper Factory Builder + private static ObjectMapper.Factory.Builder defaultGlobalFactoryBuilder(ObjectMapper.Factory.@NotNull Builder builder) { + return builder.addDiscoverer(InnerClassFieldDiscoverer.globalConfig()); + } + + @Override + protected ObjectMapper.Factory.Builder createGlobalObjectMapperFactoryBuilder() { + return defaultGlobalFactoryBuilder(super.createGlobalObjectMapperFactoryBuilder()); + } + + // Create Global Loader Builder + private static ConfigurationOptions defaultGlobalOptions(@NotNull ConfigurationOptions options) { + return options.header(GLOBAL_HEADER).serializers(builder -> builder.register(new PacketClassSerializer())); + } + + @Override + protected YamlConfigurationLoader.Builder createGlobalLoaderBuilder() { + return super.createGlobalLoaderBuilder().defaultOptions(PlazmaConfigurations::defaultGlobalOptions); + } + + // Initialize + @Override + public GlobalConfiguration initializeGlobalConfiguration() throws ConfigurateException { + GlobalConfiguration configuration = super.initializeGlobalConfiguration(); + GlobalConfiguration.set(configuration); + return configuration; + } + + @Override + protected ContextMap.Builder createDefaultContextMap() { + return super.createDefaultContextMap().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.plazmaLevelConfiguration(contextMap)); + } + + @Override + protected YamlConfigurationLoader.Builder createWorldConfigLoaderBuilder(final ContextMap contextMap) { + return super.createWorldConfigLoaderBuilder(contextMap).defaultOptions(options -> options + .header(contextMap.require(WORLD_NAME).equals(WORLD_DEFAULTS) ? LEVEL_DEFAULTS_HEADER : LEVEL_SPECIFIC_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(DoubleOrDefault.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>() {}, Registries.ENTITY_TYPE, true)) + .register(new RegistryValueSerializer<>(Item.class, Registries.ITEM, true)) + .register(new RegistryHolderSerializer<>(new TypeToken>() {}, Registries.CONFIGURED_FEATURE, false)) + .register(new RegistryHolderSerializer<>(Item.class, Registries.ITEM, true)) + ) + ); + } + + private void applyTransformations(final NodePath @NotNull [] paths, final ConfigurationNode node) throws ConfigurateException { + if (paths.length > 0) { + ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + + for (NodePath path : paths) + builder.addAction(path, TransformAction.remove()); + + builder.build().apply(node); + } + } + + @Override + protected void applyGlobalConfigTransformations(final ConfigurationNode node) throws ConfigurateException { + applyTransformations(RemovedConfigurations.REMOVED_GLOBAL_PATHS, node); + } + + @Override + protected void applyWorldConfigTransformations(final @NotNull ContextMap contextMap, final @NotNull ConfigurationNode node) throws ConfigurateException { + final ConfigurationNode version = node.node(Configuration.VERSION_FIELD); + final String world = contextMap.require(WORLD_NAME); + + if (version.virtual()) { + LOGGER.warn("The Plazma level configuration file for {} didn't have a version field, assuming latest", world); + version.raw(LevelConfigurations.CURRENT_VERSION); + } + + applyTransformations(RemovedConfigurations.REMOVED_LEVEL_PATHS, node); + } + + @Override + protected void applyDefaultsAwareWorldConfigTransformations(final ContextMap contextMap, final ConfigurationNode levelNode, final ConfigurationNode defaultsNode) throws ConfigurateException { + final ConfigurationTransformation.Builder builder = ConfigurationTransformation.builder(); + DEFAULTS_AWARE_TRANSFORMATIONS.forEach(transform -> transform.apply(builder, contextMap, defaultsNode)); + + ConfigurationTransformation transformation; + try { + transformation = builder.build(); + } catch (IllegalArgumentException ignored) { + return; + } + transformation.apply(levelNode); + } + + @Override + public LevelConfigurations createWorldConfig(final @NotNull ContextMap contextMap) { + final String levelName = contextMap.require(WORLD_NAME); + try { + return super.createWorldConfig(contextMap); + } catch (IOException exception) { + throw new RuntimeException(String.format("Could not create Plazma level configuration for %s", levelName), exception); + } + } + + @Override + protected boolean isConfigType(Type type) { + return ConfigurationPart.class.isAssignableFrom(erase(type)); + } + + @Override + protected int getWorldConfigurationCurrentVersion() { + return LevelConfigurations.CURRENT_VERSION; + } + + @VisibleForTesting + static @NotNull ConfigurationNode createForTesting() { + ObjectMapper.Factory factory = defaultGlobalFactoryBuilder(ObjectMapper.factoryBuilder()).build(); + ConfigurationOptions options = defaultGlobalOptions(defaultOptions(ConfigurationOptions.defaults())) + .serializers(builder -> builder.register(type -> ConfigurationPart.class.isAssignableFrom(erase(type)), factory.asTypeSerializer())); + return BasicConfigurationNode.root(options); + } + + @Contract("_ -> new") + public static @NotNull PlazmaConfigurations setup(final Path configurationDir) throws Exception { + try { + PaperConfigurations.createDirectoriesSymlinkAware(configurationDir); + return new PlazmaConfigurations(configurationDir); + } catch (final IOException e) { + throw new RuntimeException("Could not setup Plazma configuration files", e); + } + } + + public void reloadConfigurations(MinecraftServer server) { + try { + this.initializeGlobalConfiguration(reloader(this.globalConfigClass, GlobalConfiguration.get())); + this.initializeWorldDefaultsConfiguration(); + for (ServerLevel level : server.getAllLevels()) + this.createWorldConfig(PaperConfigurations.createWorldContextMap(level), reloader(this.worldConfigClass, level.plazmaLevelConfiguration())); + } catch (Exception e) { + throw new RuntimeException("Could not reload Plazma configuration files", e); + } + } + + public static boolean optimizeConfig() { + return OPTIMIZE_CONFIG; + } + +} diff --git a/src/main/java/org/plazmamc/plazma/configurations/RemovedConfigurations.java b/src/main/java/org/plazmamc/plazma/configurations/RemovedConfigurations.java new file mode 100644 index 0000000000000000000000000000000000000000..469100cd86e6742eeebad22923097782dc86bf37 --- /dev/null +++ b/src/main/java/org/plazmamc/plazma/configurations/RemovedConfigurations.java @@ -0,0 +1,11 @@ +package org.plazmamc.plazma.configurations; + +import org.spongepowered.configurate.NodePath; + +interface RemovedConfigurations { + + NodePath[] REMOVED_GLOBAL_PATHS = {}; + + NodePath[] REMOVED_LEVEL_PATHS = {}; + +} diff --git a/src/test/java/org/bukkit/support/AbstractTestingBase.java b/src/test/java/org/bukkit/support/AbstractTestingBase.java index 8174b6bb78553cbe124b499ffa235368179d1564..a5f8a128b27906daba5aeb293851bb03402ca9c2 100644 --- a/src/test/java/org/bukkit/support/AbstractTestingBase.java +++ b/src/test/java/org/bukkit/support/AbstractTestingBase.java @@ -64,6 +64,7 @@ public abstract class AbstractTestingBase { DummyEnchantments.setup(); io.papermc.paper.configuration.GlobalConfigTestingBase.setupGlobalConfigForTest(); // Paper + org.plazmamc.plazma.configurations.GlobalConfigurationTestingBase.setupGlobalConfigurationForTest(); // Plazma CraftRegistry.setMinecraftRegistry(REGISTRY_CUSTOM); diff --git a/src/test/java/org/plazmamc/plazma/configurations/GlobalConfigurationTestingBase.java b/src/test/java/org/plazmamc/plazma/configurations/GlobalConfigurationTestingBase.java new file mode 100644 index 0000000000000000000000000000000000000000..2b9a17a2e04f7fb6b801920ed04133db9478f984 --- /dev/null +++ b/src/test/java/org/plazmamc/plazma/configurations/GlobalConfigurationTestingBase.java @@ -0,0 +1,18 @@ +package org.plazmamc.plazma.configurations; + +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; + +public class GlobalConfigurationTestingBase { + public static void setupGlobalConfigurationForTest() { + if (GlobalConfiguration.get() == null) { + ConfigurationNode node = PlazmaConfigurations.createForTesting(); + try { + GlobalConfiguration globalConfiguration = node.require(GlobalConfiguration.class); + GlobalConfiguration.set(globalConfiguration); + } catch (SerializationException e) { + throw new RuntimeException(e); + } + } + } +}