diff --git a/README.md b/README.md index 599d7e0..5e1ab4e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ magnolia runtime +**WARNING: This software is still in its early stages, it is NOT production ready! (Well technically it could be, but you know what I mean.)** + Magnolia runtime is a different-ish paper server fork for following reasons: * Instead of patching server code directly everything will be gradually moved overt to `magnolia-core` module that does not require patches for changes * Bukkit, Spigot and Paper APIs will be gradually deprecated and their new replacement will be implemented in `magnolia-api` module that again does not require patches for changing things diff --git a/assets/magnolia-logo.png b/assets/magnolia-logo.png new file mode 100644 index 0000000..91f0270 Binary files /dev/null and b/assets/magnolia-logo.png differ diff --git a/gradle.properties b/gradle.properties index 94e9bd2..b62e713 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ -group=com.example.paperfork +group=org.op65n.magnolia version=1.18.2-R0.1-SNAPSHOT mcVersion=1.18.2 -paperRef=c449f6a1f712b81b50e250a2e258ef3e37fd6b9b +paperRef=9fd870db0b681b9840706aea4958376492e725b5 org.gradle.caching=true org.gradle.parallel=true diff --git a/magnolia-core/src/main/java/org/op65n/magnolia/core/MagnoliaCore.java b/magnolia-core/src/main/java/org/op65n/magnolia/core/MagnoliaCore.java index 9787bd9..67d5709 100644 --- a/magnolia-core/src/main/java/org/op65n/magnolia/core/MagnoliaCore.java +++ b/magnolia-core/src/main/java/org/op65n/magnolia/core/MagnoliaCore.java @@ -1,13 +1,19 @@ package org.op65n.magnolia.core; +import org.op65n.magnolia.core.console.ColoredErrorStream; import org.op65n.magnolia.core.util.SystemProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MagnoliaCore { - public static void bootstrapRuntime() { - SystemProperty.ifMissing("jdk.nio.maxCachedBufferSize", "262144"); // cap per-thread NIO cache size + private static final Logger log = LoggerFactory.getLogger(MagnoliaCore.class); + public static void bootstrapRuntime() { + ColoredErrorStream.overrideStream(); // let's enjoy the red exceptions again, or at least try to :/ + SystemProperty.ifMissing("jdk.nio.maxCachedBufferSize", "262144"); // cap per-thread NIO cache size + SystemProperty.ifMissing("java.awt.headless", "true"); // force AWT to work with its head chopped off - thanks Velocity } } diff --git a/magnolia-core/src/main/java/org/op65n/magnolia/core/console/ColoredErrorStream.java b/magnolia-core/src/main/java/org/op65n/magnolia/core/console/ColoredErrorStream.java new file mode 100644 index 0000000..564b68c --- /dev/null +++ b/magnolia-core/src/main/java/org/op65n/magnolia/core/console/ColoredErrorStream.java @@ -0,0 +1,66 @@ +package org.op65n.magnolia.core.console; + +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +/** + * This class overrides default output stream and + * appends ASCII colors to the content, we are only overriding + * error stream with red prefix and reset suffix to get red error + * messages and exceptions in the terminal output + * + * @author sebba + */ +public final class ColoredErrorStream extends OutputStream { + + private static final String COLOR_RED = "\033[31m"; + private static final String COLOR_RESET = "\033[0m"; + + private final OutputStream stream; + + private ColoredErrorStream(final @NotNull OutputStream stream) { + this.stream = stream; + } + + public static void overrideStream() { + final ColoredErrorStream coloredStream = new ColoredErrorStream(System.err); + final PrintStream printStream = new PrintStream(coloredStream, true); + + System.setErr(printStream); + } + + @Override + public void write(final int b) throws IOException { + stream.write(COLOR_RED.getBytes()); + stream.write(b); + stream.write(COLOR_RESET.getBytes()); + } + + @Override + public void write(final byte @NotNull [] b) throws IOException { + stream.write(COLOR_RED.getBytes()); + stream.write(b); + stream.write(COLOR_RESET.getBytes()); + } + + @Override + public void write(final byte @NotNull [] b, final int off, final int len) throws IOException { + stream.write(COLOR_RED.getBytes()); + stream.write(b, off, len); + stream.write(COLOR_RESET.getBytes()); + } + + @Override + public void flush() throws IOException { + stream.flush(); + } + + @Override + public void close() throws IOException { + stream.close(); + } + +} diff --git a/patches/server/0002-removed-spigot-metrics.patch b/patches/server/0002-removed-spigot-metrics.patch new file mode 100644 index 0000000..4f91aa3 --- /dev/null +++ b/patches/server/0002-removed-spigot-metrics.patch @@ -0,0 +1,665 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: sebba +Date: Fri, 13 May 2022 02:54:16 +0200 +Subject: [PATCH] removed spigot metrics + + +diff --git a/src/main/java/org/spigotmc/Metrics.java b/src/main/java/org/spigotmc/Metrics.java +deleted file mode 100644 +index 8f15af52d2ed734732134bf011ccdd4b7856a284..0000000000000000000000000000000000000000 +--- a/src/main/java/org/spigotmc/Metrics.java ++++ /dev/null +@@ -1,641 +0,0 @@ +-/* +- * Copyright 2011-2013 Tyler Blair. All rights reserved. +- * +- * Redistribution and use in source and binary forms, with or without modification, are +- * permitted provided that the following conditions are met: +- * +- * 1. Redistributions of source code must retain the above copyright notice, this list of +- * conditions and the following disclaimer. +- * +- * 2. Redistributions in binary form must reproduce the above copyright notice, this list +- * of conditions and the following disclaimer in the documentation and/or other materials +- * provided with the distribution. +- * +- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED +- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR +- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- * +- * The views and conclusions contained in the software and documentation are those of the +- * authors and contributors and should not be interpreted as representing official policies, +- * either expressed or implied, of anybody else. +- */ +-package org.spigotmc; +- +-import java.io.BufferedReader; +-import java.io.File; +-import java.io.IOException; +-import java.io.InputStreamReader; +-import java.io.OutputStreamWriter; +-import java.io.UnsupportedEncodingException; +-import java.net.Proxy; +-import java.net.URL; +-import java.net.URLConnection; +-import java.net.URLEncoder; +-import java.util.Collections; +-import java.util.HashSet; +-import java.util.Iterator; +-import java.util.LinkedHashSet; +-import java.util.Set; +-import java.util.Timer; +-import java.util.TimerTask; +-import java.util.UUID; +-import java.util.concurrent.TimeUnit; +-import java.util.logging.Level; +-import net.minecraft.server.MinecraftServer; +-import org.bukkit.Bukkit; +-import org.bukkit.configuration.InvalidConfigurationException; +-import org.bukkit.configuration.file.YamlConfiguration; +- +-/** +- *

The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.

+- * Public methods provided by this class:

+- * +- * Graph createGraph(String name);
+- * void addCustomData(BukkitMetrics.Plotter plotter);
+- * void start();
+- *
+- */ +-public class Metrics { +- +- /** +- * The current revision number +- */ +- private static final int REVISION = 6; +- /** +- * The base url of the metrics domain +- */ +- private static final String BASE_URL = "https://mcstats.spigotmc.org"; +- /** +- * The url used to report a server's status +- */ +- private static final String REPORT_URL = "/report/%s"; +- /** +- * The separator to use for custom data. This MUST NOT change unless you are hosting your own version of metrics and +- * want to change it. +- */ +- private static final String CUSTOM_DATA_SEPARATOR = "~~"; +- /** +- * Interval of time to ping (in minutes) +- */ +- private static final int PING_INTERVAL = 10; +- /** +- * All of the custom graphs to submit to metrics +- */ +- private final Set graphs = Collections.synchronizedSet(new HashSet()); +- /** +- * The default graph, used for addCustomData when you don't want a specific graph +- */ +- private final Graph defaultGraph = new Graph("Default"); +- /** +- * The plugin configuration file +- */ +- private final YamlConfiguration configuration; +- /** +- * The plugin configuration file +- */ +- private final File configurationFile; +- /** +- * Unique server id +- */ +- private final String guid; +- /** +- * Debug mode +- */ +- private final boolean debug; +- /** +- * Lock for synchronization +- */ +- private final Object optOutLock = new Object(); +- /** +- * The scheduled task +- */ +- private volatile Timer task = null; +- +- public Metrics() throws IOException { +- // load the config +- this.configurationFile = this.getConfigFile(); +- this.configuration = YamlConfiguration.loadConfiguration(configurationFile); +- +- // add some defaults +- this.configuration.addDefault("opt-out", false); +- this.configuration.addDefault("guid", UUID.randomUUID().toString()); +- this.configuration.addDefault("debug", false); +- +- // Do we need to create the file? +- if (this.configuration.get("guid", null) == null) { +- this.configuration.options().header("http://mcstats.org").copyDefaults(true); +- this.configuration.save(configurationFile); +- } +- +- // Load the guid then +- this.guid = this.configuration.getString("guid"); +- this.debug = this.configuration.getBoolean("debug", false); +- } +- +- /** +- * Construct and create a Graph that can be used to separate specific plotters to their own graphs on the metrics +- * website. Plotters can be added to the graph object returned. +- * +- * @param name The name of the graph +- * @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given +- */ +- public Graph createGraph(final String name) { +- if (name == null) { +- throw new IllegalArgumentException("Graph name cannot be null"); +- } +- +- // Construct the graph object +- final Graph graph = new Graph(name); +- +- // Now we can add our graph +- this.graphs.add(graph); +- +- // and return back +- return graph; +- } +- +- /** +- * Add a Graph object to BukkitMetrics that represents data for the plugin that should be sent to the backend +- * +- * @param graph The name of the graph +- */ +- public void addGraph(final Graph graph) { +- if (graph == null) { +- throw new IllegalArgumentException("Graph cannot be null"); +- } +- +- this.graphs.add(graph); +- } +- +- /** +- * Adds a custom data plotter to the default graph +- * +- * @param plotter The plotter to use to plot custom data +- */ +- public void addCustomData(final Plotter plotter) { +- if (plotter == null) { +- throw new IllegalArgumentException("Plotter cannot be null"); +- } +- +- // Add the plotter to the graph o/ +- this.defaultGraph.addPlotter(plotter); +- +- // Ensure the default graph is included in the submitted graphs +- this.graphs.add(defaultGraph); +- } +- +- /** +- * Start measuring statistics. This will immediately create an async repeating task as the plugin and send the +- * initial data to the metrics backend, and then after that it will post in increments of PING_INTERVAL * 1200 +- * ticks. +- * +- * @return True if statistics measuring is running, otherwise false. +- */ +- public boolean start() { +- synchronized (this.optOutLock) { +- // Did we opt out? +- if (this.isOptOut()) { +- return false; +- } +- +- // Is metrics already running? +- if (this.task != null) { +- return true; +- } +- +- // Begin hitting the server with glorious data +- this.task = new Timer("Spigot Metrics Thread", true); +- +- this.task.scheduleAtFixedRate(new TimerTask() { +- private boolean firstPost = true; +- +- public void run() { +- try { +- // This has to be synchronized or it can collide with the disable method. +- synchronized (Metrics.this.optOutLock) { +- // Disable Task, if it is running and the server owner decided to opt-out +- if (Metrics.this.isOptOut() && Metrics.this.task != null) { +- Metrics.this.task.cancel(); +- Metrics.this.task = null; +- // Tell all plotters to stop gathering information. +- for (Graph graph : Metrics.this.graphs) { +- graph.onOptOut(); +- } +- } +- } +- +- // We use the inverse of firstPost because if it is the first time we are posting, +- // it is not a interval ping, so it evaluates to FALSE +- // Each time thereafter it will evaluate to TRUE, i.e PING! +- Metrics.this.postPlugin(!firstPost); +- +- // After the first post we set firstPost to false +- // Each post thereafter will be a ping +- firstPost = false; +- } catch (IOException e) { +- if (Metrics.this.debug) { +- Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); +- } +- } +- } +- }, 0, TimeUnit.MINUTES.toMillis(PING_INTERVAL)); +- +- return true; +- } +- } +- +- /** +- * Has the server owner denied plugin metrics? +- * +- * @return true if metrics should be opted out of it +- */ +- public boolean isOptOut() { +- synchronized (this.optOutLock) { +- try { +- // Reload the metrics file +- this.configuration.load(this.getConfigFile()); +- } catch (IOException ex) { +- if (this.debug) { +- Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); +- } +- return true; +- } catch (InvalidConfigurationException ex) { +- if (this.debug) { +- Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); +- } +- return true; +- } +- return this.configuration.getBoolean("opt-out", false); +- } +- } +- +- /** +- * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. +- * +- * @throws java.io.IOException +- */ +- public void enable() throws IOException { +- // This has to be synchronized or it can collide with the check in the task. +- synchronized (this.optOutLock) { +- // Check if the server owner has already set opt-out, if not, set it. +- if (this.isOptOut()) { +- this.configuration.set("opt-out", false); +- this.configuration.save(configurationFile); +- } +- +- // Enable Task, if it is not running +- if (this.task == null) { +- this.start(); +- } +- } +- } +- +- /** +- * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. +- * +- * @throws java.io.IOException +- */ +- public void disable() throws IOException { +- // This has to be synchronized or it can collide with the check in the task. +- synchronized (this.optOutLock) { +- // Check if the server owner has already set opt-out, if not, set it. +- if (!this.isOptOut()) { +- this.configuration.set("opt-out", true); +- this.configuration.save(configurationFile); +- } +- +- // Disable Task, if it is running +- if (this.task != null) { +- this.task.cancel(); +- this.task = null; +- } +- } +- } +- +- /** +- * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status +- * +- * @return the File object for the config file +- */ +- public File getConfigFile() { +- // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use +- // is to abuse the plugin object we already have +- // plugin.getDataFolder() => base/plugins/PluginA/ +- // pluginsFolder => base/plugins/ +- // The base is not necessarily relative to the startup directory. +- // File pluginsFolder = plugin.getDataFolder().getParentFile(); +- +- // return => base/plugins/PluginMetrics/config.yml +- return new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "PluginMetrics"), "config.yml"); +- } +- +- /** +- * Generic method that posts a plugin to the metrics website +- */ +- private void postPlugin(final boolean isPing) throws IOException { +- // Server software specific section +- String pluginName = "Spigot"; +- boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled +- String pluginVersion = (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown"; +- String serverVersion = Bukkit.getVersion(); +- int playersOnline = Bukkit.getServer().getOnlinePlayers().size(); +- +- // END server software specific section -- all code below does not use any code outside of this class / Java +- +- // Construct the post data +- final StringBuilder data = new StringBuilder(); +- +- // The plugin's description file containg all of the plugin data such as name, version, author, etc +- data.append(Metrics.encode("guid")).append('=').append(Metrics.encode(this.guid)); +- Metrics.encodeDataPair(data, "version", pluginVersion); +- Metrics.encodeDataPair(data, "server", serverVersion); +- Metrics.encodeDataPair(data, "players", Integer.toString(playersOnline)); +- Metrics.encodeDataPair(data, "revision", String.valueOf(REVISION)); +- +- // New data as of R6 +- String osname = System.getProperty("os.name"); +- String osarch = System.getProperty("os.arch"); +- String osversion = System.getProperty("os.version"); +- String java_version = System.getProperty("java.version"); +- int coreCount = Runtime.getRuntime().availableProcessors(); +- +- // normalize os arch .. amd64 -> x86_64 +- if (osarch.equals("amd64")) { +- osarch = "x86_64"; +- } +- +- Metrics.encodeDataPair(data, "osname", osname); +- Metrics.encodeDataPair(data, "osarch", osarch); +- Metrics.encodeDataPair(data, "osversion", osversion); +- Metrics.encodeDataPair(data, "cores", Integer.toString(coreCount)); +- Metrics.encodeDataPair(data, "online-mode", Boolean.toString(onlineMode)); +- Metrics.encodeDataPair(data, "java_version", java_version); +- +- // If we're pinging, append it +- if (isPing) { +- Metrics.encodeDataPair(data, "ping", "true"); +- } +- +- // Acquire a lock on the graphs, which lets us make the assumption we also lock everything +- // inside of the graph (e.g plotters) +- synchronized (this.graphs) { +- final Iterator iter = this.graphs.iterator(); +- +- while (iter.hasNext()) { +- final Graph graph = iter.next(); +- +- for (Plotter plotter : graph.getPlotters()) { +- // The key name to send to the metrics server +- // The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top +- // Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME +- final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName()); +- +- // The value to send, which for the foreseeable future is just the string +- // value of plotter.getValue() +- final String value = Integer.toString(plotter.getValue()); +- +- // Add it to the http post data :) +- Metrics.encodeDataPair(data, key, value); +- } +- } +- } +- +- // Create the url +- URL url = new URL(Metrics.BASE_URL + String.format(REPORT_URL, Metrics.encode(pluginName))); +- +- // Connect to the website +- URLConnection connection; +- +- // Mineshafter creates a socks proxy, so we can safely bypass it +- // It does not reroute POST requests so we need to go around it +- if (this.isMineshafterPresent()) { +- connection = url.openConnection(Proxy.NO_PROXY); +- } else { +- connection = url.openConnection(); +- } +- +- connection.setDoOutput(true); +- +- // Write the data +- final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); +- writer.write(data.toString()); +- writer.flush(); +- +- // Now read the response +- final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); +- final String response = reader.readLine(); +- +- // close resources +- writer.close(); +- reader.close(); +- +- if (response == null || response.startsWith("ERR")) { +- throw new IOException(response); //Throw the exception +- } else { +- // Is this the first update this hour? +- if (response.contains("OK This is your first update this hour")) { +- synchronized (this.graphs) { +- final Iterator iter = this.graphs.iterator(); +- +- while (iter.hasNext()) { +- final Graph graph = iter.next(); +- +- for (Plotter plotter : graph.getPlotters()) { +- plotter.reset(); +- } +- } +- } +- } +- } +- } +- +- /** +- * Check if mineshafter is present. If it is, we need to bypass it to send POST requests +- * +- * @return true if mineshafter is installed on the server +- */ +- private boolean isMineshafterPresent() { +- try { +- Class.forName("mineshafter.MineServer"); +- return true; +- } catch (Exception e) { +- return false; +- } +- } +- +- /** +- *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first key/value pair +- * MUST be included manually, e.g:

+- * +- * StringBuffer data = new StringBuffer(); +- * data.append(encode("guid")).append('=').append(encode(guid)); +- * encodeDataPair(data, "version", description.getVersion()); +- * +- * +- * @param buffer the stringbuilder to append the data pair onto +- * @param key the key value +- * @param value the value +- */ +- private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { +- buffer.append('&').append(Metrics.encode(key)).append('=').append(Metrics.encode(value)); +- } +- +- /** +- * Encode text as UTF-8 +- * +- * @param text the text to encode +- * @return the encoded text, as UTF-8 +- */ +- private static String encode(final String text) throws UnsupportedEncodingException { +- return URLEncoder.encode(text, "UTF-8"); +- } +- +- /** +- * Represents a custom graph on the website +- */ +- public static class Graph { +- +- /** +- * The graph's name, alphanumeric and spaces only :) If it does not comply to the above when submitted, it is +- * rejected +- */ +- private final String name; +- /** +- * The set of plotters that are contained within this graph +- */ +- private final Set plotters = new LinkedHashSet(); +- +- private Graph(final String name) { +- this.name = name; +- } +- +- /** +- * Gets the graph's name +- * +- * @return the Graph's name +- */ +- public String getName() { +- return this.name; +- } +- +- /** +- * Add a plotter to the graph, which will be used to plot entries +- * +- * @param plotter the plotter to add to the graph +- */ +- public void addPlotter(final Plotter plotter) { +- this.plotters.add(plotter); +- } +- +- /** +- * Remove a plotter from the graph +- * +- * @param plotter the plotter to remove from the graph +- */ +- public void removePlotter(final Plotter plotter) { +- this.plotters.remove(plotter); +- } +- +- /** +- * Gets an unmodifiable set of the plotter objects in the graph +- * +- * @return an unmodifiable {@link java.util.Set} of the plotter objects +- */ +- public Set getPlotters() { +- return Collections.unmodifiableSet(plotters); +- } +- +- @Override +- public int hashCode() { +- return this.name.hashCode(); +- } +- +- @Override +- public boolean equals(final Object object) { +- if (!(object instanceof Graph)) { +- return false; +- } +- +- final Graph graph = (Graph) object; +- return graph.name.equals(name); +- } +- +- /** +- * Called when the server owner decides to opt-out of BukkitMetrics while the server is running. +- */ +- protected void onOptOut() { +- } +- } +- +- /** +- * Interface used to collect custom data for a plugin +- */ +- public abstract static class Plotter { +- +- /** +- * The plot's name +- */ +- private final String name; +- +- /** +- * Construct a plotter with the default plot name +- */ +- public Plotter() { +- this("Default"); +- } +- +- /** +- * Construct a plotter with a specific plot name +- * +- * @param name the name of the plotter to use, which will show up on the website +- */ +- public Plotter(final String name) { +- this.name = name; +- } +- +- /** +- * Get the current value for the plotted point. Since this function defers to an external function it may or may +- * not return immediately thus cannot be guaranteed to be thread friendly or safe. This function can be called +- * from any thread so care should be taken when accessing resources that need to be synchronized. +- * +- * @return the current value for the point to be plotted. +- */ +- public abstract int getValue(); +- +- /** +- * Get the column name for the plotted point +- * +- * @return the plotted point's column name +- */ +- public String getColumnName() { +- return this.name; +- } +- +- /** +- * Called after the website graphs have been updated +- */ +- public void reset() { +- } +- +- @Override +- public int hashCode() { +- return this.getColumnName().hashCode(); +- } +- +- @Override +- public boolean equals(final Object object) { +- if (!(object instanceof Plotter)) { +- return false; +- } +- +- final Plotter plotter = (Plotter) object; +- return plotter.name.equals(name) && plotter.getValue() == this.getValue(); +- } +- } +-} +diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java +index d509187ca63963fdd7f1a44d89d2aa1a1b1ce3bd..7043115cf98d2043ed117db5f16e272b383627e5 100644 +--- a/src/main/java/org/spigotmc/SpigotConfig.java ++++ b/src/main/java/org/spigotmc/SpigotConfig.java +@@ -48,7 +48,6 @@ public class SpigotConfig + static int version; + static Map commands; + /*========================================================================*/ +- private static Metrics metrics; + + public static void init(File configFile) + { diff --git a/patches/server/0003-better-logger-pattern.patch b/patches/server/0003-better-logger-pattern.patch new file mode 100644 index 0000000..8ce25e5 --- /dev/null +++ b/patches/server/0003-better-logger-pattern.patch @@ -0,0 +1,48 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: sebba +Date: Fri, 13 May 2022 03:33:29 +0200 +Subject: [PATCH] better logger pattern + + +diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml +index 74ccc67e3c12dc5182602fb691ef3ddeb5b53280..5997e8f713419b9fd7e30fe590b99a9eeecaf6eb 100644 +--- a/src/main/resources/log4j2.xml ++++ b/src/main/resources/log4j2.xml +@@ -1,18 +1,29 @@ + + ++ ++ ++ %-p @%c{1} %d{yyyy-MM-dd HH:mm:ss} ❯ %m%n ++ ++ ++ ++ %highlight{%-p}{FATAL=bg_red black, ERROR=bg_red black, WARN=bg_yellow black, INFO=white, DEBUG=green, TRACE=blue} ++ ++ ++ ++ %style{%d{yyyy-MM-dd HH:mm:ss}}{magenta} %style{%c{1}}{cyan} ++ ++ ++ ++ %highlight{❯ %m%n}{FATAL=red, ERROR=red, WARN=yellow, INFO=white, DEBUG=green, TRACE=blue} ++ ++ ++ + + + + + +- +- +- +- +- +- +- ++ + + +