diff --git a/Spigot-API-Patches/0004-Timings-v2.patch b/Spigot-API-Patches/0004-Timings-v2.patch index 9a84b8a771..e8093dba75 100644 --- a/Spigot-API-Patches/0004-Timings-v2.patch +++ b/Spigot-API-Patches/0004-Timings-v2.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Timings v2 diff --git a/src/main/java/co/aikar/timings/FullServerTickHandler.java b/src/main/java/co/aikar/timings/FullServerTickHandler.java new file mode 100644 -index 0000000000000000000000000000000000000000..65ff5403cbc894215089626450252a050dd13119 +index 0000000000000000000000000000000000000000..dfaa266ff53e43ad48dc5a5a5657fe70600f539a --- /dev/null +++ b/src/main/java/co/aikar/timings/FullServerTickHandler.java @@ -0,0 +1,85 @@ @@ -88,7 +88,7 @@ index 0000000000000000000000000000000000000000..65ff5403cbc894215089626450252a05 + TimingsManager.HISTORY.add(new TimingHistory()); + TimingsManager.resetTimings(); + } -+ TimingsExport.reportTimings(); ++ Bukkit.getUnsafe().reportTimings(); + } + + boolean isViolated() { @@ -1255,10 +1255,10 @@ index 0000000000000000000000000000000000000000..df142a89b8c43acb81eb383eac0ef048 +} diff --git a/src/main/java/co/aikar/timings/Timings.java b/src/main/java/co/aikar/timings/Timings.java new file mode 100644 -index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5196f82b9 +index 0000000000000000000000000000000000000000..da76e1aaee1dee794e38ddd4e0a28e0071e90bbf --- /dev/null +++ b/src/main/java/co/aikar/timings/Timings.java -@@ -0,0 +1,293 @@ +@@ -0,0 +1,296 @@ +/* + * This file is licensed under the MIT License (MIT). + * @@ -1286,11 +1286,13 @@ index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5 + +import com.google.common.base.Preconditions; +import com.google.common.collect.EvictingQueue; ++import com.google.common.collect.Lists; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.Plugin; + ++import java.util.List; +import java.util.Queue; +import java.util.logging.Level; +import org.jetbrains.annotations.NotNull; @@ -1299,6 +1301,7 @@ index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5 +@SuppressWarnings({"UnusedDeclaration", "WeakerAccess", "SameParameterValue"}) +public final class Timings { + ++ final static List requestingReport = Lists.newArrayList(); + private static final int MAX_HISTORY_FRAMES = 12; + public static final Timing NULL_HANDLER = new NullTimingHandler(); + static boolean timingsEnabled = false; @@ -1509,7 +1512,7 @@ index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5 + if (sender == null) { + sender = Bukkit.getConsoleSender(); + } -+ TimingsExport.requestingReport.add(sender); ++ requestingReport.add(sender); + } + + /** @@ -1519,7 +1522,7 @@ index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5 + */ + public static void generateReport(@NotNull TimingsReportListener sender) { + Validate.notNull(sender); -+ TimingsExport.requestingReport.add(sender); ++ requestingReport.add(sender); + } + + /* @@ -1554,10 +1557,10 @@ index 0000000000000000000000000000000000000000..0b34e0d0174b7d92a17699538d5f3fe5 +} diff --git a/src/main/java/co/aikar/timings/TimingsCommand.java b/src/main/java/co/aikar/timings/TimingsCommand.java new file mode 100644 -index 0000000000000000000000000000000000000000..c0d8f2016bbc0257c8eb4888508e8f50d7c4790f +index 0000000000000000000000000000000000000000..f7c2245a310a084367ff25db539b3c967d5cb141 --- /dev/null +++ b/src/main/java/co/aikar/timings/TimingsCommand.java -@@ -0,0 +1,122 @@ +@@ -0,0 +1,119 @@ +/* + * This file is licensed under the MIT License (MIT). + * @@ -1649,9 +1652,6 @@ index 0000000000000000000000000000000000000000..c0d8f2016bbc0257c8eb4888508e8f50 + lastResetAttempt = now; + sender.sendMessage(ChatColor.RED + "WARNING: Timings v2 should not be reset. If you are encountering lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds."); + } -+ -+ } else if ("cost".equals(arg)) { -+ sender.sendMessage("Timings cost: " + TimingsExport.getCost()); + } else if ( + "paste".equalsIgnoreCase(arg) || + "report".equalsIgnoreCase(arg) || @@ -1680,367 +1680,6 @@ index 0000000000000000000000000000000000000000..c0d8f2016bbc0257c8eb4888508e8f50 + return ImmutableList.of(); + } +} -diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java -new file mode 100644 -index 0000000000000000000000000000000000000000..93d5a3f97a1b2b3a1cd2731d48e8ebd01d29aa91 ---- /dev/null -+++ b/src/main/java/co/aikar/timings/TimingsExport.java -@@ -0,0 +1,355 @@ -+/* -+ * This file is licensed under the MIT License (MIT). -+ * -+ * Copyright (c) 2014 Daniel Ennis -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+package co.aikar.timings; -+ -+import com.google.common.collect.Lists; -+import com.google.common.collect.Sets; -+import org.apache.commons.lang.StringUtils; -+import org.bukkit.Bukkit; -+import org.bukkit.ChatColor; -+import org.bukkit.Material; -+import org.bukkit.command.CommandSender; -+import org.bukkit.configuration.ConfigurationSection; -+import org.bukkit.configuration.MemorySection; -+import org.bukkit.entity.EntityType; -+import org.json.simple.JSONObject; -+import org.json.simple.JSONValue; -+ -+import java.io.ByteArrayOutputStream; -+import java.io.IOException; -+import java.io.InputStream; -+import java.io.OutputStream; -+import java.lang.management.ManagementFactory; -+import java.lang.management.RuntimeMXBean; -+import java.net.HttpURLConnection; -+import java.net.InetAddress; -+import java.net.URL; -+import java.util.List; -+import java.util.Map; -+import java.util.Set; -+import java.util.logging.Level; -+import java.util.zip.GZIPOutputStream; -+ -+import static co.aikar.timings.TimingsManager.HISTORY; -+import static co.aikar.util.JSONUtil.appendObjectData; -+import static co.aikar.util.JSONUtil.createObject; -+import static co.aikar.util.JSONUtil.pair; -+import static co.aikar.util.JSONUtil.toArray; -+import static co.aikar.util.JSONUtil.toArrayMapper; -+import static co.aikar.util.JSONUtil.toObjectMapper; -+ -+@SuppressWarnings({"rawtypes", "SuppressionAnnotation"}) -+class TimingsExport extends Thread { -+ -+ private final TimingsReportListener listeners; -+ private final Map out; -+ private final TimingHistory[] history; -+ private static long lastReport = 0; -+ final static List requestingReport = Lists.newArrayList(); -+ -+ private TimingsExport(TimingsReportListener listeners, Map out, TimingHistory[] history) { -+ super("Timings paste thread"); -+ this.listeners = listeners; -+ this.out = out; -+ this.history = history; -+ } -+ -+ /** -+ * Checks if any pending reports are being requested, and builds one if needed. -+ */ -+ static void reportTimings() { -+ if (requestingReport.isEmpty()) { -+ return; -+ } -+ TimingsReportListener listeners = new TimingsReportListener(requestingReport); -+ listeners.addConsoleIfNeeded(); -+ -+ requestingReport.clear(); -+ long now = System.currentTimeMillis(); -+ final long lastReportDiff = now - lastReport; -+ if (lastReportDiff < 60000) { -+ listeners.sendMessage(ChatColor.RED + "Please wait at least 1 minute in between Timings reports. (" + (int)((60000 - lastReportDiff) / 1000) + " seconds)"); -+ listeners.done(); -+ return; -+ } -+ final long lastStartDiff = now - TimingsManager.timingStart; -+ if (lastStartDiff < 180000) { -+ listeners.sendMessage(ChatColor.RED + "Please wait at least 3 minutes before generating a Timings report. Unlike Timings v1, v2 benefits from longer timings and is not as useful with short timings. (" + (int)((180000 - lastStartDiff) / 1000) + " seconds)"); -+ listeners.done(); -+ return; -+ } -+ listeners.sendMessage(ChatColor.GREEN + "Preparing Timings Report..."); -+ lastReport = now; -+ Map parent = createObject( -+ // Get some basic system details about the server -+ pair("version", Bukkit.getVersion()), -+ pair("maxplayers", Bukkit.getMaxPlayers()), -+ pair("start", TimingsManager.timingStart / 1000), -+ pair("end", System.currentTimeMillis() / 1000), -+ pair("sampletime", (System.currentTimeMillis() - TimingsManager.timingStart) / 1000) -+ ); -+ if (!TimingsManager.privacy) { -+ appendObjectData(parent, -+ pair("server", Bukkit.getUnsafe().getTimingsServerName()), -+ pair("motd", Bukkit.getServer().getMotd()), -+ pair("online-mode", Bukkit.getServer().getOnlineMode()), -+ pair("icon", Bukkit.getServer().getServerIcon().getData()) -+ ); -+ } -+ -+ final Runtime runtime = Runtime.getRuntime(); -+ RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); -+ -+ parent.put("system", createObject( -+ pair("timingcost", getCost()), -+ pair("name", System.getProperty("os.name")), -+ pair("version", System.getProperty("os.version")), -+ pair("jvmversion", System.getProperty("java.version")), -+ pair("arch", System.getProperty("os.arch")), -+ pair("maxmem", runtime.maxMemory()), -+ pair("cpu", runtime.availableProcessors()), -+ pair("runtime", ManagementFactory.getRuntimeMXBean().getUptime()), -+ pair("flags", StringUtils.join(runtimeBean.getInputArguments(), " ")), -+ pair("gc", toObjectMapper(ManagementFactory.getGarbageCollectorMXBeans(), input -> pair(input.getName(), toArray(input.getCollectionCount(), input.getCollectionTime())))) -+ ) -+ ); -+ -+ Set tileEntityTypeSet = Sets.newHashSet(); -+ Set entityTypeSet = Sets.newHashSet(); -+ -+ int size = HISTORY.size(); -+ TimingHistory[] history = new TimingHistory[size + 1]; -+ int i = 0; -+ for (TimingHistory timingHistory : HISTORY) { -+ tileEntityTypeSet.addAll(timingHistory.tileEntityTypeSet); -+ entityTypeSet.addAll(timingHistory.entityTypeSet); -+ history[i++] = timingHistory; -+ } -+ -+ history[i] = new TimingHistory(); // Current snapshot -+ tileEntityTypeSet.addAll(history[i].tileEntityTypeSet); -+ entityTypeSet.addAll(history[i].entityTypeSet); -+ -+ -+ Map handlers = createObject(); -+ Map groupData; -+ synchronized (TimingIdentifier.GROUP_MAP) { -+ for (TimingIdentifier.TimingGroup group : TimingIdentifier.GROUP_MAP.values()) { -+ synchronized (group.handlers) { -+ for (TimingHandler id : group.handlers) { -+ -+ if (!id.isTimed() && !id.isSpecial()) { -+ continue; -+ } -+ -+ String name = id.identifier.name; -+ if (name.startsWith("##")) { -+ name = name.substring(3); -+ } -+ handlers.put(id.id, toArray( -+ group.id, -+ name -+ )); -+ } -+ } -+ } -+ -+ groupData = toObjectMapper( -+ TimingIdentifier.GROUP_MAP.values(), group -> pair(group.id, group.name)); -+ } -+ -+ parent.put("idmap", createObject( -+ pair("groups", groupData), -+ pair("handlers", handlers), -+ pair("worlds", toObjectMapper(TimingHistory.worldMap.entrySet(), input -> pair(input.getValue(), input.getKey()))), -+ pair("tileentity", -+ toObjectMapper(tileEntityTypeSet, input -> pair(input.ordinal(), input.name()))), -+ pair("entity", -+ toObjectMapper(entityTypeSet, input -> pair(input.ordinal(), input.name()))) -+ )); -+ -+ // Information about loaded plugins -+ -+ parent.put("plugins", toObjectMapper(Bukkit.getPluginManager().getPlugins(), -+ plugin -> pair(plugin.getName(), createObject( -+ pair("version", plugin.getDescription().getVersion()), -+ pair("description", String.valueOf(plugin.getDescription().getDescription()).trim()), -+ pair("website", plugin.getDescription().getWebsite()), -+ pair("authors", StringUtils.join(plugin.getDescription().getAuthors(), ", ")) -+ )))); -+ -+ -+ -+ // Information on the users Config -+ -+ parent.put("config", createObject( -+ pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), -+ pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), -+ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) -+ )); -+ -+ new TimingsExport(listeners, parent, history).start(); -+ } -+ -+ static long getCost() { -+ // Benchmark the users System.nanotime() for cost basis -+ int passes = 100; -+ TimingHandler SAMPLER1 = Timings.ofSafe("Timings Sampler 1"); -+ TimingHandler SAMPLER2 = Timings.ofSafe("Timings Sampler 2"); -+ TimingHandler SAMPLER3 = Timings.ofSafe("Timings Sampler 3"); -+ TimingHandler SAMPLER4 = Timings.ofSafe("Timings Sampler 4"); -+ TimingHandler SAMPLER5 = Timings.ofSafe("Timings Sampler 5"); -+ TimingHandler SAMPLER6 = Timings.ofSafe("Timings Sampler 6"); -+ -+ long start = System.nanoTime(); -+ for (int i = 0; i < passes; i++) { -+ SAMPLER1.startTiming(); -+ SAMPLER2.startTiming(); -+ SAMPLER3.startTiming(); -+ SAMPLER3.stopTiming(); -+ SAMPLER4.startTiming(); -+ SAMPLER5.startTiming(); -+ SAMPLER6.startTiming(); -+ SAMPLER6.stopTiming(); -+ SAMPLER5.stopTiming(); -+ SAMPLER4.stopTiming(); -+ SAMPLER2.stopTiming(); -+ SAMPLER1.stopTiming(); -+ } -+ long timingsCost = (System.nanoTime() - start) / passes / 6; -+ SAMPLER1.reset(true); -+ SAMPLER2.reset(true); -+ SAMPLER3.reset(true); -+ SAMPLER4.reset(true); -+ SAMPLER5.reset(true); -+ SAMPLER6.reset(true); -+ return timingsCost; -+ } -+ -+ private static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) { -+ -+ JSONObject object = new JSONObject(); -+ for (String key : config.getKeys(false)) { -+ String fullKey = (parentKey != null ? parentKey + "." + key : key); -+ if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey)) { -+ continue; -+ } -+ final Object val = config.get(key); -+ -+ object.put(key, valAsJSON(val, fullKey)); -+ } -+ return object; -+ } -+ -+ private static Object valAsJSON(Object val, final String parentKey) { -+ if (!(val instanceof MemorySection)) { -+ if (val instanceof List) { -+ Iterable v = (Iterable) val; -+ return toArrayMapper(v, input -> valAsJSON(input, parentKey)); -+ } else { -+ return String.valueOf(val); -+ } -+ } else { -+ return mapAsJSON((ConfigurationSection) val, parentKey); -+ } -+ } -+ -+ @Override -+ public void run() { -+ out.put("data", toArrayMapper(history, TimingHistory::export)); -+ -+ -+ String response = null; -+ String timingsURL = null; -+ try { -+ HttpURLConnection con = (HttpURLConnection) new URL("http://timings.aikar.co/post").openConnection(); -+ con.setDoOutput(true); -+ String hostName = "BrokenHost"; -+ try { -+ hostName = InetAddress.getLocalHost().getHostName(); -+ } catch (Exception ignored) {} -+ con.setRequestProperty("User-Agent", "Paper/" + Bukkit.getUnsafe().getTimingsServerName() + "/" + hostName); -+ con.setRequestMethod("POST"); -+ con.setInstanceFollowRedirects(false); -+ -+ OutputStream request = new GZIPOutputStream(con.getOutputStream()) {{ -+ this.def.setLevel(7); -+ }}; -+ -+ request.write(JSONValue.toJSONString(out).getBytes("UTF-8")); -+ request.close(); -+ -+ response = getResponse(con); -+ -+ if (con.getResponseCode() != 302) { -+ listeners.sendMessage( -+ ChatColor.RED + "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage()); -+ listeners.sendMessage(ChatColor.RED + "Check your logs for more information"); -+ if (response != null) { -+ Bukkit.getLogger().log(Level.SEVERE, response); -+ } -+ return; -+ } -+ -+ timingsURL = con.getHeaderField("Location"); -+ listeners.sendMessage(ChatColor.GREEN + "View Timings Report: " + timingsURL); -+ -+ if (response != null && !response.isEmpty()) { -+ Bukkit.getLogger().log(Level.INFO, "Timing Response: " + response); -+ } -+ } catch (IOException ex) { -+ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); -+ if (response != null) { -+ Bukkit.getLogger().log(Level.SEVERE, response); -+ } -+ Bukkit.getLogger().log(Level.SEVERE, "Could not paste timings", ex); -+ } finally { -+ this.listeners.done(timingsURL); -+ } -+ } -+ -+ private String getResponse(HttpURLConnection con) throws IOException { -+ InputStream is = null; -+ try { -+ is = con.getInputStream(); -+ ByteArrayOutputStream bos = new ByteArrayOutputStream(); -+ -+ byte[] b = new byte[1024]; -+ int bytesRead; -+ while ((bytesRead = is.read(b)) != -1) { -+ bos.write(b, 0, bytesRead); -+ } -+ return bos.toString(); -+ -+ } catch (IOException ex) { -+ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); -+ Bukkit.getLogger().log(Level.WARNING, con.getResponseMessage(), ex); -+ return null; -+ } finally { -+ if (is != null) { -+ is.close(); -+ } -+ } -+ } -+} diff --git a/src/main/java/co/aikar/timings/TimingsManager.java b/src/main/java/co/aikar/timings/TimingsManager.java new file mode 100644 index 0000000000000000000000000000000000000000..ef824d701c97cad8b31e76ad98c94fc4367a7eda @@ -3182,10 +2821,18 @@ index f6fb72fab398ae8ca8b746154ff3c8fcad378faf..fad4e929264e2be534d3c4a90a5d557f * Sends the component to the player * diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java -index 247d194f86c00db11acbc58e7d163b2606db4f07..72c5501e8503aa3b5564a0467fde270d7cd93492 100644 +index 247d194f86c00db11acbc58e7d163b2606db4f07..26dbc2a761108d6aad056a786cb5146c63b7ad3d 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java -@@ -69,4 +69,12 @@ public interface UnsafeValues { +@@ -18,6 +18,7 @@ import org.bukkit.plugin.PluginDescriptionFile; + @Deprecated + public interface UnsafeValues { + ++ void reportTimings(); // Paper + Material toLegacy(Material material); + + Material fromLegacy(Material material); +@@ -69,4 +70,12 @@ public interface UnsafeValues { * @return true if a file matching this key was found and deleted */ boolean removeAdvancement(NamespacedKey key); diff --git a/Spigot-API-Patches/0008-Version-Command-2.0.patch b/Spigot-API-Patches/0008-Version-Command-2.0.patch index 52b45a01fb..30c0dac026 100644 --- a/Spigot-API-Patches/0008-Version-Command-2.0.patch +++ b/Spigot-API-Patches/0008-Version-Command-2.0.patch @@ -55,10 +55,10 @@ index 0000000000000000000000000000000000000000..2a2651299e8dc631938ba4b4078dc694 + } +} diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java -index 72c5501e8503aa3b5564a0467fde270d7cd93492..c0ff133dece238297bbdf4cb938c9a3070783ec4 100644 +index 26dbc2a761108d6aad056a786cb5146c63b7ad3d..98df287bcb1462fa42b7d4a706a68d36927fbc1c 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java -@@ -76,5 +76,12 @@ public interface UnsafeValues { +@@ -77,5 +77,12 @@ public interface UnsafeValues { * @return name */ String getTimingsServerName(); diff --git a/Spigot-API-Patches/0118-Add-an-asterisk-to-legacy-API-plugins.patch b/Spigot-API-Patches/0118-Add-an-asterisk-to-legacy-API-plugins.patch index adfac8a99f..ed8e05eb0c 100644 --- a/Spigot-API-Patches/0118-Add-an-asterisk-to-legacy-API-plugins.patch +++ b/Spigot-API-Patches/0118-Add-an-asterisk-to-legacy-API-plugins.patch @@ -7,10 +7,10 @@ Not here to name and shame, only so server admins can be aware of which plugins have and haven't been updated. diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java -index c0ff133dece238297bbdf4cb938c9a3070783ec4..821064264405cde147e944df279b44a20a4868ad 100644 +index 98df287bcb1462fa42b7d4a706a68d36927fbc1c..6f5601639c472db62120bab8d903620ee87f7e5c 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java -@@ -83,5 +83,11 @@ public interface UnsafeValues { +@@ -84,5 +84,11 @@ public interface UnsafeValues { default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher(); } diff --git a/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch b/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch index e595bcf9dc..e309f0c798 100644 --- a/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch +++ b/Spigot-API-Patches/0208-Add-Raw-Byte-ItemStack-Serialization.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Add Raw Byte ItemStack Serialization Serializes using NBT which is safer for server data migrations than bukkits format. diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java -index 821064264405cde147e944df279b44a20a4868ad..4fa00140cf17e3ea7d602b6337b3fe4f2901202e 100644 +index 6f5601639c472db62120bab8d903620ee87f7e5c..70d95e89a050632b2b0e2f477d3c286a3e8db1bd 100644 --- a/src/main/java/org/bukkit/UnsafeValues.java +++ b/src/main/java/org/bukkit/UnsafeValues.java -@@ -89,5 +89,9 @@ public interface UnsafeValues { +@@ -90,5 +90,9 @@ public interface UnsafeValues { static boolean isLegacyPlugin(org.bukkit.plugin.Plugin plugin) { return !Bukkit.getUnsafe().isSupportedApiVersion(plugin.getDescription().getAPIVersion()); } diff --git a/Spigot-Server-Patches/0009-Timings-v2.patch b/Spigot-Server-Patches/0009-Timings-v2.patch index 4c397fec6a..dd12ff299c 100644 --- a/Spigot-Server-Patches/0009-Timings-v2.patch +++ b/Spigot-Server-Patches/0009-Timings-v2.patch @@ -6,10 +6,10 @@ Subject: [PATCH] Timings v2 diff --git a/src/main/java/co/aikar/timings/MinecraftTimings.java b/src/main/java/co/aikar/timings/MinecraftTimings.java new file mode 100644 -index 0000000000000000000000000000000000000000..223d3b1125d0781758c45c6b469e6cccd13f187a +index 0000000000000000000000000000000000000000..33342139a042ebec5fb4e8566028f8f7e3149937 --- /dev/null +++ b/src/main/java/co/aikar/timings/MinecraftTimings.java -@@ -0,0 +1,141 @@ +@@ -0,0 +1,145 @@ +package co.aikar.timings; + +import com.google.common.collect.MapMaker; @@ -81,9 +81,13 @@ index 0000000000000000000000000000000000000000..223d3b1125d0781758c45c6b469e6ccc + + final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz -> { + try { -+ return clazz.isAnonymousClass() || clazz.isLocalClass() -+ ? clazz.getName() -+ : clazz.getCanonicalName(); ++ String clsName = clazz.isAnonymousClass() || clazz.isLocalClass() ++ ? clazz.getName() ++ : clazz.getCanonicalName(); ++ if (clsName.contains("$Lambda$")) { ++ clsName = clsName.replaceAll("(Lambda\\$.*?)/.*", "$1"); ++ } ++ return clsName; + } catch (Throwable ex) { + new Exception("Error occurred detecting class name", ex).printStackTrace(); + return "MangledClassFile"; @@ -151,6 +155,389 @@ index 0000000000000000000000000000000000000000..223d3b1125d0781758c45c6b469e6ccc + return Timings.ofSafe("Command Function - " + function.getMinecraftKey().toString()); + } +} +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +new file mode 100644 +index 0000000000000000000000000000000000000000..d4ebcf8f66197299256bd6b65710a1488c90ea41 +--- /dev/null ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -0,0 +1,377 @@ ++/* ++ * This file is licensed under the MIT License (MIT). ++ * ++ * Copyright (c) 2014 Daniel Ennis ++ * ++ * Permission is hereby granted, free of charge, to any person obtaining a copy ++ * of this software and associated documentation files (the "Software"), to deal ++ * in the Software without restriction, including without limitation the rights ++ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++ * copies of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be included in ++ * all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ++ * THE SOFTWARE. ++ */ ++package co.aikar.timings; ++ ++import com.google.common.collect.Sets; ++import net.minecraft.server.MinecraftServer; ++import org.apache.commons.lang.StringUtils; ++import org.bukkit.Bukkit; ++import org.bukkit.ChatColor; ++import org.bukkit.Material; ++import org.bukkit.configuration.ConfigurationSection; ++import org.bukkit.configuration.MemorySection; ++import org.bukkit.craftbukkit.util.CraftChatMessage; ++import org.bukkit.entity.EntityType; ++import org.json.simple.JSONObject; ++import org.json.simple.JSONValue; ++ ++import java.io.ByteArrayOutputStream; ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.OutputStream; ++import java.lang.management.ManagementFactory; ++import java.lang.management.OperatingSystemMXBean; ++import java.lang.management.RuntimeMXBean; ++import java.net.HttpURLConnection; ++import java.net.InetAddress; ++import java.net.URL; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++import java.util.logging.Level; ++import java.util.zip.GZIPOutputStream; ++ ++import static co.aikar.timings.TimingsManager.HISTORY; ++import static co.aikar.util.JSONUtil.appendObjectData; ++import static co.aikar.util.JSONUtil.createObject; ++import static co.aikar.util.JSONUtil.pair; ++import static co.aikar.util.JSONUtil.toArray; ++import static co.aikar.util.JSONUtil.toArrayMapper; ++import static co.aikar.util.JSONUtil.toObjectMapper; ++ ++@SuppressWarnings({"rawtypes", "SuppressionAnnotation"}) ++public class TimingsExport extends Thread { ++ ++ private final TimingsReportListener listeners; ++ private final Map out; ++ private final TimingHistory[] history; ++ private static long lastReport = 0; ++ ++ private TimingsExport(TimingsReportListener listeners, Map out, TimingHistory[] history) { ++ super("Timings paste thread"); ++ this.listeners = listeners; ++ this.out = out; ++ this.history = history; ++ } ++ ++ /** ++ * Checks if any pending reports are being requested, and builds one if needed. ++ */ ++ public static void reportTimings() { ++ if (Timings.requestingReport.isEmpty()) { ++ return; ++ } ++ TimingsReportListener listeners = new TimingsReportListener(Timings.requestingReport); ++ listeners.addConsoleIfNeeded(); ++ ++ Timings.requestingReport.clear(); ++ long now = System.currentTimeMillis(); ++ final long lastReportDiff = now - lastReport; ++ if (lastReportDiff < 60000) { ++ listeners.sendMessage(ChatColor.RED + "Please wait at least 1 minute in between Timings reports. (" + (int)((60000 - lastReportDiff) / 1000) + " seconds)"); ++ listeners.done(); ++ return; ++ } ++ final long lastStartDiff = now - TimingsManager.timingStart; ++ if (lastStartDiff < 180000) { ++ listeners.sendMessage(ChatColor.RED + "Please wait at least 3 minutes before generating a Timings report. Unlike Timings v1, v2 benefits from longer timings and is not as useful with short timings. (" + (int)((180000 - lastStartDiff) / 1000) + " seconds)"); ++ listeners.done(); ++ return; ++ } ++ listeners.sendMessage(ChatColor.GREEN + "Preparing Timings Report..."); ++ lastReport = now; ++ Map parent = createObject( ++ // Get some basic system details about the server ++ pair("version", Bukkit.getVersion()), ++ pair("maxplayers", Bukkit.getMaxPlayers()), ++ pair("start", TimingsManager.timingStart / 1000), ++ pair("end", System.currentTimeMillis() / 1000), ++ pair("online-mode", Bukkit.getServer().getOnlineMode()), ++ pair("sampletime", (System.currentTimeMillis() - TimingsManager.timingStart) / 1000), ++ pair("datapacks", toArrayMapper(MinecraftServer.getServer().getResourcePackRepository().d(), pack -> { ++ // Don't feel like obf helper'ing these, non fatal if its temp missed. ++ return ChatColor.stripColor(CraftChatMessage.fromComponent(pack.a(true))); ++ })) ++ ); ++ if (!TimingsManager.privacy) { ++ appendObjectData(parent, ++ pair("server", Bukkit.getUnsafe().getTimingsServerName()), ++ pair("motd", Bukkit.getServer().getMotd()), ++ pair("icon", Bukkit.getServer().getServerIcon().getData()) ++ ); ++ } ++ ++ final Runtime runtime = Runtime.getRuntime(); ++ RuntimeMXBean runtimeBean = ManagementFactory.getRuntimeMXBean(); ++ ++ OperatingSystemMXBean osInfo = ManagementFactory.getOperatingSystemMXBean(); ++ ++ parent.put("system", createObject( ++ pair("timingcost", getCost()), ++ pair("loadavg", osInfo.getSystemLoadAverage()), ++ pair("name", System.getProperty("os.name")), ++ pair("version", System.getProperty("os.version")), ++ pair("jvmversion", System.getProperty("java.version")), ++ pair("arch", System.getProperty("os.arch")), ++ pair("maxmem", runtime.maxMemory()), ++ pair("memory", createObject( ++ pair("heap", ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().toString()), ++ pair("nonheap", ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().toString()), ++ pair("finalizing", ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount()) ++ )), ++ pair("cpu", runtime.availableProcessors()), ++ pair("runtime", runtimeBean.getUptime()), ++ pair("flags", StringUtils.join(runtimeBean.getInputArguments(), " ")), ++ pair("gc", toObjectMapper(ManagementFactory.getGarbageCollectorMXBeans(), input -> pair(input.getName(), toArray(input.getCollectionCount(), input.getCollectionTime())))) ++ ) ++ ); ++ ++ parent.put("worlds", toObjectMapper(MinecraftServer.getServer().getWorlds(), world -> { ++ if (world.getWorldData().getName().equals("worldeditregentempworld")) return null; ++ return pair(world.getWorldData().getName(), createObject( ++ pair("gamerules", toObjectMapper(world.getWorld().getGameRules(), rule -> { ++ return pair(rule, world.getWorld().getGameRuleValue(rule)); ++ })), ++ pair("ticking-distance", world.getChunkProvider().playerChunkMap.getEffectiveViewDistance()) ++ )); ++ })); ++ ++ Set tileEntityTypeSet = Sets.newHashSet(); ++ Set entityTypeSet = Sets.newHashSet(); ++ ++ int size = HISTORY.size(); ++ TimingHistory[] history = new TimingHistory[size + 1]; ++ int i = 0; ++ for (TimingHistory timingHistory : HISTORY) { ++ tileEntityTypeSet.addAll(timingHistory.tileEntityTypeSet); ++ entityTypeSet.addAll(timingHistory.entityTypeSet); ++ history[i++] = timingHistory; ++ } ++ ++ history[i] = new TimingHistory(); // Current snapshot ++ tileEntityTypeSet.addAll(history[i].tileEntityTypeSet); ++ entityTypeSet.addAll(history[i].entityTypeSet); ++ ++ ++ Map handlers = createObject(); ++ Map groupData; ++ synchronized (TimingIdentifier.GROUP_MAP) { ++ for (TimingIdentifier.TimingGroup group : TimingIdentifier.GROUP_MAP.values()) { ++ synchronized (group.handlers) { ++ for (TimingHandler id : group.handlers) { ++ ++ if (!id.isTimed() && !id.isSpecial()) { ++ continue; ++ } ++ ++ String name = id.identifier.name; ++ if (name.startsWith("##")) { ++ name = name.substring(3); ++ } ++ handlers.put(id.id, toArray( ++ group.id, ++ name ++ )); ++ } ++ } ++ } ++ ++ groupData = toObjectMapper( ++ TimingIdentifier.GROUP_MAP.values(), group -> pair(group.id, group.name)); ++ } ++ ++ parent.put("idmap", createObject( ++ pair("groups", groupData), ++ pair("handlers", handlers), ++ pair("worlds", toObjectMapper(TimingHistory.worldMap.entrySet(), input -> pair(input.getValue(), input.getKey()))), ++ pair("tileentity", ++ toObjectMapper(tileEntityTypeSet, input -> pair(input.ordinal(), input.name()))), ++ pair("entity", ++ toObjectMapper(entityTypeSet, input -> pair(input.ordinal(), input.name()))) ++ )); ++ ++ // Information about loaded plugins ++ ++ parent.put("plugins", toObjectMapper(Bukkit.getPluginManager().getPlugins(), ++ plugin -> pair(plugin.getName(), createObject( ++ pair("version", plugin.getDescription().getVersion()), ++ pair("description", String.valueOf(plugin.getDescription().getDescription()).trim()), ++ pair("website", plugin.getDescription().getWebsite()), ++ pair("authors", StringUtils.join(plugin.getDescription().getAuthors(), ", ")) ++ )))); ++ ++ ++ ++ // Information on the users Config ++ ++ parent.put("config", createObject( ++ pair("spigot", mapAsJSON(Bukkit.spigot().getSpigotConfig(), null)), ++ pair("bukkit", mapAsJSON(Bukkit.spigot().getBukkitConfig(), null)), ++ pair("paper", mapAsJSON(Bukkit.spigot().getPaperConfig(), null)) ++ )); ++ ++ new TimingsExport(listeners, parent, history).start(); ++ } ++ ++ static long getCost() { ++ // Benchmark the users System.nanotime() for cost basis ++ int passes = 100; ++ TimingHandler SAMPLER1 = Timings.ofSafe("Timings Sampler 1"); ++ TimingHandler SAMPLER2 = Timings.ofSafe("Timings Sampler 2"); ++ TimingHandler SAMPLER3 = Timings.ofSafe("Timings Sampler 3"); ++ TimingHandler SAMPLER4 = Timings.ofSafe("Timings Sampler 4"); ++ TimingHandler SAMPLER5 = Timings.ofSafe("Timings Sampler 5"); ++ TimingHandler SAMPLER6 = Timings.ofSafe("Timings Sampler 6"); ++ ++ long start = System.nanoTime(); ++ for (int i = 0; i < passes; i++) { ++ SAMPLER1.startTiming(); ++ SAMPLER2.startTiming(); ++ SAMPLER3.startTiming(); ++ SAMPLER3.stopTiming(); ++ SAMPLER4.startTiming(); ++ SAMPLER5.startTiming(); ++ SAMPLER6.startTiming(); ++ SAMPLER6.stopTiming(); ++ SAMPLER5.stopTiming(); ++ SAMPLER4.stopTiming(); ++ SAMPLER2.stopTiming(); ++ SAMPLER1.stopTiming(); ++ } ++ long timingsCost = (System.nanoTime() - start) / passes / 6; ++ SAMPLER1.reset(true); ++ SAMPLER2.reset(true); ++ SAMPLER3.reset(true); ++ SAMPLER4.reset(true); ++ SAMPLER5.reset(true); ++ SAMPLER6.reset(true); ++ return timingsCost; ++ } ++ ++ private static JSONObject mapAsJSON(ConfigurationSection config, String parentKey) { ++ ++ JSONObject object = new JSONObject(); ++ for (String key : config.getKeys(false)) { ++ String fullKey = (parentKey != null ? parentKey + "." + key : key); ++ if (fullKey.equals("database") || fullKey.equals("settings.bungeecord-addresses") || TimingsManager.hiddenConfigs.contains(fullKey) || key.startsWith("seed-") || key.equals("worldeditregentempworld")) { ++ continue; ++ } ++ final Object val = config.get(key); ++ ++ object.put(key, valAsJSON(val, fullKey)); ++ } ++ return object; ++ } ++ ++ private static Object valAsJSON(Object val, final String parentKey) { ++ if (!(val instanceof MemorySection)) { ++ if (val instanceof List) { ++ Iterable v = (Iterable) val; ++ return toArrayMapper(v, input -> valAsJSON(input, parentKey)); ++ } else { ++ return String.valueOf(val); ++ } ++ } else { ++ return mapAsJSON((ConfigurationSection) val, parentKey); ++ } ++ } ++ ++ @Override ++ public void run() { ++ out.put("data", toArrayMapper(history, TimingHistory::export)); ++ ++ ++ String response = null; ++ String timingsURL = null; ++ try { ++ HttpURLConnection con = (HttpURLConnection) new URL("http://timings.aikar.co/post").openConnection(); ++ con.setDoOutput(true); ++ String hostName = "BrokenHost"; ++ try { ++ hostName = InetAddress.getLocalHost().getHostName(); ++ } catch (Exception ignored) {} ++ con.setRequestProperty("User-Agent", "Paper/" + Bukkit.getUnsafe().getTimingsServerName() + "/" + hostName); ++ con.setRequestMethod("POST"); ++ con.setInstanceFollowRedirects(false); ++ ++ OutputStream request = new GZIPOutputStream(con.getOutputStream()) {{ ++ this.def.setLevel(7); ++ }}; ++ ++ request.write(JSONValue.toJSONString(out).getBytes("UTF-8")); ++ request.close(); ++ ++ response = getResponse(con); ++ ++ if (con.getResponseCode() != 302) { ++ listeners.sendMessage( ++ ChatColor.RED + "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage()); ++ listeners.sendMessage(ChatColor.RED + "Check your logs for more information"); ++ if (response != null) { ++ Bukkit.getLogger().log(Level.SEVERE, response); ++ } ++ return; ++ } ++ ++ timingsURL = con.getHeaderField("Location"); ++ listeners.sendMessage(ChatColor.GREEN + "View Timings Report: " + timingsURL); ++ ++ if (response != null && !response.isEmpty()) { ++ Bukkit.getLogger().log(Level.INFO, "Timing Response: " + response); ++ } ++ } catch (IOException ex) { ++ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); ++ if (response != null) { ++ Bukkit.getLogger().log(Level.SEVERE, response); ++ } ++ Bukkit.getLogger().log(Level.SEVERE, "Could not paste timings", ex); ++ } finally { ++ this.listeners.done(timingsURL); ++ } ++ } ++ ++ private String getResponse(HttpURLConnection con) throws IOException { ++ InputStream is = null; ++ try { ++ is = con.getInputStream(); ++ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ++ ++ byte[] b = new byte[1024]; ++ int bytesRead; ++ while ((bytesRead = is.read(b)) != -1) { ++ bos.write(b, 0, bytesRead); ++ } ++ return bos.toString(); ++ ++ } catch (IOException ex) { ++ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); ++ Bukkit.getLogger().log(Level.WARNING, con.getResponseMessage(), ex); ++ return null; ++ } finally { ++ if (is != null) { ++ is.close(); ++ } ++ } ++ } ++} diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..fa1c920ea6092259149f9e7f9cd7cc1ed27bf338 @@ -1204,7 +1591,7 @@ index 820180ab3f7053c348caa80cc21f15dfa3d26afd..fa6400dccd4df635d696e0858c0c164a private static final CraftPersistentDataTypeRegistry DATA_TYPE_REGISTRY = new CraftPersistentDataTypeRegistry(); public CraftPersistentDataContainer persistentDataContainer; diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index d9ff52eda71fcdf88d4c9114450237bcba2ccb08..740c59004514a332ebe556c4e91f84bd9bf4a11d 100644 +index 45ded8cd3aee69083f6196765208d8c2430f0a02..a126a6d22ccee90d0aadb8513bda60455a703bf6 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -1,5 +1,7 @@ @@ -1791,10 +2178,23 @@ index e52ef47b783785dc214746b678e7b549aea9a274..3d90b3426873a3528af14f7f1ab0adae this.value = value; } diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 68728b4d86c4037fb1907bd16d86df5e23e8fe77..02f33005335a0995ce4157088353da93833b6ecc 100644 +index 68728b4d86c4037fb1907bd16d86df5e23e8fe77..f647450b8c6977b4a6bb1819b34052c4ae2fe0f4 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -298,6 +298,13 @@ public final class CraftMagicNumbers implements UnsafeValues { +@@ -133,6 +133,12 @@ public final class CraftMagicNumbers implements UnsafeValues { + return CraftNamespacedKey.toMinecraft(mat.getKey()); + } + // ======================================================================== ++ // Paper start ++ @Override ++ public void reportTimings() { ++ co.aikar.timings.TimingsExport.reportTimings(); ++ } ++ // Paper end + + public static byte toLegacyData(IBlockData data) { + return CraftLegacy.toLegacyData(data); +@@ -298,6 +304,13 @@ public final class CraftMagicNumbers implements UnsafeValues { return clazz; } diff --git a/Spigot-Server-Patches/0018-Implement-Paper-VersionChecker.patch b/Spigot-Server-Patches/0018-Implement-Paper-VersionChecker.patch index 7a45f32855..575f6d9361 100644 --- a/Spigot-Server-Patches/0018-Implement-Paper-VersionChecker.patch +++ b/Spigot-Server-Patches/0018-Implement-Paper-VersionChecker.patch @@ -126,10 +126,10 @@ index 0000000000000000000000000000000000000000..5deed3e25ff41ab0a4015a5fd0c1e952 + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 02f33005335a0995ce4157088353da93833b6ecc..b1e1b59d451674b42cdc6f896d3e2b707a03b923 100644 +index f647450b8c6977b4a6bb1819b34052c4ae2fe0f4..79ddf03c7f3afc868cb73ee4c000fc879ede90d8 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -303,6 +303,11 @@ public final class CraftMagicNumbers implements UnsafeValues { +@@ -309,6 +309,11 @@ public final class CraftMagicNumbers implements UnsafeValues { public String getTimingsServerName() { return com.destroystokyo.paper.PaperConfig.timingsServerName; } diff --git a/Spigot-Server-Patches/0410-Add-CraftMagicNumbers.isSupportedApiVersion.patch b/Spigot-Server-Patches/0410-Add-CraftMagicNumbers.isSupportedApiVersion.patch index b743ce7df5..e84521520d 100644 --- a/Spigot-Server-Patches/0410-Add-CraftMagicNumbers.isSupportedApiVersion.patch +++ b/Spigot-Server-Patches/0410-Add-CraftMagicNumbers.isSupportedApiVersion.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add CraftMagicNumbers.isSupportedApiVersion() diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index b1e1b59d451674b42cdc6f896d3e2b707a03b923..770375ed4207920e71d2d0799c611c4b3cdbe6f7 100644 +index 79ddf03c7f3afc868cb73ee4c000fc879ede90d8..6363074c38312387b62e6cf5df2592aefb078f29 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -308,6 +308,11 @@ public final class CraftMagicNumbers implements UnsafeValues { +@@ -314,6 +314,11 @@ public final class CraftMagicNumbers implements UnsafeValues { public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() { return new com.destroystokyo.paper.PaperVersionFetcher(); } diff --git a/Spigot-Server-Patches/0463-Add-Raw-Byte-ItemStack-Serialization.patch b/Spigot-Server-Patches/0463-Add-Raw-Byte-ItemStack-Serialization.patch index 2c9b37c187..290b76e00c 100644 --- a/Spigot-Server-Patches/0463-Add-Raw-Byte-ItemStack-Serialization.patch +++ b/Spigot-Server-Patches/0463-Add-Raw-Byte-ItemStack-Serialization.patch @@ -50,10 +50,10 @@ index 2322c0c8c5aacebb6317eab8ce4245554f6d9d55..3e6f878cdbbf5ebfa7fb390745ead135 DataOutputStream dataoutputstream = new DataOutputStream(new BufferedOutputStream(new GZIPOutputStream(outputstream))); Throwable throwable = null; diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -index 770375ed4207920e71d2d0799c611c4b3cdbe6f7..b58f2ef5ce5f3047bf8fcfc8d80a4aa9774ff711 100644 +index 6363074c38312387b62e6cf5df2592aefb078f29..004b6ce132f8a686116b2a03a0a57461c8e164cf 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java -@@ -313,6 +313,46 @@ public final class CraftMagicNumbers implements UnsafeValues { +@@ -319,6 +319,46 @@ public final class CraftMagicNumbers implements UnsafeValues { public boolean isSupportedApiVersion(String apiVersion) { return apiVersion != null && SUPPORTED_API.contains(apiVersion); } diff --git a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch index 73aeb9d39e..f8260ddb57 100644 --- a/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch +++ b/Spigot-Server-Patches/0505-No-Tick-view-distance-implementation.patch @@ -8,6 +8,20 @@ Implements world view distance getters/setters Per-Player is absent due to difficulty of maintaining the diff required to make it happen. +diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java +index d4ebcf8f66197299256bd6b65710a1488c90ea41..a3b41ce5fc70948d4804659a472cb622bb9bbc07 100644 +--- a/src/main/java/co/aikar/timings/TimingsExport.java ++++ b/src/main/java/co/aikar/timings/TimingsExport.java +@@ -153,7 +153,8 @@ public class TimingsExport extends Thread { + pair("gamerules", toObjectMapper(world.getWorld().getGameRules(), rule -> { + return pair(rule, world.getWorld().getGameRuleValue(rule)); + })), +- pair("ticking-distance", world.getChunkProvider().playerChunkMap.getEffectiveViewDistance()) ++ pair("ticking-distance", world.getChunkProvider().playerChunkMap.getEffectiveViewDistance()), ++ pair("notick-viewdistance", world.getChunkProvider().playerChunkMap.getEffectiveNoTickViewDistance()) + )); + })); + diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java index 4612697569fd6e3683b0e58453b61a9a8d077229..5c8a946d5c895fc2622c7df656cc462c58104cf7 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -539,7 +553,7 @@ index 74b868d7cec0260a10ca7718f48308f4ec54d56b..f832e7cdfc6741a932787f02754f1452 } } diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 60af90bf8c376ab8ab61b16ae38886149faa88cc..9b726de6daeba42120f3a948fbdcf080d0e72917 100644 +index 154b9f9a5696eaebb01dd59b7b27de75e95b501d..3061d856d52776ebc2eed6541238c8a760d7c536 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -151,7 +151,7 @@ public abstract class PlayerList { @@ -570,7 +584,7 @@ index 60af90bf8c376ab8ab61b16ae38886149faa88cc..9b726de6daeba42120f3a948fbdcf080 while (iterator.hasNext()) { diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 4dc03c27bba5cd2dd1d6cb2e82087ef834f41d81..e82b8b9a4dbc9a595df7d8e56596a93fde7b687a 100644 +index 9f4268202653f6f0ebed49cd67ae691a8b18ccd2..5535f98ac0ec1668b162cb652e88a122bcadac0c 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -444,8 +444,13 @@ public abstract class World implements GeneratorAccess, AutoCloseable {