From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: tr7zw Date: Wed, 5 Aug 2020 08:05:10 -0500 Subject: [PATCH] Add config Yatopia command and basic settings diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index b67f05f290db653cf75a89e85bd3decb5044a1e0..11758b6b158d971b9beb1b609d7513dab46be525 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -232,6 +232,7 @@ public class TimingsExport extends Thread { pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)), // Tuinity - add config to timings report pair("tuinity", mapAsJSON(Bukkit.spigot().getTuinityConfig(), null)) // Tuinity - add config to timings report , pair("purpur", mapAsJSON(Bukkit.spigot().getPurpurConfig(), null)) // Purpur - add config to timings report + , pair("yatopia", mapAsJSON(Bukkit.spigot().getYatopiaConfig(), null)) // Yatopia - add config to timings report )); new TimingsExport(listeners, parent, history).start(); diff --git a/src/main/java/dev/tr7zw/yatopia/YatopiaCommand.java b/src/main/java/dev/tr7zw/yatopia/YatopiaCommand.java new file mode 100644 index 0000000000000000000000000000000000000000..0ada1657122fd973bbb8ec8b7c7be83de0bd4a00 --- /dev/null +++ b/src/main/java/dev/tr7zw/yatopia/YatopiaCommand.java @@ -0,0 +1,141 @@ +package dev.tr7zw.yatopia; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.function.Function; +import net.minecraft.server.MinecraftKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.World; +import net.minecraft.server.WorldDataServer; +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.spigotmc.TicksPerSecondCommand; + +public class YatopiaCommand extends Command { + + public YatopiaCommand(String name) { + super(name); + this.description = "Yatopia related commands"; + this.usageMessage = "/yatopia [help | reload | info | version]"; + this.setPermission("bukkit.command.yatopia"); + } + + @Override + public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { + if (args.length <= 1) { + return getListMatchingLast(args, "help", "info", "reload", "version"); + } + return Collections.emptyList(); + } + + // Code from Mojang - copyright them + public static List getListMatchingLast(String[] args, String... matches) { + return getListMatchingLast(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(String[] strings, Collection collection) { + String last = strings[strings.length - 1]; + List results = Lists.newArrayList(); + + if (!collection.isEmpty()) { + Function toStringFunction = (o) -> { + Preconditions.checkNotNull(o); + return o.toString(); + }; + for (Object c : collection) { + String value = toStringFunction.apply(c); + if (matches(last, value)) { + results.add(value); + } + } + + if (results.isEmpty()) { + for (Object c : collection) { + if (c instanceof MinecraftKey) { + MinecraftKey key = (MinecraftKey) c; + if (matches(last, key.getKey())) { + results.add(key.toString()); + } + } + } + } + } + + return results; + } + // 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; + } + + switch (args[0].toLowerCase(Locale.ENGLISH)) { + case "info": + doInfo(sender); + break; + case "reload": + doReload(sender); + break; + case "ver": + case "version": + Command ver = org.bukkit.Bukkit.getServer().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 doInfo(CommandSender sender) { + broadcastMessage(sender, ChatColor.GOLD + "Last tick took " + TicksPerSecondCommand.format(Bukkit.getLastTickMs())); + for (World world : MinecraftServer.getServer().getWorlds()) { + String name = ((WorldDataServer) world.worldData).getName(); + broadcastMessage(sender, ChatColor.GOLD + "Entities in world " + + ChatColor.YELLOW + "\"" + name + "\"" + + ChatColor.GOLD + ": " + ChatColor.YELLOW + world.getWorld().getEntityCount()); + broadcastMessage(sender, ChatColor.GOLD + "Chunks loaded in world " + + ChatColor.YELLOW + "\"" + name + "\"" + + ChatColor.GOLD + ": " + ChatColor.YELLOW + world.getWorld().getChunkCount()); + } + } + + private void doReload(CommandSender sender) { + broadcastMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); + broadcastMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); + + MinecraftServer console = MinecraftServer.getServer(); + dev.tr7zw.yatopia.YatopiaConfig.init((File) console.options.valueOf("yatopia-settings")); + console.server.reloadCount++; + + broadcastMessage(sender, ChatColor.GREEN + "Yatopia config reload complete."); + } + + // utility method + private void broadcastMessage(CommandSender sender, String message) { + Command.broadcastCommandMessage(sender, message); + } +} diff --git a/src/main/java/dev/tr7zw/yatopia/YatopiaConfig.java b/src/main/java/dev/tr7zw/yatopia/YatopiaConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..b67a82569747fb8f4a349b7f81fe502cc4e2a792 --- /dev/null +++ b/src/main/java/dev/tr7zw/yatopia/YatopiaConfig.java @@ -0,0 +1,213 @@ +package dev.tr7zw.yatopia; + +import com.google.common.base.Throwables; +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.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.regex.Pattern; +import net.minecraft.server.MinecraftServer; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +public class YatopiaConfig { + + public static File CONFIG_FILE; + private static final String HEADER = "This is the main configuration file for Yatopia.\n" + + "Yatopia contains many breaking changes and settings, so know what you are doing!\n" + + "You have been warned!\n"; + /*========================================================================*/ + public static YamlConfiguration config; + public static int version; // since we're remapping sidestreams' configs we need this public + static Map commands; + public static boolean verbose; // since we're remapping sidestreams' configs we need this public + private static boolean fatalError; + /*========================================================================*/ + private static boolean metricsStarted; + + public static void init(File configFile) { + CONFIG_FILE = configFile; + config = new YamlConfiguration(); + try { + config.load(CONFIG_FILE); + } catch (IOException ex) { + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Could not load yatopia.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("yatopia", new YatopiaCommand("yatopia")); + + version = getInt("config-version", 1); + set("config-version", 1); + removeLeftovers(); + readConfig(YatopiaConfig.class, null); + } + + private static void removeLeftovers() { + // this method is only to remove non-used values in the config + + // leftover from akarin / rainforest + if (config.get("world-settings") != null) { + set("world-settings", null); + } + // unused settings getting removed + if (config.get("settings.disablePlayerOutOfWorldBorderCheck") != null) { + set("settings.disablePlayerOutOfWorldBorderCheck", null); + } + if (config.get("settings.disableEntityCollisions") != null) { + set("settings.disableEntityCollisions", null); + } + if (config.get("settings.onlyPlayerCollisions") != null) { + set("settings.onlyPlayerCollisions", null); + } + if (config.get("settings.disableEntityCollisionboxes") != null) { + set("settings.disableEntityCollisionboxes", null); + } + } + + protected static void logError(String s) { + Bukkit.getLogger().severe(s); + } + + protected static void fatal(String s) { + fatalError = true; + throw new RuntimeException("Fatal yatopia.yml config error: " + s); + } + + protected static void log(String s) { + if (verbose) { + Bukkit.getLogger().info(s); + } + } + + public static void registerCommands() { + for (Map.Entry entry : commands.entrySet()) { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Yatopia", 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 final Pattern SPACE = Pattern.compile(" "); + private static final Pattern NOT_NUMERIC = Pattern.compile("[^-\\d.]"); + + public static int getSeconds(String str) { + str = SPACE.matcher(str).replaceAll(""); + final char unit = str.charAt(str.length() - 1); + str = NOT_NUMERIC.matcher(str).replaceAll(""); + double num; + try { + num = Double.parseDouble(str); + } catch (Exception e) { + num = 0D; + } + switch (unit) { + case 'd': + num *= (double) 60 * 60 * 24; + break; + case 'h': + num *= (double) 60 * 60; + break; + case 'm': + num *= 60; + break; + default: + case 's': + break; + } + return (int) num; + } + + protected static String timeSummary(int seconds) { + String time = ""; + + if (seconds > 60 * 60 * 24) { + time += TimeUnit.SECONDS.toDays(seconds) + "d"; + seconds %= 60 * 60 * 24; + } + + if (seconds > 60 * 60) { + time += TimeUnit.SECONDS.toHours(seconds) + "h"; + seconds %= 60 * 60; + } + + if (seconds > 0) { + time += TimeUnit.SECONDS.toMinutes(seconds) + "m"; + } + return time; + } + + 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, (double) 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, List def) { + config.addDefault(path, def); + return (List) 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)); + } + + public static boolean disableEntityStuckChecks = false; + private static void disableEntityStuckChecks() { + disableEntityStuckChecks = getBoolean("settings.disableEntityStuckChecks", false); + } + +} diff --git a/src/main/java/net/minecraft/server/DedicatedServer.java b/src/main/java/net/minecraft/server/DedicatedServer.java index a60634fc455e8a59399020689b70eb64b6824d12..cb4a89940cf02de5c3eefc5c746397a931c96b7a 100644 --- a/src/main/java/net/minecraft/server/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/DedicatedServer.java @@ -181,6 +181,15 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer // Paper end com.tuinity.tuinity.config.TuinityConfig.init((java.io.File) options.valueOf("tuinity-settings")); // Tuinity - Server Config + // Yatopia start + try { + dev.tr7zw.yatopia.YatopiaConfig.init((java.io.File) options.valueOf("yatopia-settings")); + } catch (Exception e) { + DedicatedServer.LOGGER.error("Unable to load server configuration", e); + return false; + } + dev.tr7zw.yatopia.YatopiaConfig.registerCommands(); + // Yatopia end this.setPVP(dedicatedserverproperties.pvp); this.setAllowFlight(dedicatedserverproperties.allowFlight); this.setResourcePack(dedicatedserverproperties.resourcePack, this.aZ()); diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java index f24f7f1230a380f46112e886fb5dff7c33edb2ce..88f4c30a03e9139b0284ff1a545ad80941dbd46c 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -266,6 +266,7 @@ public abstract class EntityLiving extends Entity { //this.world.getMethodProfiler().enter("livingEntityBaseTick"); // Akarin - remove caller boolean flag = this instanceof EntityHuman; + if (dev.tr7zw.yatopia.YatopiaConfig.disableEntityStuckChecks) { // Yatopia if (this.isAlive()) { if (this.inBlock()) { this.damageEntity(DamageSource.STUCK, 1.0F); @@ -281,6 +282,7 @@ public abstract class EntityLiving extends Entity { } } } + } // Yatopia if (this.isFireProof() || this.world.isClientSide) { this.extinguish(); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 2d32ab02d7edee9a964dc2d9248c0c769e7dcac8..9f9bbd1c7830a93da3fcbd399bb2e402b7698ec3 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -844,6 +844,7 @@ public final class CraftServer implements Server { net.pl3x.purpur.PurpurConfig.init((File) console.options.valueOf("purpur-settings")); // Purpur com.proximyst.rainforest.RainforestConfig.init((File) console.options.valueOf("rainforest-settings")); // Rainforest de.minebench.origami.OrigamiConfig.init((File) console.options.valueOf("origami-settings")); // Origami + dev.tr7zw.yatopia.YatopiaConfig.init((File) console.options.valueOf("yatopia-settings")); // Yatopia for (WorldServer world : console.getWorlds()) { world.worldDataServer.setDifficulty(config.difficulty); world.setSpawnFlags(config.spawnMonsters, config.spawnAnimals); @@ -2271,6 +2272,13 @@ public final class CraftServer implements Server { } // Origami end + // Yatopia start + @Override + public YamlConfiguration getYatopiaConfig() { + return dev.tr7zw.yatopia.YatopiaConfig.config; + } + // Yatopia 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 132ad379242d9aa2defc639f79764050b73c572d..5387a50e4a24e2eb49693368c5342a69b8d14b69 100644 --- a/src/main/java/org/bukkit/craftbukkit/Main.java +++ b/src/main/java/org/bukkit/craftbukkit/Main.java @@ -169,6 +169,14 @@ public class Main { .describedAs("Yml file"); // Origami end + // Yatopia start + acceptsAll(asList("yatopia", "yatopia-settings"), "File for yatopia settings") + .withRequiredArg() + .ofType(File.class) + .defaultsTo(new File("yatopia.yml")) + .describedAs("Yml file"); + // Yatopia end + // Paper start acceptsAll(asList("server-name"), "Name of the server") .withRequiredArg()