reformat spigot package

This commit is contained in:
Lulu13022002
2024-12-16 19:22:36 +01:00
parent 7d29c678f7
commit 57c683647f
11 changed files with 559 additions and 1376 deletions

View File

@@ -2,16 +2,13 @@ package org.spigotmc;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
public class AsyncCatcher public class AsyncCatcher {
{
public static boolean enabled = true; public static boolean enabled = true;
public static void catchOp(String reason) public static void catchOp(String reason) {
{ if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) {
if ( AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread ) MinecraftServer.LOGGER.error("Thread {} failed main thread check: {}", Thread.currentThread().getName(), reason, new Throwable()); // Paper
{
MinecraftServer.LOGGER.error("Thread " + Thread.currentThread().getName() + " failed main thread check: " + reason, new Throwable()); // Paper
throw new IllegalStateException("Asynchronous " + reason + "!"); throw new IllegalStateException("Asynchronous " + reason + "!");
} }
} }

View File

@@ -5,34 +5,29 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import net.minecraft.nbt.NbtAccounter; import net.minecraft.nbt.NbtAccounter;
public class LimitStream extends FilterInputStream public class LimitStream extends FilterInputStream {
{
private final NbtAccounter limit; private final NbtAccounter limit;
public LimitStream(InputStream is, NbtAccounter limit) public LimitStream(InputStream is, NbtAccounter limit) {
{
super(is); super(is);
this.limit = limit; this.limit = limit;
} }
@Override @Override
public int read() throws IOException public int read() throws IOException {
{
this.limit.accountBytes(1); this.limit.accountBytes(1);
return super.read(); return super.read();
} }
@Override @Override
public int read(byte[] b) throws IOException public int read(byte[] b) throws IOException {
{
this.limit.accountBytes(b.length); this.limit.accountBytes(b.length);
return super.read(b); return super.read(b);
} }
@Override @Override
public int read(byte[] b, int off, int len) throws IOException public int read(byte[] b, int off, int len) throws IOException {
{
this.limit.accountBytes(len); this.limit.accountBytes(len);
return super.read(b, off, len); return super.read(b, off, len);
} }

View File

@@ -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;
/**
* <p> The metrics class obtains data about a plugin and submits statistics about it to the metrics backend. </p> <p>
* Public methods provided by this class: </p>
* <code>
* Graph createGraph(String name); <br/>
* void addCustomData(BukkitMetrics.Plotter plotter); <br/>
* void start(); <br/>
* </code>
*/
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<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
/**
* 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(this.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(this.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(this.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(!this.firstPost);
// After the first post we set firstPost to false
// Each post thereafter will be a ping
this.firstPost = false;
} catch (IOException e) {
if (Metrics.this.debug) {
Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage());
}
}
}
}, 0, TimeUnit.MINUTES.toMillis(Metrics.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(this.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(this.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(Metrics.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<Graph> 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", Metrics.CUSTOM_DATA_SEPARATOR, graph.getName(), Metrics.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(Metrics.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<Graph> 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;
}
}
/**
* <p>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:</p>
* <code>
* StringBuffer data = new StringBuffer();
* data.append(encode("guid")).append('=').append(encode(guid));
* encodeDataPair(data, "version", description.getVersion());
* </code>
*
* @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<Plotter> plotters = new LinkedHashSet<Plotter>();
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 <b>unmodifiable</b> set of the plotter objects in the graph
*
* @return an unmodifiable {@link java.util.Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(this.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(this.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(this.name) && plotter.getValue() == this.getValue();
}
}
}

View File

@@ -1,18 +1,16 @@
package org.spigotmc; package org.spigotmc;
import java.io.File; import java.io.File;
import java.util.List; import java.util.Locale;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.util.CraftChatMessage; import org.bukkit.craftbukkit.util.CraftChatMessage;
public class RestartCommand extends Command public class RestartCommand extends Command {
{
public RestartCommand(String name) public RestartCommand(String name) {
{
super(name); super(name);
this.description = "Restarts the server"; this.description = "Restarts the server";
this.usageMessage = "/restart"; this.usageMessage = "/restart";
@@ -20,39 +18,25 @@ public class RestartCommand extends Command
} }
@Override @Override
public boolean execute(CommandSender sender, String currentAlias, String[] args) public boolean execute(CommandSender sender, String currentAlias, String[] args) {
{ if (this.testPermission(sender)) {
if ( this.testPermission( sender ) ) MinecraftServer.getServer().processQueue.add(RestartCommand::restart);
{
MinecraftServer.getServer().processQueue.add( new Runnable()
{
@Override
public void run()
{
RestartCommand.restart();
}
} );
} }
return true; return true;
} }
public static void restart() public static void restart() {
{
RestartCommand.restart(SpigotConfig.restartScript); RestartCommand.restart(SpigotConfig.restartScript);
} }
private static void restart(final String restartScript) private static void restart(final String restartScript) {
{
AsyncCatcher.enabled = false; // Disable async catcher in case it interferes with us AsyncCatcher.enabled = false; // Disable async catcher in case it interferes with us
try try {
{
// Paper - extract method and cleanup // Paper - extract method and cleanup
boolean isRestarting = addShutdownHook(restartScript); boolean isRestarting = addShutdownHook(restartScript);
if ( isRestarting ) if (isRestarting) {
{
System.out.println("Attempting to restart with " + SpigotConfig.restartScript); System.out.println("Attempting to restart with " + SpigotConfig.restartScript);
} else } else {
{
System.out.println("Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server."); System.out.println("Startup script '" + SpigotConfig.restartScript + "' does not exist! Stopping server.");
} }
// Stop the watchdog // Stop the watchdog
@@ -60,56 +44,42 @@ public class RestartCommand extends Command
shutdownServer(isRestarting); shutdownServer(isRestarting);
// Paper end // Paper end
} catch ( Exception ex ) } catch (Exception ex) {
{
ex.printStackTrace(); ex.printStackTrace();
} }
} }
// Paper start - sync copied from above with minor changes, async added // Paper start - sync copied from above with minor changes, async added
private static void shutdownServer(boolean isRestarting) private static void shutdownServer(boolean isRestarting) {
{ if (MinecraftServer.getServer().isSameThread()) {
if ( MinecraftServer.getServer().isSameThread() )
{
// Kick all players // Kick all players
for ( ServerPlayer p : com.google.common.collect.ImmutableList.copyOf( MinecraftServer.getServer().getPlayerList().players ) ) for (ServerPlayer p : com.google.common.collect.ImmutableList.copyOf(MinecraftServer.getServer().getPlayerList().players)) {
{ p.connection.disconnect(CraftChatMessage.fromStringOrEmpty(SpigotConfig.restartMessage, true), org.bukkit.event.player.PlayerKickEvent.Cause.RESTART_COMMAND); // Paper - kick event reason (cause is never used)
p.connection.disconnect( CraftChatMessage.fromStringOrEmpty( SpigotConfig.restartMessage, true ), org.bukkit.event.player.PlayerKickEvent.Cause.RESTART_COMMAND); // Paper - kick event reason (cause is never used))
} }
// Give the socket a chance to send the packets // Give the socket a chance to send the packets
try try {
{
Thread.sleep(100); Thread.sleep(100);
} catch ( InterruptedException ex ) } catch (InterruptedException ex) {
{
} }
closeSocket(); closeSocket();
// Actually shutdown // Actually shutdown
try try {
{
MinecraftServer.getServer().close(); // calls stop() MinecraftServer.getServer().close(); // calls stop()
} catch ( Throwable t ) } catch (Throwable t) {
{
} }
// Actually stop the JVM // Actually stop the JVM
System.exit(0); System.exit(0);
} else {
} else
{
// Mark the server to shutdown at the end of the tick // Mark the server to shutdown at the end of the tick
MinecraftServer.getServer().safeShutdown(false, isRestarting); MinecraftServer.getServer().safeShutdown(false, isRestarting);
// wait 10 seconds to see if we're actually going to try shutdown // wait 10 seconds to see if we're actually going to try shutdown
try try {
{
Thread.sleep(10000); Thread.sleep(10000);
} } catch (InterruptedException ignored) {}
catch (InterruptedException ignored)
{
}
// Check if we've actually hit a state where the server is going to safely shutdown // Check if we've actually hit a state where the server is going to safely shutdown
// if we have, let the server stop as usual // if we have, let the server stop as usual
@@ -123,57 +93,40 @@ public class RestartCommand extends Command
// Paper end // Paper end
// Paper - Split from moved code // Paper - Split from moved code
private static void closeSocket() private static void closeSocket() {
{
// Close the socket so we can rebind with the new process // Close the socket so we can rebind with the new process
MinecraftServer.getServer().getConnection().stop(); MinecraftServer.getServer().getConnection().stop();
// Give time for it to kick in // Give time for it to kick in
try try {
{
Thread.sleep(100); Thread.sleep(100);
} catch ( InterruptedException ex ) } catch (InterruptedException ignored) {}
{
}
} }
// Paper end // Paper end
// Paper start - copied from above and modified to return if the hook registered // Paper start - copied from above and modified to return if the hook registered
private static boolean addShutdownHook(String restartScript) private static boolean addShutdownHook(String restartScript) {
{
String[] split = restartScript.split(" "); String[] split = restartScript.split(" ");
if ( split.length > 0 && new File( split[0] ).isFile() ) if (split.length > 0 && new File(split[0]).isFile()) {
{ Thread shutdownHook = new Thread(() -> {
Thread shutdownHook = new Thread() try {
{ String os = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
@Override if (os.contains("win")) {
public void run()
{
try
{
String os = System.getProperty( "os.name" ).toLowerCase(java.util.Locale.ENGLISH);
if ( os.contains( "win" ) )
{
Runtime.getRuntime().exec("cmd /c start " + restartScript); Runtime.getRuntime().exec("cmd /c start " + restartScript);
} else } else {
{
Runtime.getRuntime().exec("sh " + restartScript); Runtime.getRuntime().exec("sh " + restartScript);
} }
} catch ( Exception e ) } catch (Exception e) {
{
e.printStackTrace(); e.printStackTrace();
} }
} });
};
shutdownHook.setDaemon(true); shutdownHook.setDaemon(true);
Runtime.getRuntime().addShutdownHook(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownHook);
return true; return true;
} else } else {
{
return false; return false;
} }
} }
// Paper end // Paper end
} }

View File

@@ -1,12 +1,14 @@
package org.spigotmc; package org.spigotmc;
import java.io.File; import java.io.File;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import org.bukkit.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import static net.kyori.adventure.text.Component.text;
public class SpigotCommand extends Command { public class SpigotCommand extends Command {
public SpigotCommand(String name) { public SpigotCommand(String name) {
@@ -21,13 +23,17 @@ public class SpigotCommand extends Command {
if (!this.testPermission(sender)) return true; if (!this.testPermission(sender)) return true;
if (args.length != 1) { if (args.length != 1) {
sender.sendMessage(ChatColor.RED + "Usage: " + this.usageMessage); sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED));
return false; return false;
} }
if (args[0].equals("reload")) { if (args[0].equals("reload")) {
Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); Command.broadcastCommandMessage(sender, text().color(NamedTextColor.RED)
Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); .append(text("Please note that this command is not supported and may cause issues."))
.appendNewline()
.append(text("If you encounter any issues please use the /stop command to restart your server."))
.build()
);
MinecraftServer console = MinecraftServer.getServer(); MinecraftServer console = MinecraftServer.getServer();
org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings")); org.spigotmc.SpigotConfig.init((File) console.options.valueOf("spigot-settings"));
@@ -36,7 +42,7 @@ public class SpigotCommand extends Command {
} }
console.server.reloadCount++; console.server.reloadCount++;
Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Reload complete."); Command.broadcastCommandMessage(sender, text("Reload complete.", NamedTextColor.GREEN));
} }
return true; return true;

View File

@@ -28,39 +28,35 @@ import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
public class SpigotConfig public class SpigotConfig {
{
private static File CONFIG_FILE; private static File CONFIG_FILE;
private static final String HEADER = "This is the main configuration file for Spigot.\n" private static final String HEADER = """
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n" This is the main configuration file for Spigot.
+ "with caution, and make sure you know what each option does before configuring.\n" As you can see, there's tons to configure. Some options may impact gameplay, so use
+ "For a reference for any variable inside this file, check out the Spigot wiki at\n" with caution, and make sure you know what each option does before configuring.
+ "http://www.spigotmc.org/wiki/spigot-configuration/\n" For a reference for any variable inside this file, check out the Spigot wiki at
+ "\n" http://www.spigotmc.org/wiki/spigot-configuration/
+ "If you need help with the configuration or have any questions related to Spigot,\n"
+ "join us at the Discord or drop by our forums and leave a post.\n" If you need help with the configuration or have any questions related to Spigot,
+ "\n" join us at the Discord or drop by our forums and leave a post.
+ "Discord: https://www.spigotmc.org/go/discord\n"
+ "Forums: http://www.spigotmc.org/\n"; Discord: https://www.spigotmc.org/go/discord
Forums: http://www.spigotmc.org/
""";
/*========================================================================*/ /*========================================================================*/
public static YamlConfiguration config; public static YamlConfiguration config;
static int version; static int version;
static Map<String, Command> commands; static Map<String, Command> commands;
/*========================================================================*/ /*========================================================================*/
private static Metrics metrics;
public static void init(File configFile) public static void init(File configFile) {
{
SpigotConfig.CONFIG_FILE = configFile; SpigotConfig.CONFIG_FILE = configFile;
SpigotConfig.config = new YamlConfiguration(); SpigotConfig.config = new YamlConfiguration();
try try {
{
SpigotConfig.config.load(SpigotConfig.CONFIG_FILE); SpigotConfig.config.load(SpigotConfig.CONFIG_FILE);
} catch ( IOException ex ) } catch (IOException ignored) {
{ } catch (InvalidConfigurationException ex) {
} catch ( InvalidConfigurationException ex )
{
Bukkit.getLogger().log(Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex); Bukkit.getLogger().log(Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex);
throw Throwables.propagate(ex); throw Throwables.propagate(ex);
} }
@@ -68,7 +64,7 @@ public class SpigotConfig
SpigotConfig.config.options().header(SpigotConfig.HEADER); SpigotConfig.config.options().header(SpigotConfig.HEADER);
SpigotConfig.config.options().copyDefaults(true); SpigotConfig.config.options().copyDefaults(true);
SpigotConfig.commands = new HashMap<String, Command>(); SpigotConfig.commands = new HashMap<>();
SpigotConfig.commands.put("spigot", new SpigotCommand("spigot")); SpigotConfig.commands.put("spigot", new SpigotCommand("spigot"));
SpigotConfig.version = SpigotConfig.getInt("config-version", 12); SpigotConfig.version = SpigotConfig.getInt("config-version", 12);
@@ -76,113 +72,77 @@ public class SpigotConfig
SpigotConfig.readConfig(SpigotConfig.class, null); SpigotConfig.readConfig(SpigotConfig.class, null);
} }
public static void registerCommands() public static void registerCommands() {
{ for (Map.Entry<String, Command> entry : SpigotConfig.commands.entrySet()) {
for ( Map.Entry<String, Command> entry : SpigotConfig.commands.entrySet() )
{
MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Spigot", entry.getValue()); MinecraftServer.getServer().server.getCommandMap().register(entry.getKey(), "Spigot", entry.getValue());
} }
/* // Paper - Replace with our own
if ( SpigotConfig.metrics == null )
{
try
{
SpigotConfig.metrics = new Metrics();
SpigotConfig.metrics.start();
} catch ( IOException ex )
{
Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex );
}
}
*/ // Paper end
} }
public static void readConfig(Class<?> clazz, Object instance) // Paper - package-private -> public public static void readConfig(Class<?> clazz, Object instance) { // Paper - package-private -> public
{ for (Method method : clazz.getDeclaredMethods()) {
for ( Method method : clazz.getDeclaredMethods() ) if (Modifier.isPrivate(method.getModifiers())) {
{ if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) {
if ( Modifier.isPrivate( method.getModifiers() ) ) try {
{
if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE )
{
try
{
method.setAccessible(true); method.setAccessible(true);
method.invoke(instance); method.invoke(instance);
} catch ( InvocationTargetException ex ) } catch (InvocationTargetException ex) {
{
throw Throwables.propagate(ex.getCause()); throw Throwables.propagate(ex.getCause());
} catch ( Exception ex ) } catch (Exception ex) {
{
Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex); Bukkit.getLogger().log(Level.SEVERE, "Error invoking " + method, ex);
} }
} }
} }
} }
try try {
{
SpigotConfig.config.save(SpigotConfig.CONFIG_FILE); SpigotConfig.config.save(SpigotConfig.CONFIG_FILE);
} catch ( IOException ex ) } catch (IOException ex) {
{
Bukkit.getLogger().log(Level.SEVERE, "Could not save " + SpigotConfig.CONFIG_FILE, ex); Bukkit.getLogger().log(Level.SEVERE, "Could not save " + SpigotConfig.CONFIG_FILE, ex);
} }
} }
private static void set(String path, Object val) private static void set(String path, Object val) {
{
SpigotConfig.config.set(path, val); SpigotConfig.config.set(path, val);
} }
private static boolean getBoolean(String path, boolean def) private static boolean getBoolean(String path, boolean def) {
{
SpigotConfig.config.addDefault(path, def); SpigotConfig.config.addDefault(path, def);
return SpigotConfig.config.getBoolean(path, SpigotConfig.config.getBoolean(path)); return SpigotConfig.config.getBoolean(path, SpigotConfig.config.getBoolean(path));
} }
private static int getInt(String path, int def) private static int getInt(String path, int def) {
{
SpigotConfig.config.addDefault(path, def); SpigotConfig.config.addDefault(path, def);
return SpigotConfig.config.getInt(path, SpigotConfig.config.getInt(path)); return SpigotConfig.config.getInt(path, SpigotConfig.config.getInt(path));
} }
private static <T> List getList(String path, T def) private static <T> List getList(String path, T def) {
{
SpigotConfig.config.addDefault(path, def); SpigotConfig.config.addDefault(path, def);
return (List<T>) SpigotConfig.config.getList(path, SpigotConfig.config.getList(path)); return (List<T>) SpigotConfig.config.getList(path, SpigotConfig.config.getList(path));
} }
private static String getString(String path, String def) private static String getString(String path, String def) {
{
SpigotConfig.config.addDefault(path, def); SpigotConfig.config.addDefault(path, def);
return SpigotConfig.config.getString(path, SpigotConfig.config.getString(path)); return SpigotConfig.config.getString(path, SpigotConfig.config.getString(path));
} }
private static double getDouble(String path, double def) private static double getDouble(String path, double def) {
{
SpigotConfig.config.addDefault(path, def); SpigotConfig.config.addDefault(path, def);
return SpigotConfig.config.getDouble(path, SpigotConfig.config.getDouble(path)); return SpigotConfig.config.getDouble(path, SpigotConfig.config.getDouble(path));
} }
public static boolean logCommands; public static boolean logCommands;
private static void logCommands() private static void logCommands() {
{
SpigotConfig.logCommands = SpigotConfig.getBoolean("commands.log", true); SpigotConfig.logCommands = SpigotConfig.getBoolean("commands.log", true);
} }
public static int tabComplete; public static int tabComplete;
public static boolean sendNamespaced; public static boolean sendNamespaced;
private static void tabComplete() private static void tabComplete() {
{ if (SpigotConfig.version < 6) {
if ( SpigotConfig.version < 6 )
{
boolean oldValue = SpigotConfig.getBoolean("commands.tab-complete", true); boolean oldValue = SpigotConfig.getBoolean("commands.tab-complete", true);
if ( oldValue ) if (oldValue) {
{
SpigotConfig.set("commands.tab-complete", 0); SpigotConfig.set("commands.tab-complete", 0);
} else } else {
{
SpigotConfig.set("commands.tab-complete", -1); SpigotConfig.set("commands.tab-complete", -1);
} }
} }
@@ -194,15 +154,14 @@ public class SpigotConfig
public static String unknownCommandMessage; public static String unknownCommandMessage;
public static String serverFullMessage; public static String serverFullMessage;
public static String outdatedClientMessage = "Outdated client! Please use {0}"; public static String outdatedClientMessage = "Outdated client! Please use {0}";
public static String outdatedServerMessage = "Outdated server! I\'m still on {0}"; public static String outdatedServerMessage = "Outdated server! I'm still on {0}";
private static String transform(String s)
{ private static String transform(String s) {
return ChatColor.translateAlternateColorCodes('&', s).replaceAll("\\\\n", "\n"); return ChatColor.translateAlternateColorCodes('&', s).replaceAll("\\\\n", "\n");
} }
private static void messages()
{ private static void messages() {
if (SpigotConfig.version < 8) if (SpigotConfig.version < 8) {
{
SpigotConfig.set("messages.outdated-client", SpigotConfig.outdatedClientMessage); SpigotConfig.set("messages.outdated-client", SpigotConfig.outdatedClientMessage);
SpigotConfig.set("messages.outdated-server", SpigotConfig.outdatedServerMessage); SpigotConfig.set("messages.outdated-server", SpigotConfig.outdatedServerMessage);
} }
@@ -218,28 +177,24 @@ public class SpigotConfig
public static boolean restartOnCrash = true; public static boolean restartOnCrash = true;
public static String restartScript = "./start.sh"; public static String restartScript = "./start.sh";
public static String restartMessage; public static String restartMessage;
private static void watchdog() private static void watchdog() {
{
SpigotConfig.timeoutTime = SpigotConfig.getInt("settings.timeout-time", SpigotConfig.timeoutTime); SpigotConfig.timeoutTime = SpigotConfig.getInt("settings.timeout-time", SpigotConfig.timeoutTime);
SpigotConfig.restartOnCrash = SpigotConfig.getBoolean("settings.restart-on-crash", SpigotConfig.restartOnCrash); SpigotConfig.restartOnCrash = SpigotConfig.getBoolean("settings.restart-on-crash", SpigotConfig.restartOnCrash);
SpigotConfig.restartScript = SpigotConfig.getString("settings.restart-script", SpigotConfig.restartScript); SpigotConfig.restartScript = SpigotConfig.getString("settings.restart-script", SpigotConfig.restartScript);
SpigotConfig.restartMessage = SpigotConfig.transform(SpigotConfig.getString("messages.restart", "Server is restarting")); SpigotConfig.restartMessage = SpigotConfig.transform(SpigotConfig.getString("messages.restart", "Server is restarting"));
SpigotConfig.commands.put("restart", new RestartCommand("restart")); SpigotConfig.commands.put("restart", new RestartCommand("restart"));
// WatchdogThread.doStart( SpigotConfig.timeoutTime, SpigotConfig.restartOnCrash ); // Paper - moved to after paper config initialization
} }
public static boolean bungee; public static boolean bungee;
private static void bungee() { private static void bungee() {
if ( SpigotConfig.version < 4 ) if (SpigotConfig.version < 4) {
{
SpigotConfig.set("settings.bungeecord", false); SpigotConfig.set("settings.bungeecord", false);
System.out.println("Outdated config, disabling BungeeCord support!"); System.out.println("Outdated config, disabling BungeeCord support!");
} }
SpigotConfig.bungee = SpigotConfig.getBoolean("settings.bungeecord", false); SpigotConfig.bungee = SpigotConfig.getBoolean("settings.bungeecord", false);
} }
private static void nettyThreads() private static void nettyThreads() {
{
int count = SpigotConfig.getInt("settings.netty-threads", 4); int count = SpigotConfig.getInt("settings.netty-threads", 4);
System.setProperty("io.netty.eventLoopThreads", Integer.toString(count)); System.setProperty("io.netty.eventLoopThreads", Integer.toString(count));
Bukkit.getLogger().log(Level.INFO, "Using {0} threads for Netty based IO", count); Bukkit.getLogger().log(Level.INFO, "Using {0} threads for Netty based IO", count);
@@ -247,8 +202,8 @@ public class SpigotConfig
public static boolean disableStatSaving; public static boolean disableStatSaving;
public static Map<ResourceLocation, Integer> forcedStats = new HashMap<>(); public static Map<ResourceLocation, Integer> forcedStats = new HashMap<>();
private static void stats()
{ private static void stats() {
SpigotConfig.disableStatSaving = SpigotConfig.getBoolean("stats.disable-saving", false); SpigotConfig.disableStatSaving = SpigotConfig.getBoolean("stats.disable-saving", false);
if (!SpigotConfig.config.contains("stats.forced-stats")) { if (!SpigotConfig.config.contains("stats.forced-stats")) {
@@ -256,102 +211,82 @@ public class SpigotConfig
} }
ConfigurationSection section = SpigotConfig.config.getConfigurationSection("stats.forced-stats"); ConfigurationSection section = SpigotConfig.config.getConfigurationSection("stats.forced-stats");
for ( String name : section.getKeys( true ) ) for (String name : section.getKeys(true)) {
{ if (section.isInt(name)) {
if ( section.isInt( name ) ) try {
{
try
{
ResourceLocation key = ResourceLocation.parse(name); ResourceLocation key = ResourceLocation.parse(name);
if ( BuiltInRegistries.CUSTOM_STAT.get( key ) == null ) if (BuiltInRegistries.CUSTOM_STAT.get(key) == null) {
{
Bukkit.getLogger().log(Level.WARNING, "Ignoring non existent stats.forced-stats " + name); Bukkit.getLogger().log(Level.WARNING, "Ignoring non existent stats.forced-stats " + name);
continue; continue;
} }
SpigotConfig.forcedStats.put(key, section.getInt(name)); SpigotConfig.forcedStats.put(key, section.getInt(name));
} catch (Exception ex) } catch (Exception ex) {
{
Bukkit.getLogger().log(Level.WARNING, "Ignoring invalid stats.forced-stats " + name); Bukkit.getLogger().log(Level.WARNING, "Ignoring invalid stats.forced-stats " + name);
} }
} }
} }
} }
private static void tpsCommand() private static void tpsCommand() {
{
SpigotConfig.commands.put("tps", new TicksPerSecondCommand("tps")); SpigotConfig.commands.put("tps", new TicksPerSecondCommand("tps"));
} }
public static int playerSample; public static int playerSample;
private static void playerSample() private static void playerSample() {
{
SpigotConfig.playerSample = Math.max(SpigotConfig.getInt("settings.sample-count", 12), 0); // Paper - Avoid negative counts SpigotConfig.playerSample = Math.max(SpigotConfig.getInt("settings.sample-count", 12), 0); // Paper - Avoid negative counts
Bukkit.getLogger().log(Level.INFO, "Server Ping Player Sample Count: {0}", playerSample); // Paper - Use logger Bukkit.getLogger().log(Level.INFO, "Server Ping Player Sample Count: {0}", playerSample); // Paper - Use logger
} }
public static int playerShuffle; public static int playerShuffle;
private static void playerShuffle() private static void playerShuffle() {
{
SpigotConfig.playerShuffle = SpigotConfig.getInt("settings.player-shuffle", 0); SpigotConfig.playerShuffle = SpigotConfig.getInt("settings.player-shuffle", 0);
} }
public static List<String> spamExclusions; public static List<String> spamExclusions;
private static void spamExclusions() private static void spamExclusions() {
{ SpigotConfig.spamExclusions = SpigotConfig.getList("commands.spam-exclusions", List.of("/skill"));
SpigotConfig.spamExclusions = SpigotConfig.getList( "commands.spam-exclusions", Arrays.asList( new String[]
{
"/skill"
} ) );
} }
public static boolean silentCommandBlocks; public static boolean silentCommandBlocks;
private static void silentCommandBlocks() private static void silentCommandBlocks() {
{
SpigotConfig.silentCommandBlocks = SpigotConfig.getBoolean("commands.silent-commandblock-console", false); SpigotConfig.silentCommandBlocks = SpigotConfig.getBoolean("commands.silent-commandblock-console", false);
} }
public static Set<String> replaceCommands; public static Set<String> replaceCommands;
private static void replaceCommands() private static void replaceCommands() {
{ if (SpigotConfig.config.contains("replace-commands")) {
if ( SpigotConfig.config.contains( "replace-commands" ) )
{
SpigotConfig.set("commands.replace-commands", SpigotConfig.config.getStringList("replace-commands")); SpigotConfig.set("commands.replace-commands", SpigotConfig.config.getStringList("replace-commands"));
SpigotConfig.config.set("replace-commands", null); SpigotConfig.config.set("replace-commands", null);
} }
SpigotConfig.replaceCommands = new HashSet<String>( (List<String>) SpigotConfig.getList( "commands.replace-commands", SpigotConfig.replaceCommands = new HashSet<>(SpigotConfig.getList("commands.replace-commands",
Arrays.asList("setblock", "summon", "testforblock", "tellraw"))); Arrays.asList("setblock", "summon", "testforblock", "tellraw")));
} }
public static int userCacheCap; public static int userCacheCap;
private static void userCacheCap() private static void userCacheCap() {
{
SpigotConfig.userCacheCap = SpigotConfig.getInt("settings.user-cache-size", 1000); SpigotConfig.userCacheCap = SpigotConfig.getInt("settings.user-cache-size", 1000);
} }
public static boolean saveUserCacheOnStopOnly; public static boolean saveUserCacheOnStopOnly;
private static void saveUserCacheOnStopOnly() private static void saveUserCacheOnStopOnly() {
{
SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean("settings.save-user-cache-on-stop-only", false); SpigotConfig.saveUserCacheOnStopOnly = SpigotConfig.getBoolean("settings.save-user-cache-on-stop-only", false);
} }
public static double movedWronglyThreshold; public static double movedWronglyThreshold;
private static void movedWronglyThreshold() private static void movedWronglyThreshold() {
{
SpigotConfig.movedWronglyThreshold = SpigotConfig.getDouble("settings.moved-wrongly-threshold", 0.0625D); SpigotConfig.movedWronglyThreshold = SpigotConfig.getDouble("settings.moved-wrongly-threshold", 0.0625D);
} }
public static double movedTooQuicklyMultiplier; public static double movedTooQuicklyMultiplier;
private static void movedTooQuicklyMultiplier() private static void movedTooQuicklyMultiplier() {
{
SpigotConfig.movedTooQuicklyMultiplier = SpigotConfig.getDouble("settings.moved-too-quickly-multiplier", 10.0D); SpigotConfig.movedTooQuicklyMultiplier = SpigotConfig.getDouble("settings.moved-too-quickly-multiplier", 10.0D);
} }
public static double maxAbsorption = 2048; public static double maxAbsorption = 2048;
public static double maxHealth = 2048; public static double maxHealth = 1024;
public static double movementSpeed = 2048; public static double movementSpeed = 1024;
public static double attackDamage = 2048; public static double attackDamage = 2048;
private static void attributeMaxes() private static void attributeMaxes() {
{
SpigotConfig.maxAbsorption = SpigotConfig.getDouble("settings.attribute.maxAbsorption.max", SpigotConfig.maxAbsorption); SpigotConfig.maxAbsorption = SpigotConfig.getDouble("settings.attribute.maxAbsorption.max", SpigotConfig.maxAbsorption);
((RangedAttribute) Attributes.MAX_ABSORPTION.value()).maxValue = SpigotConfig.maxAbsorption; ((RangedAttribute) Attributes.MAX_ABSORPTION.value()).maxValue = SpigotConfig.maxAbsorption;
SpigotConfig.maxHealth = SpigotConfig.getDouble("settings.attribute.maxHealth.max", SpigotConfig.maxHealth); SpigotConfig.maxHealth = SpigotConfig.getDouble("settings.attribute.maxHealth.max", SpigotConfig.maxHealth);
@@ -363,12 +298,10 @@ public class SpigotConfig
} }
public static boolean debug; public static boolean debug;
private static void debug() private static void debug() {
{
SpigotConfig.debug = SpigotConfig.getBoolean("settings.debug", false); SpigotConfig.debug = SpigotConfig.getBoolean("settings.debug", false);
if ( SpigotConfig.debug && !LogManager.getRootLogger().isTraceEnabled() ) if (SpigotConfig.debug && !LogManager.getRootLogger().isTraceEnabled()) {
{
// Enable debug logging // Enable debug logging
LoggerContext ctx = (LoggerContext) LogManager.getContext(false); LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration conf = ctx.getConfiguration(); Configuration conf = ctx.getConfiguration();
@@ -376,12 +309,8 @@ public class SpigotConfig
ctx.updateLoggers(conf); ctx.updateLoggers(conf);
} }
if ( LogManager.getRootLogger().isTraceEnabled() ) if (LogManager.getRootLogger().isTraceEnabled()) {
{
Bukkit.getLogger().info("Debug logging is enabled"); Bukkit.getLogger().info("Debug logging is enabled");
} else
{
// Bukkit.getLogger().info( "Debug logging is disabled" ); // Paper - Don't log if debug logging isn't enabled.
} }
} }
@@ -389,7 +318,7 @@ public class SpigotConfig
public static List<String> disabledAdvancements; public static List<String> disabledAdvancements;
private static void disabledAdvancements() { private static void disabledAdvancements() {
SpigotConfig.disableAdvancementSaving = SpigotConfig.getBoolean("advancements.disable-saving", false); SpigotConfig.disableAdvancementSaving = SpigotConfig.getBoolean("advancements.disable-saving", false);
SpigotConfig.disabledAdvancements = SpigotConfig.getList("advancements.disabled", Arrays.asList(new String[]{"minecraft:story/disabled"})); SpigotConfig.disabledAdvancements = SpigotConfig.getList("advancements.disabled", List.of("minecraft:story/disabled"));
} }
public static boolean logVillagerDeaths; public static boolean logVillagerDeaths;

View File

@@ -4,84 +4,71 @@ import java.util.List;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
public class SpigotWorldConfig public class SpigotWorldConfig {
{
private final String worldName; private final String worldName;
private final YamlConfiguration config; private final YamlConfiguration config;
private boolean verbose; private boolean verbose;
public SpigotWorldConfig(String worldName) public SpigotWorldConfig(String worldName) {
{
this.worldName = worldName; this.worldName = worldName;
this.config = SpigotConfig.config; this.config = SpigotConfig.config;
this.init(); this.init();
} }
public void init() public void init() {
{
this.verbose = this.getBoolean("verbose", false); // Paper this.verbose = this.getBoolean("verbose", false); // Paper
this.log("-------- World Settings For [" + this.worldName + "] --------"); this.log("-------- World Settings For [" + this.worldName + "] --------");
SpigotConfig.readConfig(SpigotWorldConfig.class, this); SpigotConfig.readConfig(SpigotWorldConfig.class, this);
} }
private void log(String s) private void log(String s) {
{ if (this.verbose) {
if ( this.verbose )
{
Bukkit.getLogger().info(s); Bukkit.getLogger().info(s);
} }
} }
private void set(String path, Object val) private void set(String path, Object val) {
{
this.config.set("world-settings.default." + path, val); this.config.set("world-settings.default." + path, val);
} }
public boolean getBoolean(String path, boolean def) public boolean getBoolean(String path, boolean def) {
{
this.config.addDefault("world-settings.default." + path, def); this.config.addDefault("world-settings.default." + path, def);
return this.config.getBoolean("world-settings." + this.worldName + "." + path, this.config.getBoolean("world-settings.default." + path)); return this.config.getBoolean("world-settings." + this.worldName + "." + path, this.config.getBoolean("world-settings.default." + path));
} }
public double getDouble(String path, double def) public double getDouble(String path, double def) {
{
this.config.addDefault("world-settings.default." + path, def); this.config.addDefault("world-settings.default." + path, def);
return this.config.getDouble("world-settings." + this.worldName + "." + path, this.config.getDouble("world-settings.default." + path)); return this.config.getDouble("world-settings." + this.worldName + "." + path, this.config.getDouble("world-settings.default." + path));
} }
public int getInt(String path) public int getInt(String path) {
{
return this.config.getInt("world-settings." + this.worldName + "." + path); return this.config.getInt("world-settings." + this.worldName + "." + path);
} }
public int getInt(String path, int def) public int getInt(String path, int def) {
{
// Paper start - get int without setting default // Paper start - get int without setting default
return this.getInt(path, def, true); return this.getInt(path, def, true);
} }
public int getInt(String path, int def, boolean setDef)
{ public int getInt(String path, int def, boolean setDef) {
if (setDef) this.config.addDefault("world-settings.default." + path, def); if (setDef) this.config.addDefault("world-settings.default." + path, def);
return this.config.getInt("world-settings." + this.worldName + "." + path, this.config.getInt("world-settings.default." + path, def)); return this.config.getInt("world-settings." + this.worldName + "." + path, this.config.getInt("world-settings.default." + path, def));
// Paper end // Paper end
} }
public <T> List getList(String path, T def) public <T> List getList(String path, T def) {
{
this.config.addDefault("world-settings.default." + path, def); this.config.addDefault("world-settings.default." + path, def);
return (List<T>) this.config.getList("world-settings." + this.worldName + "." + path, this.config.getList("world-settings.default." + path)); return (List<T>) this.config.getList("world-settings." + this.worldName + "." + path, this.config.getList("world-settings.default." + path));
} }
public String getString(String path, String def) public String getString(String path, String def) {
{
this.config.addDefault("world-settings.default." + path, def); this.config.addDefault("world-settings.default." + path, def);
return this.config.getString("world-settings." + this.worldName + "." + path, this.config.getString("world-settings.default." + path)); return this.config.getString("world-settings." + this.worldName + "." + path, this.config.getString("world-settings.default." + path));
} }
private Object get(String path, Object def) private Object get(String path, Object def) {
{
this.config.addDefault("world-settings.default." + path, def); this.config.addDefault("world-settings.default." + path, def);
return this.config.get("world-settings." + this.worldName + "." + path, this.config.get("world-settings.default." + path)); return this.config.get("world-settings." + this.worldName + "." + path, this.config.get("world-settings.default." + path));
} }
@@ -109,11 +96,10 @@ public class SpigotWorldConfig
public int caveVinesModifier; public int caveVinesModifier;
public int glowBerryModifier; // Paper public int glowBerryModifier; // Paper
public int pitcherPlantModifier; // Paper public int pitcherPlantModifier; // Paper
private int getAndValidateGrowth(String crop)
{ private int getAndValidateGrowth(String crop) {
int modifier = this.getInt("growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100); int modifier = this.getInt("growth." + crop.toLowerCase(java.util.Locale.ENGLISH) + "-modifier", 100);
if ( modifier == 0 ) if (modifier == 0) {
{
this.log("Cannot set " + crop + " growth to zero, defaulting to 100"); this.log("Cannot set " + crop + " growth to zero, defaulting to 100");
modifier = 100; modifier = 100;
} }
@@ -121,8 +107,8 @@ public class SpigotWorldConfig
return modifier; return modifier;
} }
private void growthModifiers()
{ private void growthModifiers() {
this.cactusModifier = this.getAndValidateGrowth("Cactus"); this.cactusModifier = this.getAndValidateGrowth("Cactus");
this.caneModifier = this.getAndValidateGrowth("Cane"); this.caneModifier = this.getAndValidateGrowth("Cane");
this.melonModifier = this.getAndValidateGrowth("Melon"); this.melonModifier = this.getAndValidateGrowth("Melon");
@@ -148,31 +134,26 @@ public class SpigotWorldConfig
} }
public double itemMerge; public double itemMerge;
private void itemMerge() private void itemMerge() {
{
this.itemMerge = this.getDouble("merge-radius.item", 0.5); this.itemMerge = this.getDouble("merge-radius.item", 0.5);
this.log("Item Merge Radius: " + this.itemMerge); this.log("Item Merge Radius: " + this.itemMerge);
} }
public double expMerge; public double expMerge;
private void expMerge() private void expMerge() {
{
this.expMerge = this.getDouble("merge-radius.exp", -1); this.expMerge = this.getDouble("merge-radius.exp", -1);
this.log("Experience Merge Radius: " + this.expMerge); this.log("Experience Merge Radius: " + this.expMerge);
} }
public int viewDistance; public int viewDistance;
private void viewDistance() private void viewDistance() {
{ if (SpigotConfig.version < 12) {
if ( SpigotConfig.version < 12 )
{
this.set("view-distance", null); this.set("view-distance", null);
} }
Object viewDistanceObject = this.get("view-distance", "default"); Object viewDistanceObject = this.get("view-distance", "default");
this.viewDistance = (viewDistanceObject) instanceof Number ? ((Number) viewDistanceObject).intValue() : -1; this.viewDistance = (viewDistanceObject) instanceof Number ? ((Number) viewDistanceObject).intValue() : -1;
if ( this.viewDistance <= 0 ) if (this.viewDistance <= 0) {
{
this.viewDistance = Bukkit.getViewDistance(); this.viewDistance = Bukkit.getViewDistance();
} }
@@ -181,12 +162,10 @@ public class SpigotWorldConfig
} }
public int simulationDistance; public int simulationDistance;
private void simulationDistance() private void simulationDistance() {
{
Object simulationDistanceObject = this.get("simulation-distance", "default"); Object simulationDistanceObject = this.get("simulation-distance", "default");
this.simulationDistance = (simulationDistanceObject) instanceof Number ? ((Number) simulationDistanceObject).intValue() : -1; this.simulationDistance = (simulationDistanceObject) instanceof Number ? ((Number) simulationDistanceObject).intValue() : -1;
if ( this.simulationDistance <= 0 ) if (this.simulationDistance <= 0) {
{
this.simulationDistance = Bukkit.getSimulationDistance(); this.simulationDistance = Bukkit.getSimulationDistance();
} }
@@ -194,15 +173,13 @@ public class SpigotWorldConfig
} }
public byte mobSpawnRange; public byte mobSpawnRange;
private void mobSpawnRange() private void mobSpawnRange() {
{
this.mobSpawnRange = (byte) getInt("mob-spawn-range", 8); // Paper - Vanilla this.mobSpawnRange = (byte) getInt("mob-spawn-range", 8); // Paper - Vanilla
this.log("Mob Spawn Range: " + this.mobSpawnRange); this.log("Mob Spawn Range: " + this.mobSpawnRange);
} }
public int itemDespawnRate; public int itemDespawnRate;
private void itemDespawnRate() private void itemDespawnRate() {
{
this.itemDespawnRate = this.getInt("item-despawn-rate", 6000); this.itemDespawnRate = this.getInt("item-despawn-rate", 6000);
this.log("Item Despawn Rate: " + this.itemDespawnRate); this.log("Item Despawn Rate: " + this.itemDespawnRate);
} }
@@ -233,8 +210,8 @@ public class SpigotWorldConfig
// Paper end // Paper end
public boolean tickInactiveVillagers = true; public boolean tickInactiveVillagers = true;
public boolean ignoreSpectatorActivation = false; public boolean ignoreSpectatorActivation = false;
private void activationRange()
{ private void activationRange() {
boolean hasAnimalsConfig = config.getInt("entity-activation-range.animals", this.animalActivationRange) != this.animalActivationRange; // Paper boolean hasAnimalsConfig = config.getInt("entity-activation-range.animals", this.animalActivationRange) != this.animalActivationRange; // Paper
this.animalActivationRange = this.getInt("entity-activation-range.animals", this.animalActivationRange); this.animalActivationRange = this.getInt("entity-activation-range.animals", this.animalActivationRange);
this.monsterActivationRange = this.getInt("entity-activation-range.monsters", this.monsterActivationRange); this.monsterActivationRange = this.getInt("entity-activation-range.monsters", this.monsterActivationRange);
@@ -276,8 +253,7 @@ public class SpigotWorldConfig
public int miscTrackingRange = 96; public int miscTrackingRange = 96;
public int displayTrackingRange = 128; public int displayTrackingRange = 128;
public int otherTrackingRange = 64; public int otherTrackingRange = 64;
private void trackingRange() private void trackingRange() {
{
this.playerTrackingRange = this.getInt("entity-tracking-range.players", this.playerTrackingRange); this.playerTrackingRange = this.getInt("entity-tracking-range.players", this.playerTrackingRange);
this.animalTrackingRange = this.getInt("entity-tracking-range.animals", this.animalTrackingRange); this.animalTrackingRange = this.getInt("entity-tracking-range.animals", this.animalTrackingRange);
this.monsterTrackingRange = this.getInt("entity-tracking-range.monsters", this.monsterTrackingRange); this.monsterTrackingRange = this.getInt("entity-tracking-range.monsters", this.monsterTrackingRange);
@@ -291,12 +267,10 @@ public class SpigotWorldConfig
public int hopperCheck; public int hopperCheck;
public int hopperAmount; public int hopperAmount;
public boolean hopperCanLoadChunks; public boolean hopperCanLoadChunks;
private void hoppers() private void hoppers() {
{
// Set the tick delay between hopper item movements // Set the tick delay between hopper item movements
this.hopperTransfer = this.getInt("ticks-per.hopper-transfer", 8); this.hopperTransfer = this.getInt("ticks-per.hopper-transfer", 8);
if ( SpigotConfig.version < 11 ) if (SpigotConfig.version < 11) {
{
this.set("ticks-per.hopper-check", 1); this.set("ticks-per.hopper-check", 1);
} }
this.hopperCheck = this.getInt("ticks-per.hopper-check", 1); this.hopperCheck = this.getInt("ticks-per.hopper-check", 1);
@@ -307,49 +281,42 @@ public class SpigotWorldConfig
public int arrowDespawnRate; public int arrowDespawnRate;
public int tridentDespawnRate; public int tridentDespawnRate;
private void arrowDespawnRate() private void arrowDespawnRate() {
{
this.arrowDespawnRate = this.getInt("arrow-despawn-rate", 1200); this.arrowDespawnRate = this.getInt("arrow-despawn-rate", 1200);
this.tridentDespawnRate = this.getInt("trident-despawn-rate", this.arrowDespawnRate); this.tridentDespawnRate = this.getInt("trident-despawn-rate", this.arrowDespawnRate);
this.log("Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Respawn Rate:" + this.tridentDespawnRate); this.log("Arrow Despawn Rate: " + this.arrowDespawnRate + " Trident Respawn Rate:" + this.tridentDespawnRate);
} }
public boolean zombieAggressiveTowardsVillager; public boolean zombieAggressiveTowardsVillager;
private void zombieAggressiveTowardsVillager() private void zombieAggressiveTowardsVillager() {
{
this.zombieAggressiveTowardsVillager = this.getBoolean("zombie-aggressive-towards-villager", true); this.zombieAggressiveTowardsVillager = this.getBoolean("zombie-aggressive-towards-villager", true);
this.log("Zombie Aggressive Towards Villager: " + this.zombieAggressiveTowardsVillager); this.log("Zombie Aggressive Towards Villager: " + this.zombieAggressiveTowardsVillager);
} }
public boolean nerfSpawnerMobs; public boolean nerfSpawnerMobs;
private void nerfSpawnerMobs() private void nerfSpawnerMobs() {
{
this.nerfSpawnerMobs = this.getBoolean("nerf-spawner-mobs", false); this.nerfSpawnerMobs = this.getBoolean("nerf-spawner-mobs", false);
this.log("Nerfing mobs spawned from spawners: " + this.nerfSpawnerMobs); this.log("Nerfing mobs spawned from spawners: " + this.nerfSpawnerMobs);
} }
public boolean enableZombiePigmenPortalSpawns; public boolean enableZombiePigmenPortalSpawns;
private void enableZombiePigmenPortalSpawns() private void enableZombiePigmenPortalSpawns() {
{
this.enableZombiePigmenPortalSpawns = this.getBoolean("enable-zombie-pigmen-portal-spawns", true); this.enableZombiePigmenPortalSpawns = this.getBoolean("enable-zombie-pigmen-portal-spawns", true);
this.log("Allow Zombie Pigmen to spawn from portal blocks: " + this.enableZombiePigmenPortalSpawns); this.log("Allow Zombie Pigmen to spawn from portal blocks: " + this.enableZombiePigmenPortalSpawns);
} }
public int dragonDeathSoundRadius; public int dragonDeathSoundRadius;
private void keepDragonDeathPerWorld() private void keepDragonDeathPerWorld() {
{
this.dragonDeathSoundRadius = this.getInt("dragon-death-sound-radius", 0); this.dragonDeathSoundRadius = this.getInt("dragon-death-sound-radius", 0);
} }
public int witherSpawnSoundRadius; public int witherSpawnSoundRadius;
private void witherSpawnSoundRadius() private void witherSpawnSoundRadius() {
{
this.witherSpawnSoundRadius = this.getInt("wither-spawn-sound-radius", 0); this.witherSpawnSoundRadius = this.getInt("wither-spawn-sound-radius", 0);
} }
public int endPortalSoundRadius; public int endPortalSoundRadius;
private void endPortalSoundRadius() private void endPortalSoundRadius() {
{
this.endPortalSoundRadius = this.getInt("end-portal-sound-radius", 0); this.endPortalSoundRadius = this.getInt("end-portal-sound-radius", 0);
} }
@@ -375,13 +342,14 @@ public class SpigotWorldConfig
public int buriedTreasureSeed; public int buriedTreasureSeed;
public Integer mineshaftSeed; public Integer mineshaftSeed;
public Long strongholdSeed; public Long strongholdSeed;
private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) { private <N extends Number> N getSeed(String path, java.util.function.Function<String, N> toNumberFunc) {
final String value = this.getString(path, "default"); final String value = this.getString(path, "default");
return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null; return org.apache.commons.lang3.math.NumberUtils.isParsable(value) ? toNumberFunc.apply(value) : null;
} }
// Paper end // Paper end
private void initWorldGenSeeds() private void initWorldGenSeeds() {
{
this.villageSeed = this.getInt("seed-village", 10387312); this.villageSeed = this.getInt("seed-village", 10387312);
this.desertSeed = this.getInt("seed-desert", 14357617); this.desertSeed = this.getInt("seed-desert", 14357617);
this.iglooSeed = this.getInt("seed-igloo", 14357618); this.iglooSeed = this.getInt("seed-igloo", 14357618);
@@ -416,10 +384,8 @@ public class SpigotWorldConfig
public float swimMultiplier; public float swimMultiplier;
public float sprintMultiplier; public float sprintMultiplier;
public float otherMultiplier; public float otherMultiplier;
private void initHunger() private void initHunger() {
{ if (SpigotConfig.version < 10) {
if ( SpigotConfig.version < 10 )
{
this.set("hunger.walk-exhaustion", null); this.set("hunger.walk-exhaustion", null);
this.set("hunger.sprint-exhaustion", null); this.set("hunger.sprint-exhaustion", null);
this.set("hunger.combat-exhaustion", 0.1); this.set("hunger.combat-exhaustion", 0.1);
@@ -438,8 +404,7 @@ public class SpigotWorldConfig
public int currentPrimedTnt = 0; public int currentPrimedTnt = 0;
public int maxTntTicksPerTick; public int maxTntTicksPerTick;
private void maxTntPerTick() { private void maxTntPerTick() {
if ( SpigotConfig.version < 7 ) if (SpigotConfig.version < 7) {
{
this.set("max-tnt-per-tick", 100); this.set("max-tnt-per-tick", 100);
} }
this.maxTntTicksPerTick = this.getInt("max-tnt-per-tick", 100); this.maxTntTicksPerTick = this.getInt("max-tnt-per-tick", 100);
@@ -447,23 +412,20 @@ public class SpigotWorldConfig
} }
public int hangingTickFrequency; public int hangingTickFrequency;
private void hangingTickFrequency() private void hangingTickFrequency() {
{
this.hangingTickFrequency = this.getInt("hanging-tick-frequency", 100); this.hangingTickFrequency = this.getInt("hanging-tick-frequency", 100);
} }
public int tileMaxTickTime; public int tileMaxTickTime;
public int entityMaxTickTime; public int entityMaxTickTime;
private void maxTickTimes() private void maxTickTimes() {
{
this.tileMaxTickTime = this.getInt("max-tick-time.tile", 50); this.tileMaxTickTime = this.getInt("max-tick-time.tile", 50);
this.entityMaxTickTime = this.getInt("max-tick-time.entity", 50); this.entityMaxTickTime = this.getInt("max-tick-time.entity", 50);
this.log("Tile Max Tick Time: " + this.tileMaxTickTime + "ms Entity max Tick Time: " + this.entityMaxTickTime + "ms"); this.log("Tile Max Tick Time: " + this.tileMaxTickTime + "ms Entity max Tick Time: " + this.entityMaxTickTime + "ms");
} }
public int thunderChance; public int thunderChance;
private void thunderChance() private void thunderChance() {
{
this.thunderChance = this.getInt("thunder-chance", 100000); this.thunderChance = this.getInt("thunder-chance", 100000);
} }

View File

@@ -5,8 +5,8 @@ public class TickLimiter {
private final int maxTime; private final int maxTime;
private long startTime; private long startTime;
public TickLimiter(int maxtime) { public TickLimiter(int maxTime) {
this.maxTime = maxtime; this.maxTime = maxTime;
} }
public void initTick() { public void initTick() {

View File

@@ -1,51 +1,55 @@
package org.spigotmc; package org.spigotmc;
import net.minecraft.server.MinecraftServer; import net.kyori.adventure.text.Component;
import org.bukkit.ChatColor; import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
public class TicksPerSecondCommand extends Command import static net.kyori.adventure.text.Component.text;
{
public TicksPerSecondCommand(String name) public class TicksPerSecondCommand extends Command {
{
private boolean hasShownMemoryWarning; // Paper
public TicksPerSecondCommand(String name) {
super(name); super(name);
this.description = "Gets the current ticks per second for the server"; this.description = "Gets the current ticks per second for the server";
this.usageMessage = "/tps"; this.usageMessage = "/tps";
this.setPermission("bukkit.command.tps"); this.setPermission("bukkit.command.tps");
} }
// Paper start // Paper start
private static final net.kyori.adventure.text.Component WARN_MSG = net.kyori.adventure.text.Component.text() private static final Component WARN_MSG = text()
.append(net.kyori.adventure.text.Component.text("Warning: ", net.kyori.adventure.text.format.NamedTextColor.RED)) .append(text("Warning: ", NamedTextColor.RED))
.append(net.kyori.adventure.text.Component.text("Memory usage on modern garbage collectors is not a stable value and it is perfectly normal to see it reach max. Please do not pay it much attention.", net.kyori.adventure.text.format.NamedTextColor.GOLD)) .append(text("Memory usage on modern garbage collectors is not a stable value and it is perfectly normal to see it reach max. Please do not pay it much attention.", NamedTextColor.GOLD))
.build(); .build();
// Paper end // Paper end
@Override @Override
public boolean execute(CommandSender sender, String currentAlias, String[] args) public boolean execute(CommandSender sender, String currentAlias, String[] args) {
{ if (!this.testPermission(sender)) {
if ( !this.testPermission( sender ) )
{
return true; return true;
} }
// Paper start - Further improve tick handling // Paper start - Further improve tick handling
double[] tps = org.bukkit.Bukkit.getTPS(); double[] tps = org.bukkit.Bukkit.getTPS();
net.kyori.adventure.text.Component[] tpsAvg = new net.kyori.adventure.text.Component[tps.length]; Component[] tpsAvg = new Component[tps.length];
for (int i = 0; i < tps.length; i++) { for (int i = 0; i < tps.length; i++) {
tpsAvg[i] = TicksPerSecondCommand.format(tps[i]); tpsAvg[i] = TicksPerSecondCommand.format(tps[i]);
} }
net.kyori.adventure.text.TextComponent.Builder builder = net.kyori.adventure.text.Component.text(); TextComponent.Builder builder = text();
builder.append(net.kyori.adventure.text.Component.text("TPS from last 1m, 5m, 15m: ", net.kyori.adventure.text.format.NamedTextColor.GOLD)); builder.append(text("TPS from last 1m, 5m, 15m: ", NamedTextColor.GOLD));
builder.append(net.kyori.adventure.text.Component.join(net.kyori.adventure.text.JoinConfiguration.commas(true), tpsAvg)); builder.append(Component.join(JoinConfiguration.commas(true), tpsAvg));
sender.sendMessage(builder.asComponent()); sender.sendMessage(builder.asComponent());
if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) { if (args.length > 0 && args[0].equals("mem") && sender.hasPermission("bukkit.command.tpsmemory")) {
sender.sendMessage(net.kyori.adventure.text.Component.text() sender.sendMessage(text()
.append(net.kyori.adventure.text.Component.text("Current Memory Usage: ", net.kyori.adventure.text.format.NamedTextColor.GOLD)) .append(text("Current Memory Usage: ", NamedTextColor.GOLD))
.append(net.kyori.adventure.text.Component.text(((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: " + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)", net.kyori.adventure.text.format.NamedTextColor.GREEN)) .append(text(((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1024 * 1024)) + "/" + (Runtime.getRuntime().totalMemory() / (1024 * 1024)) + " mb (Max: " + (Runtime.getRuntime().maxMemory() / (1024 * 1024)) + " mb)", NamedTextColor.GREEN))
); );
if (!this.hasShownMemoryWarning) { if (!this.hasShownMemoryWarning) {
sender.sendMessage(WARN_MSG); sender.sendMessage(WARN_MSG);
@@ -57,13 +61,11 @@ public class TicksPerSecondCommand extends Command
return true; return true;
} }
private boolean hasShownMemoryWarning; // Paper private static Component format(double tps) { // Paper - Made static
private static net.kyori.adventure.text.Component format(double tps) // Paper - Made static // Paper start
{ TextColor color = ((tps > 18.0) ? NamedTextColor.GREEN : (tps > 16.0) ? NamedTextColor.YELLOW : NamedTextColor.RED);
// Paper
net.kyori.adventure.text.format.TextColor color = ( ( tps > 18.0 ) ? net.kyori.adventure.text.format.NamedTextColor.GREEN : ( tps > 16.0 ) ? net.kyori.adventure.text.format.NamedTextColor.YELLOW : net.kyori.adventure.text.format.NamedTextColor.RED );
String amount = Math.min(Math.round(tps * 100.0) / 100.0, 20.0) + (tps > 21.0 ? "*" : ""); // Paper - only print * at 21, we commonly peak to 20.02 as the tick sleep is not accurate enough, stop the noise String amount = Math.min(Math.round(tps * 100.0) / 100.0, 20.0) + (tps > 21.0 ? "*" : ""); // Paper - only print * at 21, we commonly peak to 20.02 as the tick sleep is not accurate enough, stop the noise
return net.kyori.adventure.text.Component.text(amount, color); return text(amount, color);
// Paper end // Paper end
} }
} }

View File

@@ -18,9 +18,7 @@ public final class TrackingRange {
* Gets the range an entity should be 'tracked' by players and visible in * Gets the range an entity should be 'tracked' by players and visible in
* the client. * the client.
* *
* @param entity
* @param defaultRange Default range defined by Mojang * @param defaultRange Default range defined by Mojang
* @return
*/ */
public static int getEntityTrackingRange(final Entity entity, final int defaultRange) { public static int getEntityTrackingRange(final Entity entity, final int defaultRange) {
if (defaultRange == 0) { if (defaultRange == 0) {
@@ -51,7 +49,7 @@ public final class TrackingRange {
} else { } else {
if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) { if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) {
// Exempt ender dragon // Exempt ender dragon
return ((ServerLevel) entity.getCommandSenderWorld()).getChunkSource().chunkMap.serverViewDistance; return ((ServerLevel) entity.level()).getChunkSource().chunkMap.serverViewDistance;
} }
return config.otherTrackingRange; return config.otherTrackingRange;
} }

View File

@@ -5,11 +5,12 @@ import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo; import java.lang.management.ThreadInfo;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import io.papermc.paper.configuration.GlobalConfiguration;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
public class WatchdogThread extends Thread public class WatchdogThread extends Thread {
{
private static WatchdogThread instance; private static WatchdogThread instance;
private long timeoutTime; private long timeoutTime;
@@ -21,159 +22,140 @@ public class WatchdogThread extends Thread
private volatile long lastTick; private volatile long lastTick;
private volatile boolean stopping; private volatile boolean stopping;
private WatchdogThread(long timeoutTime, boolean restart) private WatchdogThread(long timeoutTime, boolean restart) {
{
super("Paper Watchdog Thread"); super("Paper Watchdog Thread");
this.timeoutTime = timeoutTime; this.timeoutTime = timeoutTime;
this.restart = restart; this.restart = restart;
earlyWarningEvery = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper this.earlyWarningEvery = Math.min(GlobalConfiguration.get().watchdog.earlyWarningEvery, timeoutTime); // Paper
earlyWarningDelay = Math.min(io.papermc.paper.configuration.GlobalConfiguration.get().watchdog.earlyWarningDelay, timeoutTime); // Paper this.earlyWarningDelay = Math.min(GlobalConfiguration.get().watchdog.earlyWarningDelay, timeoutTime); // Paper
} }
private static long monotonicMillis() private static long monotonicMillis() {
{
return System.nanoTime() / 1000000L; return System.nanoTime() / 1000000L;
} }
public static void doStart(int timeoutTime, boolean restart) public static void doStart(int timeoutTime, boolean restart) {
{ if (WatchdogThread.instance == null) {
if ( WatchdogThread.instance == null )
{
WatchdogThread.instance = new WatchdogThread(timeoutTime * 1000L, restart); WatchdogThread.instance = new WatchdogThread(timeoutTime * 1000L, restart);
WatchdogThread.instance.start(); WatchdogThread.instance.start();
} else } else {
{
WatchdogThread.instance.timeoutTime = timeoutTime * 1000L; WatchdogThread.instance.timeoutTime = timeoutTime * 1000L;
WatchdogThread.instance.restart = restart; WatchdogThread.instance.restart = restart;
} }
} }
public static void tick() public static void tick() {
{
WatchdogThread.instance.lastTick = WatchdogThread.monotonicMillis(); WatchdogThread.instance.lastTick = WatchdogThread.monotonicMillis();
} }
public static void doStop() public static void doStop() {
{ if (WatchdogThread.instance != null) {
if ( WatchdogThread.instance != null )
{
WatchdogThread.instance.stopping = true; WatchdogThread.instance.stopping = true;
} }
} }
@Override @Override
public void run() public void run() {
{ while (!this.stopping) {
while ( !this.stopping )
{
//
// Paper start // Paper start
Logger log = Bukkit.getServer().getLogger(); Logger logger = Bukkit.getServer().getLogger();
long currentTime = WatchdogThread.monotonicMillis(); long currentTime = WatchdogThread.monotonicMillis();
if ( this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) // Paper - Add property to disable if (this.lastTick != 0 && this.timeoutTime > 0 && currentTime > this.lastTick + this.earlyWarningEvery && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
{ boolean isLongTimeout = currentTime > this.lastTick + this.timeoutTime;
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
// Don't spam early warning dumps // Don't spam early warning dumps
if ( !isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay)) continue; if (!isLongTimeout && (this.earlyWarningEvery <= 0 ||
if ( !isLongTimeout && MinecraftServer.getServer().hasStopped()) continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this... !hasStarted || currentTime < this.lastEarlyWarning + this.earlyWarningEvery ||
lastEarlyWarning = currentTime; currentTime < this.lastTick + this.earlyWarningDelay))
continue;
if (!isLongTimeout && MinecraftServer.getServer().hasStopped())
continue; // Don't spam early watchdog warnings during shutdown, we'll come back to this...
this.lastEarlyWarning = currentTime;
if (isLongTimeout) { if (isLongTimeout) {
// Paper end // Paper end
log.log( Level.SEVERE, "------------------------------" ); logger.log(Level.SEVERE, "------------------------------");
log.log( Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug." ); // Paper logger.log(Level.SEVERE, "The server has stopped responding! This is (probably) not a Paper bug."); // Paper
log.log( Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author" ); logger.log(Level.SEVERE, "If you see a plugin in the Server thread dump below, then please report it to that author");
log.log( Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring" ); logger.log(Level.SEVERE, "\t *Especially* if it looks like HTTP or MySQL operations are occurring");
log.log( Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once" ); logger.log(Level.SEVERE, "If you see a world save or edit, then it means you did far more than your server can handle at once");
log.log( Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes" ); logger.log(Level.SEVERE, "\t If this is the case, consider increasing timeout-time in spigot.yml but note that this will replace the crash with LARGE lag spikes");
log.log( Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues" ); logger.log(Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues");
log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); logger.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
log.log( Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion() ); logger.log(Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion());
//
if ( net.minecraft.world.level.Level.lastPhysicsProblem != null ) if (net.minecraft.world.level.Level.lastPhysicsProblem != null) {
{ logger.log(Level.SEVERE, "------------------------------");
log.log( Level.SEVERE, "------------------------------" ); logger.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
log.log( Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed" ); logger.log(Level.SEVERE, "near " + net.minecraft.world.level.Level.lastPhysicsProblem);
log.log( Level.SEVERE, "near " + net.minecraft.world.level.Level.lastPhysicsProblem );
} }
//
// Paper start - Warn in watchdog if an excessive velocity was ever set // Paper start - Warn in watchdog if an excessive velocity was ever set
if (org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null) { if (CraftServer.excessiveVelEx != null) {
log.log(Level.SEVERE, "------------------------------"); logger.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity"); logger.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated"); logger.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage()); logger.log(Level.SEVERE, CraftServer.excessiveVelEx.getMessage());
for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) { for (StackTraceElement stack : CraftServer.excessiveVelEx.getStackTrace()) {
log.log( Level.SEVERE, "\t\t" + stack ); logger.log(Level.SEVERE, "\t\t" + stack);
} }
} }
// Paper end // Paper end
} else } else {
{ logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---");
log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH - " + Bukkit.getServer().getVersion() + " ---"); logger.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
} }
// Paper end - Different message for short timeout // Paper end - Different message for short timeout
log.log( Level.SEVERE, "------------------------------" ); logger.log(Level.SEVERE, "------------------------------");
log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper logger.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):"); // Paper
WatchdogThread.dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); WatchdogThread.dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE), logger);
log.log( Level.SEVERE, "------------------------------" ); logger.log(Level.SEVERE, "------------------------------");
//
// Paper start - Only print full dump on long timeouts // Paper start - Only print full dump on long timeouts
if ( isLongTimeout ) if (isLongTimeout) {
{ logger.log(Level.SEVERE, "Entire Thread Dump:");
log.log( Level.SEVERE, "Entire Thread Dump:" );
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true); ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for ( ThreadInfo thread : threads ) for (ThreadInfo thread : threads) {
{ WatchdogThread.dumpThread(thread, logger);
WatchdogThread.dumpThread( thread, log );
} }
} else { } else {
log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); logger.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---");
} }
log.log( Level.SEVERE, "------------------------------" ); logger.log(Level.SEVERE, "------------------------------");
if ( isLongTimeout ) if (isLongTimeout) {
{ if (this.restart && !MinecraftServer.getServer().hasStopped()) {
if ( this.restart && !MinecraftServer.getServer().hasStopped() )
{
RestartCommand.restart(); RestartCommand.restart();
} }
break; break;
} // Paper end }
// Paper end
} }
try try {
{
sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
} catch ( InterruptedException ex ) } catch (InterruptedException ex) {
{
this.interrupt(); this.interrupt();
} }
} }
} }
private static void dumpThread(ThreadInfo thread, Logger log) private static void dumpThread(ThreadInfo thread, Logger log) {
{
log.log(Level.SEVERE, "------------------------------"); log.log(Level.SEVERE, "------------------------------");
//
log.log(Level.SEVERE, "Current Thread: " + thread.getThreadName()); log.log(Level.SEVERE, "Current Thread: " + thread.getThreadName());
log.log(Level.SEVERE, "\tPID: " + thread.getThreadId() log.log(Level.SEVERE, "\tPID: " + thread.getThreadId()
+ " | Suspended: " + thread.isSuspended() + " | Suspended: " + thread.isSuspended()
+ " | Native: " + thread.isInNative() + " | Native: " + thread.isInNative()
+ " | State: " + thread.getThreadState()); + " | State: " + thread.getThreadState());
if ( thread.getLockedMonitors().length != 0 ) if (thread.getLockedMonitors().length != 0) {
{
log.log(Level.SEVERE, "\tThread is waiting on monitor(s):"); log.log(Level.SEVERE, "\tThread is waiting on monitor(s):");
for ( MonitorInfo monitor : thread.getLockedMonitors() ) for (MonitorInfo monitor : thread.getLockedMonitors()) {
{
log.log(Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame()); log.log(Level.SEVERE, "\t\tLocked on:" + monitor.getLockedStackFrame());
} }
} }
log.log(Level.SEVERE, "\tStack:"); log.log(Level.SEVERE, "\tStack:");
//
for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace()) ) // Paper for (StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace())) { // Paper
{
log.log(Level.SEVERE, "\t\t" + stack); log.log(Level.SEVERE, "\t\t" + stack);
} }
} }