diff --git a/patches/server/0019-Remove-TickTask.patch b/patches/server/0019-Remove-TickTask.patch new file mode 100644 index 0000000..3065511 --- /dev/null +++ b/patches/server/0019-Remove-TickTask.patch @@ -0,0 +1,97 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 16:40:59 +0100 +Subject: [PATCH] Remove TickTask + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index c691ee6208521b1570f0f25c8bbfd05146bd155c..1fe78da9a07bb2cadcd18ad4d30f9b101c696c36 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -186,7 +186,7 @@ import org.bukkit.event.server.ServerLoadEvent; + import co.aikar.timings.MinecraftTimings; // Paper + import org.spigotmc.SlackActivityAccountant; // Spigot + +-public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, AutoCloseable { ++public abstract class MinecraftServer extends ReentrantBlockableEventLoop implements CommandSource, AutoCloseable { // Patina + + private static MinecraftServer SERVER; // Paper + public static final Logger LOGGER = LogManager.getLogger(); +@@ -1368,19 +1368,21 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop {}; + } + // Paper end +- return new TickTask(this.tickCount, runnable); ++ return runnable; // Patina + } + ++ /* // Patina + protected boolean shouldRun(TickTask ticktask) { + return ticktask.getTick() + 3 < this.tickCount || this.haveTime(); + } ++ */ + + @Override + public boolean pollTask() { +@@ -1411,10 +1413,12 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop optional = Optional.of(this.getFile("server-icon.png")).filter(File::isFile); +diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +index 406bfe20a7b8786bbc6bee46151be91dadec6180..369eb8e05d13cc4c9af096af22f670c29a6fe6e4 100644 +--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java ++++ b/src/main/java/net/minecraft/server/level/ServerChunkCache.java +@@ -1262,10 +1262,12 @@ public class ServerChunkCache extends ChunkSource { + return runnable; + } + ++ /* // Patina + @Override + protected boolean shouldRun(Runnable task) { + return true; + } ++ */ + + @Override + protected boolean scheduleExecutables() { +diff --git a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +index dcce05d2f4ab16424db4ab103a12188e207a457b..540aa5e12bb6b44d510c701f2867f541b07ebcc4 100644 +--- a/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java ++++ b/src/main/java/net/minecraft/util/thread/BlockableEventLoop.java +@@ -29,7 +29,7 @@ public abstract class BlockableEventLoop implements Profiler + + protected abstract R wrapRunnable(Runnable runnable); + +- protected abstract boolean shouldRun(R task); ++ //protected abstract boolean shouldRun(R task); // Patina + + public boolean isSameThread() { + return Thread.currentThread() == this.getRunningThread(); +@@ -116,7 +116,7 @@ public abstract class BlockableEventLoop implements Profiler + R runnable = this.pendingRunnables.peek(); + if (runnable == null) { + return false; +- } else if (this.blockingCount == 0 && !this.shouldRun(runnable)) { ++ } else if (this.blockingCount == 0 && !true/*this.shouldRun(runnable)*/) { // Patina + return false; + } else { + this.doRunTask(this.pendingRunnables.remove()); diff --git a/patches/server/0020-Completely-remove-Metrics.patch b/patches/server/0020-Completely-remove-Metrics.patch new file mode 100644 index 0000000..fdd145b --- /dev/null +++ b/patches/server/0020-Completely-remove-Metrics.patch @@ -0,0 +1,1375 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 16:51:55 +0100 +Subject: [PATCH] Completely remove Metrics + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java +deleted file mode 100644 +index e0a96811e6d14283c096fa2a0e63d92f96a6f6c7..0000000000000000000000000000000000000000 +--- a/src/main/java/com/destroystokyo/paper/Metrics.java ++++ /dev/null +@@ -1,675 +0,0 @@ +-package com.destroystokyo.paper; +- +-import net.minecraft.server.MinecraftServer; +-import org.bukkit.Bukkit; +-import org.bukkit.configuration.file.YamlConfiguration; +-import org.bukkit.craftbukkit.util.CraftMagicNumbers; +-import org.bukkit.plugin.Plugin; +- +-import org.json.simple.JSONArray; +-import org.json.simple.JSONObject; +- +-import javax.net.ssl.HttpsURLConnection; +-import java.io.ByteArrayOutputStream; +-import java.io.DataOutputStream; +-import java.io.File; +-import java.io.IOException; +-import java.net.URL; +-import java.util.*; +-import java.util.concurrent.Callable; +-import java.util.concurrent.Executors; +-import java.util.concurrent.ScheduledExecutorService; +-import java.util.concurrent.TimeUnit; +-import java.util.logging.Level; +-import java.util.logging.Logger; +-import java.util.regex.Matcher; +-import java.util.regex.Pattern; +-import java.util.zip.GZIPOutputStream; +- +-/** +- * bStats collects some data for plugin authors. +- * +- * Check out https://bStats.org/ to learn more about bStats! +- */ +-public class Metrics { +- +- // Executor service for requests +- // We use an executor service because the Bukkit scheduler is affected by server lags +- private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); +- +- // The version of this bStats class +- public static final int B_STATS_VERSION = 1; +- +- // The url to which the data is sent +- private static final String URL = "https://bStats.org/submitData/server-implementation"; +- +- // Should failed requests be logged? +- private static boolean logFailedRequests = false; +- +- // The logger for the failed requests +- private static Logger logger = Logger.getLogger("bStats"); +- +- // The name of the server software +- private final String name; +- +- // The uuid of the server +- private final String serverUUID; +- +- // A list with all custom charts +- private final List charts = new ArrayList<>(); +- +- /** +- * Class constructor. +- * +- * @param name The name of the server software. +- * @param serverUUID The uuid of the server. +- * @param logFailedRequests Whether failed requests should be logged or not. +- * @param logger The logger for the failed requests. +- */ +- public Metrics(String name, String serverUUID, boolean logFailedRequests, Logger logger) { +- this.name = name; +- this.serverUUID = serverUUID; +- Metrics.logFailedRequests = logFailedRequests; +- Metrics.logger = logger; +- +- // Start submitting the data +- startSubmitting(); +- } +- +- /** +- * Adds a custom chart. +- * +- * @param chart The chart to add. +- */ +- public void addCustomChart(CustomChart chart) { +- if (chart == null) { +- throw new IllegalArgumentException("Chart cannot be null!"); +- } +- charts.add(chart); +- } +- +- /** +- * Starts the Scheduler which submits our data every 30 minutes. +- */ +- private void startSubmitting() { +- final Runnable submitTask = () -> { +- if (MinecraftServer.getServer().hasStopped()) { +- return; +- } +- submitData(); +- }; +- +- // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the +- // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay. +- // WARNING: You must not modify any part of this Metrics class, including the submit delay or frequency! +- // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it! +- long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); +- long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); +- scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); +- scheduler.scheduleAtFixedRate(submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); +- } +- +- /** +- * Gets the plugin specific data. +- * +- * @return The plugin specific data. +- */ +- private JSONObject getPluginData() { +- JSONObject data = new JSONObject(); +- +- data.put("pluginName", name); // Append the name of the server software +- JSONArray customCharts = new JSONArray(); +- for (CustomChart customChart : charts) { +- // Add the data of the custom charts +- JSONObject chart = customChart.getRequestJsonObject(); +- if (chart == null) { // If the chart is null, we skip it +- continue; +- } +- customCharts.add(chart); +- } +- data.put("customCharts", customCharts); +- +- return data; +- } +- +- /** +- * Gets the server specific data. +- * +- * @return The server specific data. +- */ +- private JSONObject getServerData() { +- // OS specific data +- String osName = System.getProperty("os.name"); +- String osArch = System.getProperty("os.arch"); +- String osVersion = System.getProperty("os.version"); +- int coreCount = Runtime.getRuntime().availableProcessors(); +- +- JSONObject data = new JSONObject(); +- +- data.put("serverUUID", serverUUID); +- +- data.put("osName", osName); +- data.put("osArch", osArch); +- data.put("osVersion", osVersion); +- data.put("coreCount", coreCount); +- +- return data; +- } +- +- /** +- * Collects the data and sends it afterwards. +- */ +- private void submitData() { +- final JSONObject data = getServerData(); +- +- JSONArray pluginData = new JSONArray(); +- pluginData.add(getPluginData()); +- data.put("plugins", pluginData); +- +- try { +- // We are still in the Thread of the timer, so nothing get blocked :) +- sendData(data); +- } catch (Exception e) { +- // Something went wrong! :( +- if (logFailedRequests) { +- logger.log(Level.WARNING, "Could not submit stats of " + name, e); +- } +- } +- } +- +- /** +- * Sends the data to the bStats server. +- * +- * @param data The data to send. +- * @throws Exception If the request failed. +- */ +- private static void sendData(JSONObject data) throws Exception { +- if (data == null) { +- throw new IllegalArgumentException("Data cannot be null!"); +- } +- HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); +- +- // Compress the data to save bandwidth +- byte[] compressedData = compress(data.toString()); +- +- // Add headers +- connection.setRequestMethod("POST"); +- connection.addRequestProperty("Accept", "application/json"); +- connection.addRequestProperty("Connection", "close"); +- connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request +- connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); +- connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format +- connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); +- +- // Send data +- connection.setDoOutput(true); +- DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); +- outputStream.write(compressedData); +- outputStream.flush(); +- outputStream.close(); +- +- connection.getInputStream().close(); // We don't care about the response - Just send our data :) +- } +- +- /** +- * Gzips the given String. +- * +- * @param str The string to gzip. +- * @return The gzipped String. +- * @throws IOException If the compression failed. +- */ +- private static byte[] compress(final String str) throws IOException { +- if (str == null) { +- return null; +- } +- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); +- GZIPOutputStream gzip = new GZIPOutputStream(outputStream); +- gzip.write(str.getBytes("UTF-8")); +- gzip.close(); +- return outputStream.toByteArray(); +- } +- +- /** +- * Represents a custom chart. +- */ +- public static abstract class CustomChart { +- +- // The id of the chart +- final String chartId; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- */ +- CustomChart(String chartId) { +- if (chartId == null || chartId.isEmpty()) { +- throw new IllegalArgumentException("ChartId cannot be null or empty!"); +- } +- this.chartId = chartId; +- } +- +- private JSONObject getRequestJsonObject() { +- JSONObject chart = new JSONObject(); +- chart.put("chartId", chartId); +- try { +- JSONObject data = getChartData(); +- if (data == null) { +- // If the data is null we don't send the chart. +- return null; +- } +- chart.put("data", data); +- } catch (Throwable t) { +- if (logFailedRequests) { +- logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); +- } +- return null; +- } +- return chart; +- } +- +- protected abstract JSONObject getChartData() throws Exception; +- +- } +- +- /** +- * Represents a custom simple pie. +- */ +- public static class SimplePie extends CustomChart { +- +- private final Callable callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public SimplePie(String chartId, Callable callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- String value = callable.call(); +- if (value == null || value.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- data.put("value", value); +- return data; +- } +- } +- +- /** +- * Represents a custom advanced pie. +- */ +- public static class AdvancedPie extends CustomChart { +- +- private final Callable> callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public AdvancedPie(String chartId, Callable> callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- JSONObject values = new JSONObject(); +- Map map = callable.call(); +- if (map == null || map.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- boolean allSkipped = true; +- for (Map.Entry entry : map.entrySet()) { +- if (entry.getValue() == 0) { +- continue; // Skip this invalid +- } +- allSkipped = false; +- values.put(entry.getKey(), entry.getValue()); +- } +- if (allSkipped) { +- // Null = skip the chart +- return null; +- } +- data.put("values", values); +- return data; +- } +- } +- +- /** +- * Represents a custom drilldown pie. +- */ +- public static class DrilldownPie extends CustomChart { +- +- private final Callable>> callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public DrilldownPie(String chartId, Callable>> callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- public JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- JSONObject values = new JSONObject(); +- Map> map = callable.call(); +- if (map == null || map.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- boolean reallyAllSkipped = true; +- for (Map.Entry> entryValues : map.entrySet()) { +- JSONObject value = new JSONObject(); +- boolean allSkipped = true; +- for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { +- value.put(valueEntry.getKey(), valueEntry.getValue()); +- allSkipped = false; +- } +- if (!allSkipped) { +- reallyAllSkipped = false; +- values.put(entryValues.getKey(), value); +- } +- } +- if (reallyAllSkipped) { +- // Null = skip the chart +- return null; +- } +- data.put("values", values); +- return data; +- } +- } +- +- /** +- * Represents a custom single line chart. +- */ +- public static class SingleLineChart extends CustomChart { +- +- private final Callable callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public SingleLineChart(String chartId, Callable callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- int value = callable.call(); +- if (value == 0) { +- // Null = skip the chart +- return null; +- } +- data.put("value", value); +- return data; +- } +- +- } +- +- /** +- * Represents a custom multi line chart. +- */ +- public static class MultiLineChart extends CustomChart { +- +- private final Callable> callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public MultiLineChart(String chartId, Callable> callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- JSONObject values = new JSONObject(); +- Map map = callable.call(); +- if (map == null || map.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- boolean allSkipped = true; +- for (Map.Entry entry : map.entrySet()) { +- if (entry.getValue() == 0) { +- continue; // Skip this invalid +- } +- allSkipped = false; +- values.put(entry.getKey(), entry.getValue()); +- } +- if (allSkipped) { +- // Null = skip the chart +- return null; +- } +- data.put("values", values); +- return data; +- } +- +- } +- +- /** +- * Represents a custom simple bar chart. +- */ +- public static class SimpleBarChart extends CustomChart { +- +- private final Callable> callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public SimpleBarChart(String chartId, Callable> callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- JSONObject values = new JSONObject(); +- Map map = callable.call(); +- if (map == null || map.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- for (Map.Entry entry : map.entrySet()) { +- JSONArray categoryValues = new JSONArray(); +- categoryValues.add(entry.getValue()); +- values.put(entry.getKey(), categoryValues); +- } +- data.put("values", values); +- return data; +- } +- +- } +- +- /** +- * Represents a custom advanced bar chart. +- */ +- public static class AdvancedBarChart extends CustomChart { +- +- private final Callable> callable; +- +- /** +- * Class constructor. +- * +- * @param chartId The id of the chart. +- * @param callable The callable which is used to request the chart data. +- */ +- public AdvancedBarChart(String chartId, Callable> callable) { +- super(chartId); +- this.callable = callable; +- } +- +- @Override +- protected JSONObject getChartData() throws Exception { +- JSONObject data = new JSONObject(); +- JSONObject values = new JSONObject(); +- Map map = callable.call(); +- if (map == null || map.isEmpty()) { +- // Null = skip the chart +- return null; +- } +- boolean allSkipped = true; +- for (Map.Entry entry : map.entrySet()) { +- if (entry.getValue().length == 0) { +- continue; // Skip this invalid +- } +- allSkipped = false; +- JSONArray categoryValues = new JSONArray(); +- for (int categoryValue : entry.getValue()) { +- categoryValues.add(categoryValue); +- } +- values.put(entry.getKey(), categoryValues); +- } +- if (allSkipped) { +- // Null = skip the chart +- return null; +- } +- data.put("values", values); +- return data; +- } +- +- } +- +- static class PaperMetrics { +- static void startMetrics() { +- // Get the config file +- File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "config.yml"); +- YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); +- +- // Check if the config file exists +- if (!config.isSet("serverUuid")) { +- +- // Add default values +- config.addDefault("enabled", true); +- // Every server gets it's unique random id. +- config.addDefault("serverUuid", UUID.randomUUID().toString()); +- // Should failed request be logged? +- config.addDefault("logFailedRequests", false); +- +- // Inform the server owners about bStats +- config.options().header( +- "bStats collects some data for plugin authors like how many servers are using their plugins.\n" + +- "To honor their work, you should not disable it.\n" + +- "This has nearly no effect on the server performance!\n" + +- "Check out https://bStats.org/ to learn more :)" +- ).copyDefaults(true); +- try { +- config.save(configFile); +- } catch (IOException ignored) { +- } +- } +- // Load the data +- String serverUUID = config.getString("serverUuid"); +- 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("Mirai", serverUUID, logFailedRequests, Bukkit.getLogger()); // Mirai +- +- metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> { +- String minecraftVersion = Bukkit.getVersion(); +- minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1); +- return minecraftVersion; +- })); +- +- metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size())); +- metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline")); +- metrics.addCustomChart(new Metrics.SimplePie("mirai_version", () -> (Metrics.class.getPackage().getImplementationVersion() != null) ? Metrics.class.getPackage().getImplementationVersion() : "unknown")); +- +- metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> { +- Map> map = new HashMap<>(); +- String javaVersion = System.getProperty("java.version"); +- Map entry = new HashMap<>(); +- entry.put(javaVersion, 1); +- +- // http://openjdk.java.net/jeps/223 +- // Java decided to change their versioning scheme and in doing so modified the java.version system +- // property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier +- // we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+ +- String majorVersion = javaVersion.split("\\.")[0]; +- String release; +- +- int indexOf = javaVersion.lastIndexOf('.'); +- +- if (majorVersion.equals("1")) { +- release = "Java " + javaVersion.substring(0, indexOf); +- } else { +- // of course, it really wouldn't be all that simple if they didn't add a quirk, now would it +- // valid strings for the major may potentially include values such as -ea to deannotate a pre release +- Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion); +- if (versionMatcher.find()) { +- majorVersion = versionMatcher.group(0); +- } +- release = "Java " + majorVersion; +- } +- map.put(release, entry); +- +- return map; +- })); +- +- metrics.addCustomChart(new Metrics.DrilldownPie("legacy_plugins", () -> { +- Map> map = new HashMap<>(); +- +- // count legacy plugins +- int legacy = 0; +- for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) { +- if (CraftMagicNumbers.isLegacy(plugin.getDescription())) { +- legacy++; +- } +- } +- +- // insert real value as lower dimension +- Map entry = new HashMap<>(); +- entry.put(String.valueOf(legacy), 1); +- +- // create buckets as higher dimension +- if (legacy == 0) { +- map.put("0 \uD83D\uDE0E", entry); // :sunglasses: +- } else if (legacy <= 5) { +- map.put("1-5", entry); +- } else if (legacy <= 10) { +- map.put("6-10", entry); +- } else if (legacy <= 25) { +- map.put("11-25", entry); +- } else if (legacy <= 50) { +- map.put("26-50", entry); +- } else { +- map.put("50+ \uD83D\uDE2D", entry); // :cry: +- } +- +- return map; +- })); +- } +- +- } +- } +-} +diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java +index 9b12693b07b1fd3a634f865ac4ec612bb890f7ff..dc689c24fcd6ecdc9bc5f6f7f74964f4a311baa7 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java +@@ -51,7 +51,7 @@ public class PaperConfig { + private static boolean verbose; + private static boolean fatalError; + /*========================================================================*/ +- private static boolean metricsStarted; ++ //private static boolean metricsStarted; // Patina + + public static void init(File configFile) { + CONFIG_FILE = configFile; +@@ -123,10 +123,12 @@ public class PaperConfig { + MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Paper", entry.getValue()); + } + ++ /* // Patina + if (!metricsStarted) { + Metrics.PaperMetrics.startMetrics(); + metricsStarted = true; + } ++ */ + } + + static void readConfig(Class clazz, Object instance) { +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 3b9dfaf9e6a63220754738dc966ee72cb91f80a4..238b056c154ba1c58b8d63a387d08910a31d0560 100644 +--- a/src/main/java/org/spigotmc/SpigotConfig.java ++++ b/src/main/java/org/spigotmc/SpigotConfig.java +@@ -48,7 +48,7 @@ public class SpigotConfig + static int version; + static Map commands; + /*========================================================================*/ +- private static Metrics metrics; ++ //private static Metrics metrics; // Patina + + public static void init(File configFile) + { diff --git a/patches/server/0021-Remove-VersionFetcher.patch b/patches/server/0021-Remove-VersionFetcher.patch new file mode 100644 index 0000000..b4d1908 --- /dev/null +++ b/patches/server/0021-Remove-VersionFetcher.patch @@ -0,0 +1,180 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 16:53:47 +0100 +Subject: [PATCH] Remove VersionFetcher + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java +deleted file mode 100644 +index ece77f5ea4b14bbed7c070131b3251ea86764538..0000000000000000000000000000000000000000 +--- a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java ++++ /dev/null +@@ -1,149 +0,0 @@ +-package com.destroystokyo.paper; +- +-import com.destroystokyo.paper.util.VersionFetcher; +-import com.google.common.base.Charsets; +-import com.google.common.io.Resources; +-import com.google.gson.*; +-import net.kyori.adventure.text.Component; +-import net.kyori.adventure.text.event.ClickEvent; +-import net.kyori.adventure.text.format.NamedTextColor; +-import net.kyori.adventure.text.format.TextDecoration; +-import net.kyori.adventure.text.TextComponent; +- +-import javax.annotation.Nonnull; +-import javax.annotation.Nullable; +-import java.io.*; +-import java.net.HttpURLConnection; +-import java.net.URL; +-import java.util.stream.StreamSupport; +- +-public class PaperVersionFetcher implements VersionFetcher { +- private static final java.util.regex.Pattern VER_PATTERN = java.util.regex.Pattern.compile("^([0-9\\.]*)\\-.*R"); // R is an anchor, will always give '-R' at end +- private static final String GITHUB_BRANCH_NAME = "master"; +- private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads"; +- private static @Nullable String mcVer; +- +- @Override +- public long getCacheTime() { +- return 720000; +- } +- +- @Nonnull +- @Override +- public Component getVersionMessage(@Nonnull String serverVersion) { +- String[] parts = serverVersion.substring("git-Paper-".length()).split("[-\\s]"); +- final Component updateMessage = getUpdateStatusMessage("PaperMC/Paper", GITHUB_BRANCH_NAME, parts[0]); +- final Component history = getHistory(); +- +- return history != null ? TextComponent.ofChildren(updateMessage, Component.newline(), history) : updateMessage; +- } +- +- private static @Nullable String getMinecraftVersion() { +- if (mcVer == null) { +- java.util.regex.Matcher matcher = VER_PATTERN.matcher(org.bukkit.Bukkit.getBukkitVersion()); +- if (matcher.find()) { +- String result = matcher.group(); +- mcVer = result.substring(0, result.length() - 2); // strip 'R' anchor and trailing '-' +- } else { +- org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to PaperMC!"); +- org.bukkit.Bukkit.getLogger().warning("Pattern: " + VER_PATTERN.toString()); +- org.bukkit.Bukkit.getLogger().warning("Version: " + org.bukkit.Bukkit.getBukkitVersion()); +- } +- } +- +- return mcVer; +- } +- +- private static Component getUpdateStatusMessage(@Nonnull String repo, @Nonnull String branch, @Nonnull String versionInfo) { +- int distance; +- try { +- int jenkinsBuild = Integer.parseInt(versionInfo); +- distance = fetchDistanceFromSiteApi(jenkinsBuild, getMinecraftVersion()); +- } catch (NumberFormatException ignored) { +- versionInfo = versionInfo.replace("\"", ""); +- distance = fetchDistanceFromGitHub(repo, branch, versionInfo); +- } +- +- switch (distance) { +- case -1: +- return Component.text("Error obtaining version information", NamedTextColor.YELLOW); +- case 0: +- return Component.text("You are running the latest version", NamedTextColor.GREEN); +- case -2: +- return Component.text("Unknown version", NamedTextColor.YELLOW); +- default: +- return Component.text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW) +- .append(Component.newline()) +- .append(Component.text("Download the new version at: ") +- .append(Component.text(DOWNLOAD_PAGE, NamedTextColor.GOLD) +- .hoverEvent(Component.text("Click to open", NamedTextColor.WHITE)) +- .clickEvent(ClickEvent.openUrl(DOWNLOAD_PAGE)))); +- } +- } +- +- private static int fetchDistanceFromSiteApi(int jenkinsBuild, @Nullable String siteApiVersion) { +- if (siteApiVersion == null) { return -1; } +- try { +- try (BufferedReader reader = Resources.asCharSource( +- new URL("https://papermc.io/api/v2/projects/paper/versions/" + siteApiVersion), +- Charsets.UTF_8 +- ).openBufferedStream()) { +- JsonObject json = new Gson().fromJson(reader, JsonObject.class); +- JsonArray builds = json.getAsJsonArray("builds"); +- int latest = StreamSupport.stream(builds.spliterator(), false) +- .mapToInt(e -> e.getAsInt()) +- .max() +- .getAsInt(); +- return latest - jenkinsBuild; +- } catch (JsonSyntaxException ex) { +- ex.printStackTrace(); +- return -1; +- } +- } catch (IOException e) { +- e.printStackTrace(); +- return -1; +- } +- } +- +- // Contributed by Techcable in GH-65 +- private static int fetchDistanceFromGitHub(@Nonnull String repo, @Nonnull String branch, @Nonnull String hash) { +- try { +- HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/" + branch + "..." + hash).openConnection(); +- connection.connect(); +- if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit +- try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))) { +- JsonObject obj = new Gson().fromJson(reader, JsonObject.class); +- String status = obj.get("status").getAsString(); +- switch (status) { +- case "identical": +- return 0; +- case "behind": +- return obj.get("behind_by").getAsInt(); +- default: +- return -1; +- } +- } catch (JsonSyntaxException | NumberFormatException e) { +- e.printStackTrace(); +- return -1; +- } +- } catch (IOException e) { +- e.printStackTrace(); +- return -1; +- } +- } +- +- @Nullable +- private Component getHistory() { +- final VersionHistoryManager.VersionData data = VersionHistoryManager.INSTANCE.getVersionData(); +- if (data == null) { +- return null; +- } +- +- final String oldVersion = data.getOldVersion(); +- if (oldVersion == null) { +- return null; +- } +- +- return Component.text("Previous version: " + oldVersion, NamedTextColor.GRAY, TextDecoration.ITALIC); +- } +-} +diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +index 489932059dae0970e63d223a1c84b64688f09eee..ec38dcd9de5712cf0ef04eb30d471d5b57a3eb0c 100644 +--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java ++++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +@@ -392,10 +392,12 @@ public final class CraftMagicNumbers implements UnsafeValues { + return com.destroystokyo.paper.PaperConfig.timingsServerName; + } + ++ /* // Patina + @Override + public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { + return new com.destroystokyo.paper.PaperVersionFetcher(); // Mirai + } ++ */ + + @Override + public boolean isSupportedApiVersion(String apiVersion) { diff --git a/patches/server/0022-Remove-unnecessary-LogManager.getLogger.patch b/patches/server/0022-Remove-unnecessary-LogManager.getLogger.patch new file mode 100644 index 0000000..bf366cc --- /dev/null +++ b/patches/server/0022-Remove-unnecessary-LogManager.getLogger.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 16:56:03 +0100 +Subject: [PATCH] Remove unnecessary `LogManager.getLogger()` + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java +index 762927080ac2201fd74519d24989092902d4c78d..16def3cbbb048033d62e241603b1a3ffdceece19 100644 +--- a/src/main/java/net/minecraft/world/entity/Entity.java ++++ b/src/main/java/net/minecraft/world/entity/Entity.java +@@ -163,7 +163,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + @Override + public synchronized void setSeed(long seed) { + if (locked) { +- LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); ++ LOGGER.error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable()); // Patina - LOGGER + } else { + super.setSeed(seed); + locked = true; +@@ -2322,7 +2322,7 @@ public abstract class Entity implements Nameable, EntityAccess, CommandSource, i + try { + spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.valueOf(spawnReasonName); + } catch (Exception ignored) { +- LogManager.getLogger().error("Unknown SpawnReason " + spawnReasonName + " for " + this); ++ LOGGER.error("Unknown SpawnReason " + spawnReasonName + " for " + this); // Patina - LOGGER + } + } + if (spawnReason == null) { diff --git a/patches/server/0023-Make-a-field-final.patch b/patches/server/0023-Make-a-field-final.patch new file mode 100644 index 0000000..8a488f3 --- /dev/null +++ b/patches/server/0023-Make-a-field-final.patch @@ -0,0 +1,21 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 16:58:51 +0100 +Subject: [PATCH] Make a field final + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/commands/CommandSourceStack.java b/src/main/java/net/minecraft/commands/CommandSourceStack.java +index cb0045fc4ddd738c45dee89d57b213a633b9a136..325ca5b716702c151b3075f9cb062ce0793cb694 100644 +--- a/src/main/java/net/minecraft/commands/CommandSourceStack.java ++++ b/src/main/java/net/minecraft/commands/CommandSourceStack.java +@@ -56,7 +56,7 @@ public class CommandSourceStack implements SharedSuggestionProvider, com.destroy + private final ResultConsumer consumer; + private final EntityAnchorArgument.Anchor anchor; + private final Vec2 rotation; +- public java.util.Map currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper ++ public final java.util.Map currentCommand = new java.util.concurrent.ConcurrentHashMap<>(); // CraftBukkit // Paper // Patina - make a field final + + public CommandSourceStack(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String name, Component displayName, MinecraftServer server, @Nullable Entity entity) { + this(output, pos, rot, world, level, name, displayName, server, entity, false, (commandcontext, flag, j) -> { diff --git a/patches/server/0024-Don-t-create-new-random-instance.patch b/patches/server/0024-Don-t-create-new-random-instance.patch new file mode 100644 index 0000000..5511779 --- /dev/null +++ b/patches/server/0024-Don-t-create-new-random-instance.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 17:06:40 +0100 +Subject: [PATCH] Don't create new random instance + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 1fe78da9a07bb2cadcd18ad4d30f9b101c696c36..a16c569cf0c8dba0b2bc61dedf5394a941696d56 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -650,7 +650,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop 2147483647L ? Integer.MAX_VALUE : (int) l; + int j1 = this.getCoprime(i1); +- int k1 = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextInt(i1); ++ int k1 = worldserver.random.nextInt(i1); // Patina - don't create new random instance + + for (int l1 = 0; l1 < i1; ++l1) { + int i2 = (k1 + j1 * l1) % i1; +@@ -405,7 +405,7 @@ public class ServerPlayer extends Player { + long l = k * k; + int i1 = l > 2147483647L ? Integer.MAX_VALUE : (int) l; + int j1 = this.getCoprime(i1); +- int k1 = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextInt(i1); ++ int k1 = world.random.nextInt(i1); // Patina - don't create new random instance + + for (int l1 = 0; l1 < i1; ++l1) { + int i2 = (k1 + j1 * l1) % i1; +diff --git a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java +index b0cbe7d42eef1865e84211844b351027a26a5956..711d5dc2a8b3a5f2a2a35cab59b5ecfdeb152141 100644 +--- a/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java ++++ b/src/main/java/net/minecraft/server/rcon/thread/QueryThreadGs4.java +@@ -348,7 +348,7 @@ public class QueryThreadGs4 extends GenericThread { + this.identBytes[2] = bs[5]; + this.identBytes[3] = bs[6]; + this.ident = new String(this.identBytes, StandardCharsets.UTF_8); +- this.challenge = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextInt(16777216); ++ this.challenge = java.util.concurrent.ThreadLocalRandom.current().nextInt(16777216); // Patina - don't create new random instance + this.challengeBytes = String.format("\t%s%d\u0000", this.ident, this.challenge).getBytes(StandardCharsets.UTF_8); + } + +diff --git a/src/main/java/net/minecraft/world/level/Level.java b/src/main/java/net/minecraft/world/level/Level.java +index 21e478f81401649a170bfd20505f8094aa97b591..30ed4649ba827b27784cdc6278e6760e2b5acef3 100644 +--- a/src/main/java/net/minecraft/world/level/Level.java ++++ b/src/main/java/net/minecraft/world/level/Level.java +@@ -124,13 +124,14 @@ public abstract class Level implements LevelAccessor, AutoCloseable { + public final Thread thread; + private final boolean isDebug; + private int skyDarken; +- protected int randValue = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextInt(); ++ //protected int randValue = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextInt(); // Patina - moved down + protected final int addend = 1013904223; + protected float oRainLevel; + public float rainLevel; + protected float oThunderLevel; + public float thunderLevel; + public final Random random = new org.yatopiamc.yatopia.server.util.FastRandom(); ++ protected int randValue = random.nextInt(); // Patina - don't create new random instance + private final DimensionType dimensionType; + public final WritableLevelData levelData; + private final Supplier profiler; +diff --git a/src/main/java/net/minecraft/world/level/levelgen/WorldGenSettings.java b/src/main/java/net/minecraft/world/level/levelgen/WorldGenSettings.java +index e3a2919e214c63aa8cc2334d30dade3daf39b92d..a580c191a0450c1088c32344b30a3f452913ac5e 100644 +--- a/src/main/java/net/minecraft/world/level/levelgen/WorldGenSettings.java ++++ b/src/main/java/net/minecraft/world/level/levelgen/WorldGenSettings.java +@@ -86,7 +86,7 @@ public class WorldGenSettings { + } + + public static WorldGenSettings makeDefault(RegistryAccess registryManager) { +- long i = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextLong(); ++ long i = java.util.concurrent.ThreadLocalRandom.current().nextLong(); // Patina - don't create new random instance + + return new WorldGenSettings(i, true, false, WorldGenSettings.withOverworld(registryManager.registryOrThrow(Registry.DIMENSION_TYPE_REGISTRY), DimensionType.defaultDimensions(registryManager, i), WorldGenSettings.makeDefaultOverworld(registryManager, i))); + } +@@ -215,7 +215,7 @@ public class WorldGenSettings { + }).orElse("default"); + + properties.put("level-type", s4); +- long i = (new org.yatopiamc.yatopia.server.util.FastRandom()).nextLong(); ++ long i = java.util.concurrent.ThreadLocalRandom.current().nextLong(); // Patina - don't create new random instance + + if (!s1.isEmpty()) { + try { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 5ac260b44c97e82f4cd0436750fb5ff19d32a128..0291d6fca982c967e2d3ff31ea042d22b7e234d1 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1216,7 +1216,7 @@ public final class CraftServer implements Server { + + if (worlddimension == null) { + dimensionmanager = (DimensionType) console.registryHolder.registryOrThrow(Registry.DIMENSION_TYPE_REGISTRY).getOrThrow(DimensionType.OVERWORLD_LOCATION); +- chunkgenerator = WorldGenSettings.makeDefaultOverworld(console.registryHolder, (new org.yatopiamc.yatopia.server.util.FastRandom()).nextLong()); ++ chunkgenerator = WorldGenSettings.makeDefaultOverworld(console.registryHolder, java.util.concurrent.ThreadLocalRandom.current().nextLong()); // Patina - don't create new random instance + } else { + dimensionmanager = worlddimension.type(); + chunkgenerator = worlddimension.generator(); diff --git a/patches/server/0025-Completely-remove-bootstrapExecutor.patch b/patches/server/0025-Completely-remove-bootstrapExecutor.patch new file mode 100644 index 0000000..36aef85 --- /dev/null +++ b/patches/server/0025-Completely-remove-bootstrapExecutor.patch @@ -0,0 +1,56 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Etil <81570777+etil2jz@users.noreply.github.com> +Date: Thu, 9 Dec 2021 17:10:22 +0100 +Subject: [PATCH] Completely remove bootstrapExecutor + +Original code by PatinaMC, licensed under GNU General Public License v3.0 +You can find the original code on https://github.com/PatinaMC/Patina + +diff --git a/src/main/java/net/minecraft/Util.java b/src/main/java/net/minecraft/Util.java +index 32a4b7554677609691e23f833a5ec76d1d437ff0..13085533dba61cf526cf77bac44e32358b592c7e 100644 +--- a/src/main/java/net/minecraft/Util.java ++++ b/src/main/java/net/minecraft/Util.java +@@ -67,7 +67,7 @@ public class Util { + private static final int DEFAULT_MAX_THREADS = 255; + private static final String MAX_THREADS_SYSTEM_PROPERTY = "max.bg.threads"; + private static final AtomicInteger WORKER_COUNT = new AtomicInteger(1); +- private static final ExecutorService BOOTSTRAP_EXECUTOR = makeExecutor("Bootstrap", -2); // Paper - add -2 priority ++ //private static final ExecutorService BOOTSTRAP_EXECUTOR = makeExecutor("Bootstrap", -2); // Paper - add -2 priority // Patina + private static final ExecutorService BACKGROUND_EXECUTOR = makeExecutor("Main", -1); // Paper - add -1 priority + // Paper start - don't submit BLOCKING PROFILE LOOKUPS to the world gen thread + public static final ExecutorService PROFILE_EXECUTOR = Executors.newFixedThreadPool(2, new java.util.concurrent.ThreadFactory() { +@@ -183,9 +183,11 @@ public class Util { + return 255; + } + ++ /* // Patina + public static ExecutorService bootstrapExecutor() { + return BOOTSTRAP_EXECUTOR; + } ++ */ + + public static ExecutorService backgroundExecutor() { + return BACKGROUND_EXECUTOR; +diff --git a/src/main/java/net/minecraft/util/datafix/DataFixers.java b/src/main/java/net/minecraft/util/datafix/DataFixers.java +index b08d32bc80b4a65ebb980366a3e717c3b0e1bdab..9e31e12203dcfa0836e38ee32bad33bf34caa688 100644 +--- a/src/main/java/net/minecraft/util/datafix/DataFixers.java ++++ b/src/main/java/net/minecraft/util/datafix/DataFixers.java +@@ -78,15 +78,15 @@ public class DataFixers { + + private static final BiFunction SAME = Schema::new; + private static final BiFunction SAME_NAMESPACED = NamespacedSchema::new; +- private static final DataFixer DATA_FIXER = DataFixers.createFixerUpper(); ++ private static final DataFixer DATA_FIXER; // Patina + + public DataFixers() {} + +- private static DataFixer createFixerUpper() { ++ static { // Patina + DataFixerBuilder datafixerbuilder = new DataFixerBuilder(SharedConstants.getCurrentVersion().getWorldVersion()); + + DataFixers.addFixers(datafixerbuilder); +- return datafixerbuilder.build(Util.bootstrapExecutor()); ++ DATA_FIXER = datafixerbuilder.build(Util.bootstrapExecutor()); // Patina + } + + public static DataFixer getDataFixer() {