From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: William Blake Galbreath <blake.galbreath@gmail.com>
Date: Thu, 9 May 2019 18:09:43 -0500
Subject: [PATCH] Purpur config files


diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
index b480bd3044370b8eb733166f0c4b737344475993..4d8740678049aa749b42618470e9cc838555528d 100644
--- a/src/main/java/com/destroystokyo/paper/Metrics.java
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
@@ -593,7 +593,7 @@ public class Metrics {
             boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
             // Only start Metrics, if it's enabled in the config
             if (config.getBoolean("enabled", true)) {
-                Metrics metrics = new Metrics("Airplane", serverUUID, logFailedRequests, Bukkit.getLogger()); // Tuinity - we have our own bstats page // Airplane
+                Metrics metrics = new Metrics("Purpur", serverUUID, logFailedRequests, Bukkit.getLogger()); // Purpur
 
                 metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
                     String minecraftVersion = Bukkit.getVersion();
@@ -602,8 +602,8 @@ public class Metrics {
                 }));
 
                 metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
-                metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() || PaperConfig.isProxyOnlineMode() ? "online" : "offline"));
-                metrics.addCustomChart(new Metrics.SimplePie("airplane_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); // Tuinity - we have our own bstats page // Airplane
+                metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : (PaperConfig.isProxyOnlineMode() ? "bungee" : "offline"))); // Purpur
+                metrics.addCustomChart(new Metrics.SimplePie("purpur_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); // Purpur
 
                 metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
                     Map<String, Map<String, Integer>> map = new HashMap<>();
diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java
index efc1e42d606e1c9feb1a4871c0714933ae92a1b2..a0ed8ed1d6b89a4f10dff645e09eaff303fb3f8a 100644
--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java
+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java
@@ -118,6 +118,11 @@ public class PaperConfig {
             }
         }
 
+        // Purpur start - public save method for config migration
+        saveConfig();
+    }
+    public static void saveConfig() {
+        // Purpur end
         try {
             config.save(CONFIG_FILE);
         } catch (IOException ex) {
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 24a46ad36613faa5f5a1a12b70f7af886e1608ae..28d47ef97939309ce26b6e4cae14925b510755fd 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -212,6 +212,15 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
             return false;
         }
         com.destroystokyo.paper.PaperConfig.registerCommands();
+        // Purpur start
+        try {
+            net.pl3x.purpur.PurpurConfig.init((java.io.File) options.valueOf("purpur-settings"));
+        } catch (Exception e) {
+            DedicatedServer.LOGGER.error("Unable to load server configuration", e);
+            return false;
+        }
+        net.pl3x.purpur.PurpurConfig.registerCommands();
+        // Purpur end
         com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now
         // Paper end
         com.tuinity.tuinity.config.TuinityConfig.init((java.io.File) options.valueOf("tuinity-settings")); // Tuinity - Server Config
diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java
index f0420b87025a090a659542a00614e805bf850fd2..89b892c23a396d94c11b5d6b7ffe79e892297181 100644
--- a/src/main/java/net/minecraft/world/level/World.java
+++ b/src/main/java/net/minecraft/world/level/World.java
@@ -157,6 +157,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
     public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray
 
     public final com.tuinity.tuinity.config.TuinityConfig.WorldConfig tuinityConfig; // Tuinity - Server Config
+    public final net.pl3x.purpur.PurpurWorldConfig purpurConfig; // Purpur
 
     public final co.aikar.timings.WorldTimingsHandler timings; // Paper
     public static BlockPosition lastPhysicsProblem; // Spigot
@@ -249,6 +250,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
         this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(((net.minecraft.world.level.storage.WorldDataServer) worlddatamutable).getName(), this.spigotConfig); // Paper
         this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this, executor) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray
         this.tuinityConfig = new com.tuinity.tuinity.config.TuinityConfig.WorldConfig(((net.minecraft.world.level.storage.WorldDataServer)worlddatamutable).getName()); // Tuinity - Server Config
+        this.purpurConfig = new net.pl3x.purpur.PurpurWorldConfig((((net.minecraft.world.level.storage.WorldDataServer)worlddatamutable).getName())); // Purpur
         this.generator = gen;
         this.world = new CraftWorld((WorldServer) this, gen, env);
         this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit
diff --git a/src/main/java/net/pl3x/purpur/PurpurConfig.java b/src/main/java/net/pl3x/purpur/PurpurConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..19541e7071c05dce30c384093c8eae5beff3e515
--- /dev/null
+++ b/src/main/java/net/pl3x/purpur/PurpurConfig.java
@@ -0,0 +1,130 @@
+package net.pl3x.purpur;
+
+import com.google.common.base.Throwables;
+import net.minecraft.server.MinecraftServer;
+import net.pl3x.purpur.command.PurpurCommand;
+import org.bukkit.Bukkit;
+import org.bukkit.command.Command;
+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.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+public class PurpurConfig {
+    private static final String HEADER = "This is the main configuration file for Purpur.\n"
+            + "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
+            + "with caution, and make sure you know what each option does before configuring.\n"
+            + "\n"
+            + "If you need help with the configuration or have any questions related to Purpur,\n"
+            + "join us in our Discord guild.\n"
+            + "\n"
+            + "Website: https://github.com/pl3xgaming/Purpur \n"
+            + "Wiki: https://github.com/pl3xgaming/Purpur/wiki \n";
+    private static File CONFIG_FILE;
+    public static YamlConfiguration config;
+
+    private static Map<String, Command> commands;
+
+    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 purpur.yml, please correct your syntax errors", ex);
+            throw Throwables.propagate(ex);
+        }
+        config.options().header(HEADER);
+        config.options().copyDefaults(true);
+        verbose = getBoolean("verbose", false);
+
+        commands = new HashMap<>();
+        commands.put("purpur", new PurpurCommand("purpur"));
+
+        version = getInt("config-version", 11);
+        set("config-version", 11);
+
+        readConfig(PurpurConfig.class, null);
+    }
+
+    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);
+    }
+
+    public static void registerCommands() {
+        for (Map.Entry<String, Command> entry : commands.entrySet()) {
+            MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Purpur", entry.getValue());
+        }
+    }
+
+    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 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));
+    }
+
+    private static String getString(String path, String def) {
+        config.addDefault(path, def);
+        return config.getString(path, config.getString(path));
+    }
+}
diff --git a/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..361f7857e461578e90cb71e15027dadaf794cb69
--- /dev/null
+++ b/src/main/java/net/pl3x/purpur/PurpurWorldConfig.java
@@ -0,0 +1,59 @@
+package net.pl3x.purpur;
+
+import org.bukkit.configuration.ConfigurationSection;
+import java.util.List;
+import static net.pl3x.purpur.PurpurConfig.log;
+
+public class PurpurWorldConfig {
+
+    private final String worldName;
+
+    public PurpurWorldConfig(String worldName) {
+        this.worldName = worldName;
+        init();
+    }
+
+    public void init() {
+        log("-------- World Settings For [" + worldName + "] --------");
+        PurpurConfig.readConfig(PurpurWorldConfig.class, this);
+    }
+
+    private void set(String path, Object val) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, val);
+        PurpurConfig.config.set("world-settings.default." + path, val);
+        if (PurpurConfig.config.get("world-settings." + worldName + "." + path) != null) {
+            PurpurConfig.config.addDefault("world-settings." + worldName + "." + path, val);
+            PurpurConfig.config.set("world-settings." + worldName + "." + path, val);
+        }
+    }
+
+    private ConfigurationSection getConfigurationSection(String path) {
+        ConfigurationSection section = PurpurConfig.config.getConfigurationSection("world-settings." + worldName + "." + path);
+        return section != null ? section : PurpurConfig.config.getConfigurationSection("world-settings.default." + path);
+    }
+
+    private boolean getBoolean(String path, boolean def) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, def);
+        return PurpurConfig.config.getBoolean("world-settings." + worldName + "." + path, PurpurConfig.config.getBoolean("world-settings.default." + path));
+    }
+
+    private double getDouble(String path, double def) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, def);
+        return PurpurConfig.config.getDouble("world-settings." + worldName + "." + path, PurpurConfig.config.getDouble("world-settings.default." + path));
+    }
+
+    private int getInt(String path, int def) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, def);
+        return PurpurConfig.config.getInt("world-settings." + worldName + "." + path, PurpurConfig.config.getInt("world-settings.default." + path));
+    }
+
+    private <T> List<?> getList(String path, T def) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, def);
+        return PurpurConfig.config.getList("world-settings." + worldName + "." + path, PurpurConfig.config.getList("world-settings.default." + path));
+    }
+
+    private String getString(String path, String def) {
+        PurpurConfig.config.addDefault("world-settings.default." + path, def);
+        return PurpurConfig.config.getString("world-settings." + worldName + "." + path, PurpurConfig.config.getString("world-settings.default." + path));
+    }
+}
diff --git a/src/main/java/net/pl3x/purpur/command/PurpurCommand.java b/src/main/java/net/pl3x/purpur/command/PurpurCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..536955124afaec5c8a070249c7432cb99bf43d67
--- /dev/null
+++ b/src/main/java/net/pl3x/purpur/command/PurpurCommand.java
@@ -0,0 +1,65 @@
+package net.pl3x.purpur.command;
+
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.WorldServer;
+import net.pl3x.purpur.PurpurConfig;
+import org.bukkit.ChatColor;
+import org.bukkit.Location;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class PurpurCommand extends Command {
+    public PurpurCommand(String name) {
+        super(name);
+        this.description = "Purpur related commands";
+        this.usageMessage = "/purpur [reload | version]";
+        this.setPermission("bukkit.command.purpur");
+    }
+
+    @Override
+    public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+        if (args.length == 1) {
+            return Stream.of("reload", "version")
+                    .filter(arg -> arg.startsWith(args[0].toLowerCase()))
+                    .collect(Collectors.toList());
+        }
+        return Collections.emptyList();
+    }
+
+    @Override
+    public boolean execute(CommandSender sender, String commandLabel, String[] args) {
+        if (!testPermission(sender)) return true;
+
+        if (args.length != 1) {
+            sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage);
+            return false;
+        }
+
+        if (args[0].equalsIgnoreCase("reload")) {
+            Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues.");
+            Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server.");
+
+            MinecraftServer console = MinecraftServer.getServer();
+            PurpurConfig.init((File) console.options.valueOf("purpur-settings"));
+            for (WorldServer world : console.getWorlds()) {
+                world.purpurConfig.init();
+            }
+            console.server.reloadCount++;
+
+            Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Purpur config reload complete.");
+        } else if (args[0].equalsIgnoreCase("version")) {
+            Command verCmd = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
+            if (verCmd != null) {
+                return verCmd.execute(sender, commandLabel, new String[0]);
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index f9913d7bd66935f975b756f31e26153ec160b3b1..3e0b92259ad00541a8da595a918275c1a5b2bc39 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -865,6 +865,7 @@ public final class CraftServer implements Server {
         org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot
         com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); // Paper
         com.tuinity.tuinity.config.TuinityConfig.init((File) console.options.valueOf("tuinity-settings")); // Tuinity - Server Config
+        net.pl3x.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur
         for (WorldServer world : console.getWorlds()) {
             world.worldDataServer.setDifficulty(config.difficulty);
             world.setSpawnFlags(config.spawnMonsters, config.spawnAnimals);
@@ -900,6 +901,7 @@ public final class CraftServer implements Server {
             world.spigotConfig.init(); // Spigot
             world.paperConfig.init(); // Paper
             world.tuinityConfig.init(); // Tuinity - Server Config
+            world.purpurConfig.init(); // Purpur
         }
 
         Plugin[] pluginClone = pluginManager.getPlugins().clone(); // Paper
@@ -918,6 +920,7 @@ public final class CraftServer implements Server {
         reloadData();
         org.spigotmc.SpigotConfig.registerCommands(); // Spigot
         com.destroystokyo.paper.PaperConfig.registerCommands(); // Paper
+        net.pl3x.purpur.PurpurConfig.registerCommands(); // Purpur
         overrideAllCommandBlockCommands = commandsConfiguration.getStringList("command-block-overrides").contains("*");
         ignoreVanillaPermissions = commandsConfiguration.getBoolean("ignore-vanilla-permissions");
 
@@ -2362,6 +2365,18 @@ public final class CraftServer implements Server {
         }
         // Tuinity end - add config to timings report
 
+        // Purpur start
+        @Override
+        public YamlConfiguration getPurpurConfig() {
+            return net.pl3x.purpur.PurpurConfig.config;
+        }
+
+        @Override
+        public java.util.Properties getServerProperties() {
+            return getProperties().properties;
+        }
+        // Purpur end
+
         @Override
         public void restart() {
             org.spigotmc.RestartCommand.restart();
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
index 145748b5ca095cd555fd1f085a596bf0f58fea13..adf182b651ba8c7105483a4b18d1adfea37f91b9 100644
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -148,6 +148,14 @@ public class Main {
                         .describedAs("Yml file");
                 /* Conctete End - Server Config */
 
+                // Purpur Start
+                acceptsAll(asList("purpur", "purpur-settings"), "File for purpur settings")
+                        .withRequiredArg()
+                        .ofType(File.class)
+                        .defaultsTo(new File("purpur.yml"))
+                        .describedAs("Yml file");
+                // Purpur end
+
                 // Paper start
                 acceptsAll(asList("server-name"), "Name of the server")
                         .withRequiredArg()
