From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Allink Date: Sun, 10 Jul 2022 10:15:20 +0100 Subject: [PATCH] Add Scissors configuration file & command diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index 06bff37e4c1fddd3be6343049a66787c63fb420c..1c1cb20190e8edb7f55a60fc662c28e204690223 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -25,6 +25,7 @@ package co.aikar.timings; import com.google.common.collect.Sets; import io.papermc.paper.adventure.PaperAdventure; +import me.totalfreedom.scissors.ScissorsConfig; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; @@ -241,7 +242,8 @@ 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)) + pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), + pair("scissors", mapAsJSON(ScissorsConfig.config, null)) // Scissors )); new TimingsExport(listeners, parent, history).start(); diff --git a/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java b/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..797677d892d83cf54d9a60af1e277b67ed3d6e95 --- /dev/null +++ b/src/main/java/me/totalfreedom/scissors/ScissorsCommand.java @@ -0,0 +1,150 @@ +package me.totalfreedom.scissors; + +import com.google.common.base.Functions; +import com.google.common.base.Joiner; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +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.*; +import java.util.stream.Collectors; + +public class ScissorsCommand extends Command +{ + + private static final String BASE_PERM = "bukkit.command.scissors."; + private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("reload", "version").build(); + + public ScissorsCommand(String name) + { + super(name); + this.description = "Scissors related commands"; + this.usageMessage = "/scissors [" + Joiner.on(" | ").join(SUBCOMMANDS) + "]"; + this.setPermission("bukkit.command.scissors;" + Joiner.on(';').join(SUBCOMMANDS.stream().map(s -> BASE_PERM + s).collect(Collectors.toSet()))); + } + + private static boolean testPermission(CommandSender commandSender, String permission) + { + if (commandSender.hasPermission(BASE_PERM + permission) || commandSender.hasPermission("bukkit.command.scissors")) + return true; + commandSender.sendMessage(Bukkit.getPermissionMessage()); // Sorry, kashike + return false; + } + + // Code from Mojang - copyright them + public static List getListMatchingLast(CommandSender sender, String[] args, String... matches) + { + return getListMatchingLast(sender, args, Arrays.asList(matches)); + } + + public static boolean matches(String s, String s1) + { + return s1.regionMatches(true, 0, s, 0, s.length()); + } + + public static List getListMatchingLast(CommandSender sender, String[] strings, Collection collection) + { + String last = strings[strings.length - 1]; + ArrayList results = Lists.newArrayList(); + + if (!collection.isEmpty()) + { + Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator(); + + while (iterator.hasNext()) + { + String s1 = (String) iterator.next(); + + if (matches(last, s1) && (sender.hasPermission(BASE_PERM + s1) || sender.hasPermission("bukkit.command.scissors"))) + { + results.add(s1); + } + } + + if (results.isEmpty()) + { + iterator = collection.iterator(); + + while (iterator.hasNext()) + { + Object object = iterator.next(); + + if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) + { + results.add(String.valueOf(object)); + } + } + } + } + + return results; + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException + { + if (args.length <= 1) + return getListMatchingLast(sender, args, SUBCOMMANDS); + + return Collections.emptyList(); + } + // end copy stuff + + @Override + public boolean execute(CommandSender sender, String commandLabel, String[] args) + { + if (!testPermission(sender)) return true; + + if (args.length == 0) + { + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) + { + if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true; + } + switch (args[0].toLowerCase(Locale.ENGLISH)) + { + case "reload": + doReload(sender); + break; + case "ver": + if (!testPermission(sender, "version")) + break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set) + case "version": + Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); + if (ver != null) + { + ver.execute(sender, commandLabel, new String[0]); + break; + } + // else - fall through to default + default: + sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); + return false; + } + + return true; + } + + private void doReload(CommandSender sender) + { + 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(); + ScissorsConfig.init((File) console.options.valueOf("scissors-settings")); + console.server.reloadCount++; + + Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Scissors config reload complete."); + } +} diff --git a/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java b/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..e08f502fc7165f9f466217910210edb5059d39a8 --- /dev/null +++ b/src/main/java/me/totalfreedom/scissors/ScissorsConfig.java @@ -0,0 +1,199 @@ +package me.totalfreedom.scissors; + + +import com.google.common.base.Throwables; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServer; +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.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.regex.Pattern; + +// TODO - Migrate to new format +public class ScissorsConfig +{ + + private static final String HEADER = """ + This is the main configuration file for Scissors. + As you can see, there's tons 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 Scissors, + join us in our Discord. + + Discord: https://discord.com/invite/mtVQcHn58h + Website: https://scissors.gg/\s + Docs: https://scissors.gg/javadoc/1.19/\s + """; + private static final Pattern SPACE = Pattern.compile(" "); + private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]"); + /*========================================================================*/ + public static YamlConfiguration config; + static int version; + /*========================================================================*/ + static Map commands; + private static File CONFIG_FILE; + + public static void init(File configFile) + { + final File configFolder = (File) DedicatedServer.getServer().options.valueOf("scissors-settings" + "-directory"); + final Path configFolderPath = configFolder.toPath(); + final Path oldConfigFilePath = configFile.toPath(); + final Path newConfigFilePath = configFolderPath.resolve(configFile.toPath()); + + if (configFile.exists()) + { + try + { + Files.move(oldConfigFilePath, newConfigFilePath); + } + catch (IOException e) + { + throw new RuntimeException("Error migrating configuration file to new directory!", e); + } + } + + CONFIG_FILE = newConfigFilePath.toFile(); + config = new YamlConfiguration(); + try + { + config.load(CONFIG_FILE); + } + catch (IOException ex) + { + } + catch (InvalidConfigurationException ex) + { + Bukkit.getLogger().log(Level.SEVERE, "Could not load scissors.yml, please correct your syntax errors", ex); + throw Throwables.propagate(ex); + } + + commands = new HashMap<>(); + commands.put("scissors", new ScissorsCommand("scissors")); + + config.options().header(HEADER); + config.options().copyDefaults(true); + + version = getInt("config-version", 1); + set("config-version", 1); + readConfig(ScissorsConfig.class, null); + } + + protected static void logError(String s) + { + Bukkit.getLogger().severe(s); + } + + protected static void fatal(String s) + { + throw new RuntimeException("Fatal scissors.yml config error: " + s); + } + + public static void registerCommands() + { + for (Map.Entry entry : commands.entrySet()) + { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Scissors", 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); + } + } + } + } + saveConfig(); + } + + static void saveConfig() + { + try + { + config.save(CONFIG_FILE); + } + catch (IOException ex) + { + Bukkit.getLogger().log(Level.SEVERE, "Could not save " + CONFIG_FILE, ex); + } + } + + public static boolean runCommandsInBooks = false; + + private static void runCommandsInBooks() { + runCommandsInBooks = getBoolean("runCommandsInBooks", false); + } + + private static void set(String path, Object 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 float getFloat(String path, float def) + { + // TODO: Figure out why getFloat() always returns the default value. + return (float) getDouble(path, def); + } + + private static int getInt(String path, int def) + { + config.addDefault(path, def); + return config.getInt(path, config.getInt(path)); + } + + private static 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/minecraft/server/Main.java b/src/main/java/net/minecraft/server/Main.java index 31faf2d6492696f7d0c99a48edbc0d6f15db1209..d6d3e802648eaf001f6e8143a361053ab41c5291 100644 --- a/src/main/java/net/minecraft/server/Main.java +++ b/src/main/java/net/minecraft/server/Main.java @@ -121,6 +121,7 @@ public class Main { // Paper start - load config files for access below if needed org.bukkit.configuration.file.YamlConfiguration bukkitConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("bukkit-settings")); org.bukkit.configuration.file.YamlConfiguration spigotConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("spigot-settings")); + org.bukkit.configuration.file.YamlConfiguration scissorsConfiguration = io.papermc.paper.configuration.PaperConfigurations.loadLegacyConfigFile((File) optionset.valueOf("scissors-settings")); // Scissors - TODO Change this // Paper end Path path1 = Paths.get("eula.txt"); diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index 51b3db0b6c2cede95b584268e035c0fb36d38094..f8e49559657c1a2788a3aa8a15df6da7491c3fda 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -221,7 +221,15 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface com.destroystokyo.paper.VersionHistoryManager.INSTANCE.getClass(); // load version history now io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider // Paper end - + // Scissors start + try { + me.totalfreedom.scissors.ScissorsConfig.init((java.io.File) options.valueOf("scissors-settings")); + } catch (Exception e) { + DedicatedServer.LOGGER.error("Unable to load server configuration", e); + return false; + } + me.totalfreedom.scissors.ScissorsConfig.registerCommands(); + // Scissors end this.setPvpAllowed(dedicatedserverproperties.pvp); this.setFlightAllowed(dedicatedserverproperties.allowFlight); this.setMotd(dedicatedserverproperties.motd); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 653c34ae4a5f55773f0c4af54c8cbb426ddb7d7b..9a2ed55bc6837875adfbf623b1cabf605801d5b4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -978,6 +978,8 @@ public final class CraftServer implements Server { } org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); // Spigot + me.totalfreedom.scissors.ScissorsConfig.init(((File) console.options.valueOf("scissors-settings"))); // Scissors + this.console.paperConfigurations.reloadConfigs(this.console); for (ServerLevel world : this.console.getAllLevels()) { // world.serverLevelData.setDifficulty(config.difficulty); // Paper - per level difficulty @@ -1009,6 +1011,7 @@ public final class CraftServer implements Server { this.reloadData(); org.spigotmc.SpigotConfig.registerCommands(); // Spigot io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper + me.totalfreedom.scissors.ScissorsConfig.registerCommands(); // Scissors this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*"); this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions"); @@ -2760,6 +2763,12 @@ public final class CraftServer implements Server { return CraftServer.this.console.paperConfigurations.createLegacyObject(CraftServer.this.console); } + @Override + public YamlConfiguration getScissorsConfig() + { + return me.totalfreedom.scissors.ScissorsConfig.config; + } + @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 21db4153a9eb5e52ff357b4146ae4302029d5cd5..f5b5eb6686684791c281604bd308107427861af2 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -173,6 +173,22 @@ public class Main { .defaultsTo("Unknown Server") .describedAs("Name"); // Paper end + + // Scissors start + acceptsAll(asList("scissors-dir", "scissors-settings-directory"), "Directory for Scissors settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File(io.papermc.paper.configuration.PaperConfigurations.CONFIG_DIR)) + .describedAs("Config directory"); + // Scissors end + + // Scissors start + acceptsAll(asList("scissors", "scissors-settings"), "File for scissors settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("scissors.yml")) + .describedAs("Yml file"); + // Scissors end } };