Work
This commit is contained in:
parent
380c4d2313
commit
7cdf804392
23 changed files with 199 additions and 502 deletions
|
@ -1,667 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Minecrell <minecrell@minecrell.net>
|
||||
Date: Fri, 9 Jun 2017 19:03:43 +0200
|
||||
Subject: [PATCH] Use TerminalConsoleAppender for console improvements
|
||||
|
||||
Rewrite console improvements (console colors, tab completion,
|
||||
persistent input line, ...) using JLine 3.x and TerminalConsoleAppender.
|
||||
|
||||
Also uses the new ANSIComponentSerializer to serialize components when
|
||||
logging them via the ComponentLogger, or when sending messages to the
|
||||
console, for hex color support.
|
||||
|
||||
New features:
|
||||
- Support console colors for Vanilla commands
|
||||
- Add console colors for warnings and errors
|
||||
- Server can now be turned off safely using CTRL + C. JLine catches
|
||||
the signal and the implementation shuts down the server cleanly.
|
||||
- Support console colors and persistent input line when running in
|
||||
IntelliJ IDEA
|
||||
|
||||
Other changes:
|
||||
- Server starts 1-2 seconds faster thanks to optimizations in Log4j
|
||||
configuration
|
||||
|
||||
Co-Authored-By: Emilia Kond <emilia@rymiel.space>
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 8332781de54d73cb5803828e98882a731daed81f..cadb6a2cae98a8df97c392bfbd5a1440bccd16f4 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -6,9 +6,30 @@ plugins {
|
||||
id("com.github.johnrengelman.shadow")
|
||||
}
|
||||
|
||||
+val log4jPlugins = sourceSets.create("log4jPlugins")
|
||||
+configurations.named(log4jPlugins.compileClasspathConfigurationName) {
|
||||
+ extendsFrom(configurations.compileClasspath.get())
|
||||
+}
|
||||
+val alsoShade: Configuration by configurations.creating
|
||||
+
|
||||
dependencies {
|
||||
implementation(project(":paper-api"))
|
||||
- implementation("jline:jline:2.12.1")
|
||||
+ // Paper start
|
||||
+ implementation("org.jline:jline-terminal-jansi:3.21.0")
|
||||
+ implementation("net.minecrell:terminalconsoleappender:1.3.0")
|
||||
+ implementation("net.kyori:adventure-text-serializer-ansi:4.14.0") // Keep in sync with adventureVersion from Paper-API build file
|
||||
+ implementation("net.kyori:ansi:1.0.3") // Manually bump beyond above transitive dep
|
||||
+ /*
|
||||
+ Required to add the missing Log4j2Plugins.dat file from log4j-core
|
||||
+ which has been removed by Mojang. Without it, log4j has to classload
|
||||
+ all its classes to check if they are plugins.
|
||||
+ Scanning takes about 1-2 seconds so adding this speeds up the server start.
|
||||
+ */
|
||||
+ runtimeOnly("org.apache.logging.log4j:log4j-core:2.19.0")
|
||||
+ log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins
|
||||
+ runtimeOnly(log4jPlugins.output)
|
||||
+ alsoShade(log4jPlugins.output)
|
||||
+ // Paper end
|
||||
implementation("org.apache.logging.log4j:log4j-iostreams:2.19.0") // Paper - remove exclusion
|
||||
implementation("org.ow2.asm:asm-commons:9.7")
|
||||
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
|
||||
@@ -75,7 +96,7 @@ relocation {
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
- configurations = listOf(project.configurations.vanillaServer.get())
|
||||
+ configurations = listOf(project.configurations.vanillaServer.get(), alsoShade)
|
||||
archiveClassifier.set("mojang-mapped")
|
||||
|
||||
for (relocation in relocation.relocations.get()) {
|
||||
diff --git a/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java b/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..91547f6e6fe90006713beb2818da634304bdd236
|
||||
--- /dev/null
|
||||
+++ b/src/log4jPlugins/java/io/papermc/paper/console/StripANSIConverter.java
|
||||
@@ -0,0 +1,51 @@
|
||||
+package io.papermc.paper.console;
|
||||
+
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.config.Configuration;
|
||||
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
+import org.apache.logging.log4j.core.layout.PatternLayout;
|
||||
+import org.apache.logging.log4j.core.pattern.ConverterKeys;
|
||||
+import org.apache.logging.log4j.core.pattern.LogEventPatternConverter;
|
||||
+import org.apache.logging.log4j.core.pattern.PatternConverter;
|
||||
+import org.apache.logging.log4j.core.pattern.PatternFormatter;
|
||||
+import org.apache.logging.log4j.core.pattern.PatternParser;
|
||||
+
|
||||
+import java.util.List;
|
||||
+import java.util.regex.Pattern;
|
||||
+
|
||||
+@Plugin(name = "stripAnsi", category = PatternConverter.CATEGORY)
|
||||
+@ConverterKeys({"stripAnsi"})
|
||||
+public final class StripANSIConverter extends LogEventPatternConverter {
|
||||
+ final private Pattern ANSI_PATTERN = Pattern.compile("\\e\\[[\\d;]*[^\\d;]");
|
||||
+
|
||||
+ private final List<PatternFormatter> formatters;
|
||||
+
|
||||
+ private StripANSIConverter(List<PatternFormatter> formatters) {
|
||||
+ super("stripAnsi", null);
|
||||
+ this.formatters = formatters;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void format(LogEvent event, StringBuilder toAppendTo) {
|
||||
+ int start = toAppendTo.length();
|
||||
+ for (PatternFormatter formatter : formatters) {
|
||||
+ formatter.format(event, toAppendTo);
|
||||
+ }
|
||||
+ String content = toAppendTo.substring(start);
|
||||
+ content = ANSI_PATTERN.matcher(content).replaceAll("");
|
||||
+
|
||||
+ toAppendTo.setLength(start);
|
||||
+ toAppendTo.append(content);
|
||||
+ }
|
||||
+
|
||||
+ public static StripANSIConverter newInstance(Configuration config, String[] options) {
|
||||
+ if (options.length != 1) {
|
||||
+ LOGGER.error("Incorrect number of options on stripAnsi. Expected exactly 1, received " + options.length);
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ PatternParser parser = PatternLayout.createPatternParser(config);
|
||||
+ List<PatternFormatter> formatters = parser.parse(options[0]);
|
||||
+ return new StripANSIConverter(formatters);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/console/PaperConsole.java b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..a4070b59e261f0f1ac4beec47b11492f4724bf27
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/console/PaperConsole.java
|
||||
@@ -0,0 +1,41 @@
|
||||
+package com.destroystokyo.paper.console;
|
||||
+
|
||||
+import net.minecraft.server.dedicated.DedicatedServer;
|
||||
+import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
+import org.bukkit.craftbukkit.command.ConsoleCommandCompleter;
|
||||
+import org.jline.reader.LineReader;
|
||||
+import org.jline.reader.LineReaderBuilder;
|
||||
+
|
||||
+public final class PaperConsole extends SimpleTerminalConsole {
|
||||
+
|
||||
+ private final DedicatedServer server;
|
||||
+
|
||||
+ public PaperConsole(DedicatedServer server) {
|
||||
+ this.server = server;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected LineReader buildReader(LineReaderBuilder builder) {
|
||||
+ return super.buildReader(builder
|
||||
+ .appName("Paper")
|
||||
+ .variable(LineReader.HISTORY_FILE, java.nio.file.Paths.get(".console_history"))
|
||||
+ .completer(new ConsoleCommandCompleter(this.server))
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected boolean isRunning() {
|
||||
+ return !this.server.isStopped() && this.server.isRunning();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void runCommand(String command) {
|
||||
+ this.server.handleConsoleInput(command, this.server.createCommandSourceStack());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected void shutdown() {
|
||||
+ this.server.halt(false);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8f07539a82f449ad217e316a7513a1708781fb63
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/console/TerminalConsoleCommandSender.java
|
||||
@@ -0,0 +1,26 @@
|
||||
+package com.destroystokyo.paper.console;
|
||||
+
|
||||
+import net.kyori.adventure.audience.MessageType;
|
||||
+import net.kyori.adventure.identity.Identity;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
+import org.apache.logging.log4j.LogManager;
|
||||
+import org.bukkit.craftbukkit.command.CraftConsoleCommandSender;
|
||||
+
|
||||
+public class TerminalConsoleCommandSender extends CraftConsoleCommandSender {
|
||||
+
|
||||
+ private static final ComponentLogger LOGGER = ComponentLogger.logger(LogManager.getRootLogger().getName());
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendRawMessage(String message) {
|
||||
+ final Component msg = LegacyComponentSerializer.legacySection().deserialize(message);
|
||||
+ this.sendMessage(Identity.nil(), msg, MessageType.SYSTEM);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void sendMessage(Identity identity, Component message, MessageType type) {
|
||||
+ LOGGER.info(message);
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
|
||||
index 5bacfadf01777171e51858d450a68135d422bf0f..41a87166d453ea4977ff0c15dab94c93715ade46 100644
|
||||
--- a/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
|
||||
+++ b/src/main/java/io/papermc/paper/adventure/PaperAdventure.java
|
||||
@@ -25,6 +25,7 @@ import net.kyori.adventure.text.flattener.ComponentFlattener;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.serializer.ComponentSerializer;
|
||||
+import net.kyori.adventure.text.serializer.ansi.ANSIComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
@@ -113,6 +114,7 @@ public final class PaperAdventure {
|
||||
public static final AttributeKey<Locale> LOCALE_ATTRIBUTE = AttributeKey.valueOf("adventure:locale"); // init after FLATTENER because classloading triggered here might create a logger
|
||||
@Deprecated
|
||||
public static final PlainComponentSerializer PLAIN = PlainComponentSerializer.builder().flattener(FLATTENER).build();
|
||||
+ public static final ANSIComponentSerializer ANSI_SERIALIZER = ANSIComponentSerializer.builder().flattener(FLATTENER).build();
|
||||
static final Codec<CompoundTag, String, IOException, IOException> NBT_CODEC = new Codec<CompoundTag, String, IOException, IOException>() {
|
||||
@Override
|
||||
public @NotNull CompoundTag decode(final @NotNull String encoded) throws IOException {
|
||||
diff --git a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java b/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
|
||||
index 8323f135d6bf2e1f12525e05094ffa3f2420e7e1..a143ea1e58464a3122fbd8ccafe417bdb3c31c78 100644
|
||||
--- a/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
|
||||
+++ b/src/main/java/io/papermc/paper/adventure/providers/ComponentLoggerProviderImpl.java
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.papermc.paper.adventure.providers;
|
||||
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
+import java.util.Locale;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLoggerProvider;
|
||||
+import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -15,6 +17,6 @@ public class ComponentLoggerProviderImpl implements ComponentLoggerProvider {
|
||||
}
|
||||
|
||||
private String serialize(final Component message) {
|
||||
- return PaperAdventure.asPlain(message, null);
|
||||
+ return PaperAdventure.ANSI_SERIALIZER.serialize(GlobalTranslator.render(message, Locale.getDefault()));
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
index fa1a6c2f1c11506243cfc61b519bef578e5627fc..7e4fd9fa173b24261b88fb5e898c905721e69dca 100644
|
||||
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
|
||||
@@ -155,7 +155,7 @@ import org.slf4j.Logger;
|
||||
import com.mojang.serialization.Dynamic;
|
||||
import com.mojang.serialization.Lifecycle;
|
||||
import java.util.Random;
|
||||
-import jline.console.ConsoleReader;
|
||||
+// import jline.console.ConsoleReader; // Paper
|
||||
import joptsimple.OptionSet;
|
||||
import net.minecraft.nbt.NbtException;
|
||||
import net.minecraft.nbt.ReportedNbtException;
|
||||
@@ -284,7 +284,6 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
public org.bukkit.craftbukkit.CraftServer server;
|
||||
public OptionSet options;
|
||||
public org.bukkit.command.ConsoleCommandSender console;
|
||||
- public ConsoleReader reader;
|
||||
public static int currentTick; // Paper - improve tick loop
|
||||
public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>();
|
||||
public int autosavePeriod;
|
||||
@@ -371,7 +370,9 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
this.options = options;
|
||||
this.worldLoader = worldLoader;
|
||||
this.vanillaCommandDispatcher = worldstem.dataPackResources().commands; // CraftBukkit
|
||||
+ // Paper start - Handled by TerminalConsoleAppender
|
||||
// Try to see if we're actually running in a terminal, disable jline if not
|
||||
+ /*
|
||||
if (System.console() == null && System.getProperty("jline.terminal") == null) {
|
||||
System.setProperty("jline.terminal", "jline.UnsupportedTerminal");
|
||||
Main.useJline = false;
|
||||
@@ -392,6 +393,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
MinecraftServer.LOGGER.warn((String) null, ex);
|
||||
}
|
||||
}
|
||||
+ */
|
||||
+ // Paper end
|
||||
Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this));
|
||||
// CraftBukkit end
|
||||
this.paperConfigurations = services.paperConfigurations(); // Paper - add paper configuration files
|
||||
@@ -1187,7 +1190,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
org.spigotmc.WatchdogThread.doStop(); // Spigot
|
||||
// CraftBukkit start - Restore terminal to original settings
|
||||
try {
|
||||
- this.reader.getTerminal().restore();
|
||||
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
// CraftBukkit end
|
||||
@@ -1685,7 +1688,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
@Override
|
||||
public void sendSystemMessage(Component message) {
|
||||
- MinecraftServer.LOGGER.info(message.getString());
|
||||
+ MinecraftServer.LOGGER.info(io.papermc.paper.adventure.PaperAdventure.ANSI_SERIALIZER.serialize(io.papermc.paper.adventure.PaperAdventure.asAdventure(message))); // Paper - Log message with colors
|
||||
}
|
||||
|
||||
public KeyPair getKeyPair() {
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index c8163a8645248765c0fa6c15bc8c37facdc70dc7..8edf2f049aa077792bfb507384f4cf5bb716cb64 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -98,6 +98,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
if (!org.bukkit.craftbukkit.Main.useConsole) {
|
||||
return;
|
||||
}
|
||||
+ // Paper start - Use TerminalConsoleAppender
|
||||
+ new com.destroystokyo.paper.console.PaperConsole(DedicatedServer.this).start();
|
||||
+ /*
|
||||
jline.console.ConsoleReader bufferedreader = DedicatedServer.this.reader;
|
||||
|
||||
// MC-33041, SPIGOT-5538: if System.in is not valid due to javaw, then return
|
||||
@@ -129,7 +132,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
continue;
|
||||
}
|
||||
if (s.trim().length() > 0) { // Trim to filter lines which are just spaces
|
||||
- DedicatedServer.this.handleConsoleInput(s, DedicatedServer.this.createCommandSourceStack());
|
||||
+ DedicatedServer.this.issueCommand(s, DedicatedServer.this.getServerCommandListener());
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
@@ -137,6 +140,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
DedicatedServer.LOGGER.error("Exception handling console input", ioexception);
|
||||
}
|
||||
|
||||
+ */
|
||||
+ // Paper end
|
||||
}
|
||||
};
|
||||
|
||||
@@ -148,6 +153,9 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
}
|
||||
global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler());
|
||||
|
||||
+ // Paper start - Not needed with TerminalConsoleAppender
|
||||
+ final org.apache.logging.log4j.Logger logger = LogManager.getRootLogger();
|
||||
+ /*
|
||||
final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger());
|
||||
for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) {
|
||||
if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) {
|
||||
@@ -156,6 +164,8 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
}
|
||||
|
||||
new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader).start();
|
||||
+ */
|
||||
+ // Paper end
|
||||
|
||||
System.setOut(IoBuilder.forLogger(logger).setLevel(Level.INFO).buildPrintStream());
|
||||
System.setErr(IoBuilder.forLogger(logger).setLevel(Level.WARN).buildPrintStream());
|
||||
diff --git a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
||||
index 75083eeb9b413e6dd5375007360dce6857a08fff..d292fdb165436f0b9b46b32110f5e09ad0e517a1 100644
|
||||
--- a/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
||||
+++ b/src/main/java/net/minecraft/server/gui/MinecraftServerGui.java
|
||||
@@ -166,7 +166,7 @@ public class MinecraftServerGui extends JComponent {
|
||||
this.finalizers.forEach(Runnable::run);
|
||||
}
|
||||
|
||||
- private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\x1B\\[([0-9]{1,2}(;[0-9]{1,2})*)?[m|K]"); // CraftBukkit
|
||||
+ private static final java.util.regex.Pattern ANSI = java.util.regex.Pattern.compile("\\e\\[[\\d;]*[^\\d;]"); // CraftBukkit // Paper
|
||||
public void print(JTextArea textArea, JScrollPane scrollPane, String message) {
|
||||
if (!SwingUtilities.isEventDispatchThread()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
index 1c313dbfc501ce5782fb2d7500b9af35f0855c46..e35e202d49f01c0c97b8c3e41a14254cbd4f2c49 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
|
||||
@@ -161,8 +161,7 @@ public abstract class PlayerList {
|
||||
|
||||
public PlayerList(MinecraftServer server, LayeredRegistryAccess<RegistryLayer> registryManager, PlayerDataStorage saveHandler, int maxPlayers) {
|
||||
this.cserver = server.server = new CraftServer((DedicatedServer) server, this);
|
||||
- server.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance();
|
||||
- server.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(server.server));
|
||||
+ server.console = new com.destroystokyo.paper.console.TerminalConsoleCommandSender(); // Paper
|
||||
// CraftBukkit end
|
||||
|
||||
this.bans = new UserBanList(PlayerList.USERBANLIST_FILE);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 827579f59d34b61912a67b40624f0f41524185fd..f7d937c6a11e24afe767411428210f3c042a6f78 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -42,7 +42,7 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.imageio.ImageIO;
|
||||
-import jline.console.ConsoleReader;
|
||||
+// import jline.console.ConsoleReader;
|
||||
import net.minecraft.advancements.AdvancementHolder;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
@@ -1341,9 +1341,13 @@ public final class CraftServer implements Server {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
+ // Paper start - JLine update
|
||||
+ /*
|
||||
public ConsoleReader getReader() {
|
||||
return this.console.reader;
|
||||
}
|
||||
+ */
|
||||
+ // Paper end
|
||||
|
||||
@Override
|
||||
public PluginCommand getPluginCommand(String name) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/Main.java b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
index db36453ff46afded45268b3701488e23305aa2eb..e5df5469f20ccffcbe9cd227df6f5c60e82206bf 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/Main.java
|
||||
@@ -13,7 +13,6 @@ import java.util.logging.Logger;
|
||||
import joptsimple.OptionParser;
|
||||
import joptsimple.OptionSet;
|
||||
import joptsimple.util.PathConverter;
|
||||
-import org.fusesource.jansi.AnsiConsole;
|
||||
|
||||
public class Main {
|
||||
public static boolean useJline = true;
|
||||
@@ -220,6 +219,8 @@ public class Main {
|
||||
}
|
||||
|
||||
try {
|
||||
+ // Paper start - Handled by TerminalConsoleAppender
|
||||
+ /*
|
||||
// This trick bypasses Maven Shade's clever rewriting of our getProperty call when using String literals
|
||||
String jline_UnsupportedTerminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd', 'T', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
||||
String jline_terminal = new String(new char[]{'j', 'l', 'i', 'n', 'e', '.', 't', 'e', 'r', 'm', 'i', 'n', 'a', 'l'});
|
||||
@@ -237,9 +238,18 @@ public class Main {
|
||||
// This ensures the terminal literal will always match the jline implementation
|
||||
System.setProperty(jline.TerminalFactory.JLINE_TERMINAL, jline.UnsupportedTerminal.class.getName());
|
||||
}
|
||||
+ */
|
||||
+
|
||||
+ if (options.has("nojline")) {
|
||||
+ System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false");
|
||||
+ useJline = false;
|
||||
+ }
|
||||
+ // Paper end
|
||||
|
||||
if (options.has("noconsole")) {
|
||||
Main.useConsole = false;
|
||||
+ useJline = false; // Paper
|
||||
+ System.setProperty(net.minecrell.terminalconsole.TerminalConsoleAppender.JLINE_OVERRIDE_PROPERTY, "false"); // Paper
|
||||
}
|
||||
|
||||
if (Main.class.getPackage().getImplementationVendor() != null && System.getProperty("IReallyKnowWhatIAmDoingISwear") == null) {
|
||||
@@ -268,6 +278,7 @@ public class Main {
|
||||
}
|
||||
// Paper end - Log Java and OS versioning to help with debugging plugin issues
|
||||
|
||||
+ System.setProperty("library.jansi.version", "Paper"); // Paper - set meaningless jansi version to prevent git builds from crashing on Windows
|
||||
System.out.println("Loading libraries, please wait...");
|
||||
net.minecraft.server.Main.main(options);
|
||||
} catch (Throwable t) {
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
|
||||
index bcf1c36d07b79520a39643d3a01020a67b1c9ef2..217e7e3b9db04c7fc5f6518f39cc9d3488f9128d 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/ColouredConsoleSender.java
|
||||
@@ -5,15 +5,13 @@ import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
-import jline.Terminal;
|
||||
+//import jline.Terminal;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
-import org.fusesource.jansi.Ansi;
|
||||
-import org.fusesource.jansi.Ansi.Attribute;
|
||||
|
||||
-public class ColouredConsoleSender extends CraftConsoleCommandSender {
|
||||
+public class ColouredConsoleSender /*extends CraftConsoleCommandSender */{/* // Paper - disable
|
||||
private final Terminal terminal;
|
||||
private final Map<ChatColor, String> replacements = new EnumMap<ChatColor, String>(ChatColor.class);
|
||||
private final ChatColor[] colors = ChatColor.values();
|
||||
@@ -93,5 +91,5 @@ public class ColouredConsoleSender extends CraftConsoleCommandSender {
|
||||
} else {
|
||||
return new ColouredConsoleSender();
|
||||
}
|
||||
- }
|
||||
+ }*/ // Paper
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
index 0b4c62387c1093652ac15b64a8703249de4cf088..d24acf28f5ed023acc550bcf877e4b9800ec8c9f 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/command/ConsoleCommandCompleter.java
|
||||
@@ -4,50 +4,73 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
-import jline.console.completer.Completer;
|
||||
+import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.util.Waitable;
|
||||
+
|
||||
+// Paper start - JLine update
|
||||
+import org.jline.reader.Candidate;
|
||||
+import org.jline.reader.Completer;
|
||||
+import org.jline.reader.LineReader;
|
||||
+import org.jline.reader.ParsedLine;
|
||||
+// Paper end
|
||||
import org.bukkit.event.server.TabCompleteEvent;
|
||||
|
||||
public class ConsoleCommandCompleter implements Completer {
|
||||
- private final CraftServer server;
|
||||
+ private final DedicatedServer server; // Paper - CraftServer -> DedicatedServer
|
||||
|
||||
- public ConsoleCommandCompleter(CraftServer server) {
|
||||
+ public ConsoleCommandCompleter(DedicatedServer server) { // Paper - CraftServer -> DedicatedServer
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
+ // Paper start - Change method signature for JLine update
|
||||
@Override
|
||||
- public int complete(final String buffer, final int cursor, final List<CharSequence> candidates) {
|
||||
+ public void complete(LineReader reader, ParsedLine line, List<Candidate> candidates) {
|
||||
+ final CraftServer server = this.server.server;
|
||||
+ final String buffer = line.line();
|
||||
+ // Paper end
|
||||
Waitable<List<String>> waitable = new Waitable<List<String>>() {
|
||||
@Override
|
||||
protected List<String> evaluate() {
|
||||
- List<String> offers = ConsoleCommandCompleter.this.server.getCommandMap().tabComplete(ConsoleCommandCompleter.this.server.getConsoleSender(), buffer);
|
||||
+ List<String> offers = server.getCommandMap().tabComplete(server.getConsoleSender(), buffer); // Paper - Remove "this."
|
||||
|
||||
- TabCompleteEvent tabEvent = new TabCompleteEvent(ConsoleCommandCompleter.this.server.getConsoleSender(), buffer, (offers == null) ? Collections.EMPTY_LIST : offers);
|
||||
- ConsoleCommandCompleter.this.server.getPluginManager().callEvent(tabEvent);
|
||||
+ TabCompleteEvent tabEvent = new TabCompleteEvent(server.getConsoleSender(), buffer, (offers == null) ? Collections.EMPTY_LIST : offers); // Paper - Remove "this."
|
||||
+ server.getPluginManager().callEvent(tabEvent); // Paper - Remove "this."
|
||||
|
||||
return tabEvent.isCancelled() ? Collections.EMPTY_LIST : tabEvent.getCompletions();
|
||||
}
|
||||
};
|
||||
- this.server.getServer().processQueue.add(waitable);
|
||||
+ server.getServer().processQueue.add(waitable); // Paper - Remove "this."
|
||||
try {
|
||||
List<String> offers = waitable.get();
|
||||
if (offers == null) {
|
||||
- return cursor;
|
||||
+ return; // Paper - Method returns void
|
||||
+ }
|
||||
+
|
||||
+ // Paper start - JLine update
|
||||
+ for (String completion : offers) {
|
||||
+ if (completion.isEmpty()) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ candidates.add(new Candidate(completion));
|
||||
}
|
||||
- candidates.addAll(offers);
|
||||
+ // Paper end
|
||||
|
||||
+ // Paper start - JLine handles cursor now
|
||||
+ /*
|
||||
final int lastSpace = buffer.lastIndexOf(' ');
|
||||
if (lastSpace == -1) {
|
||||
return cursor - buffer.length();
|
||||
} else {
|
||||
return cursor - (buffer.length() - lastSpace - 1);
|
||||
}
|
||||
+ */
|
||||
+ // Paper end
|
||||
} catch (ExecutionException e) {
|
||||
- this.server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e);
|
||||
+ server.getLogger().log(Level.WARNING, "Unhandled exception when tab completing", e); // Paper - Remove "this."
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
- return cursor;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
index 8390f5b5b957b5435efece26507a89756d0a7b3c..c6e8441e299f477ddb22c1ce2618710763978f1a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
|
||||
@@ -16,7 +16,7 @@ public class ServerShutdownThread extends Thread {
|
||||
this.server.close();
|
||||
} finally {
|
||||
try {
|
||||
- this.server.reader.getTerminal().restore();
|
||||
+ net.minecrell.terminalconsole.TerminalConsoleAppender.close(); // Paper - Use TerminalConsoleAppender
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
|
||||
index 1d8b279f3cbe6fde6bb1bfc4985c4133b0d4a95d..cdc52bbe5c6ae4688615a7732b10071f7f51718e 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/TerminalConsoleWriterThread.java
|
||||
@@ -5,12 +5,12 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
-import jline.console.ConsoleReader;
|
||||
+//import jline.console.ConsoleReader;
|
||||
import org.bukkit.craftbukkit.Main;
|
||||
-import org.fusesource.jansi.Ansi;
|
||||
-import org.fusesource.jansi.Ansi.Erase;
|
||||
+//import org.fusesource.jansi.Ansi;
|
||||
+//import org.fusesource.jansi.Ansi.Erase;
|
||||
|
||||
-public class TerminalConsoleWriterThread extends Thread {
|
||||
+public class TerminalConsoleWriterThread /*extends Thread*/ {/* // Paper - disable
|
||||
private final ConsoleReader reader;
|
||||
private final OutputStream output;
|
||||
|
||||
@@ -54,5 +54,5 @@ public class TerminalConsoleWriterThread extends Thread {
|
||||
Logger.getLogger(TerminalConsoleWriterThread.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
- }
|
||||
+ }*/
|
||||
}
|
||||
diff --git a/src/main/resources/log4j2.component.properties b/src/main/resources/log4j2.component.properties
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0694b21465fb9e4164e71862ff24b62241b191f2
|
||||
--- /dev/null
|
||||
+++ b/src/main/resources/log4j2.component.properties
|
||||
@@ -0,0 +1 @@
|
||||
+log4j.skipJansi=true
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index 722ca84968cbbbdeffd09939abff0cccd0a84010..a994ec0f8621b1f267b40049306f63479c050e2f 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -1,17 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN" packages="com.mojang.util">
|
||||
<Appenders>
|
||||
- <Console name="SysOut" target="SYSTEM_OUT">
|
||||
- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
|
||||
- </Console>
|
||||
<Queue name="ServerGuiConsole">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
|
||||
</Queue>
|
||||
- <Queue name="TerminalConsole">
|
||||
- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
|
||||
- </Queue>
|
||||
+ <TerminalConsole name="TerminalConsole">
|
||||
+ <PatternLayout pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx}" />
|
||||
+ </TerminalConsole>
|
||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
|
||||
+ <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n" />
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<OnStartupTriggeringPolicy />
|
||||
@@ -24,10 +21,9 @@
|
||||
<filters>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
|
||||
</filters>
|
||||
- <AppenderRef ref="SysOut" level="info"/>
|
||||
<AppenderRef ref="File"/>
|
||||
- <AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
<AppenderRef ref="TerminalConsole" level="info"/>
|
||||
+ <AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -1,71 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Minecrell <minecrell@minecrell.net>
|
||||
Date: Thu, 21 Sep 2017 16:14:55 +0200
|
||||
Subject: [PATCH] Handle plugin prefixes using Log4J configuration
|
||||
|
||||
Display logger name in the console for all loggers except the
|
||||
root logger, Bukkit's logger ("Minecraft") and Minecraft loggers.
|
||||
Since plugins now use the plugin name as logger name this will
|
||||
restore the plugin prefixes without having to prepend them manually
|
||||
to the log messages.
|
||||
|
||||
Logger prefixes are shown by default for all loggers except for
|
||||
the root logger, the Minecraft/Mojang loggers and the Bukkit loggers.
|
||||
This may cause additional prefixes to be disabled for plugins bypassing
|
||||
the plugin logger.
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index cadb6a2cae98a8df97c392bfbd5a1440bccd16f4..ca6e0c77acbcf3b32eb8b5c0f86701f52dfeea0c 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -25,7 +25,7 @@ dependencies {
|
||||
all its classes to check if they are plugins.
|
||||
Scanning takes about 1-2 seconds so adding this speeds up the server start.
|
||||
*/
|
||||
- runtimeOnly("org.apache.logging.log4j:log4j-core:2.19.0")
|
||||
+ implementation("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - implementation
|
||||
log4jPlugins.annotationProcessorConfigurationName("org.apache.logging.log4j:log4j-core:2.19.0") // Paper - Needed to generate meta for our Log4j plugins
|
||||
runtimeOnly(log4jPlugins.output)
|
||||
alsoShade(log4jPlugins.output)
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
index bd4c744c597201267418b07b9bcb522805c485eb..0f391af1274926ef6316bd06458767a27640f54a 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
@@ -290,7 +290,7 @@ public class SpigotConfig
|
||||
private static void playerSample()
|
||||
{
|
||||
SpigotConfig.playerSample = SpigotConfig.getInt( "settings.sample-count", 12 );
|
||||
- System.out.println( "Server Ping Player Sample Count: " + SpigotConfig.playerSample );
|
||||
+ Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger
|
||||
}
|
||||
|
||||
public static int playerShuffle;
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index a994ec0f8621b1f267b40049306f63479c050e2f..bba2de3f3feb5e6a876f9df1eee6da0c7d93e271 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -5,10 +5,22 @@
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
|
||||
</Queue>
|
||||
<TerminalConsole name="TerminalConsole">
|
||||
- <PatternLayout pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx}" />
|
||||
+ <PatternLayout>
|
||||
+ <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %msg%n%xEx}">
|
||||
+ <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
||||
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
|
||||
+ pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx}" />
|
||||
+ </LoggerNamePatternSelector>
|
||||
+ </PatternLayout>
|
||||
</TerminalConsole>
|
||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
- <PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n" />
|
||||
+ <PatternLayout>
|
||||
+ <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %stripAnsi{%msg}%n">
|
||||
+ <!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
||||
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
|
||||
+ pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n" />
|
||||
+ </LoggerNamePatternSelector>
|
||||
+ </PatternLayout>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy />
|
||||
<OnStartupTriggeringPolicy />
|
|
@ -1,47 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Minecrell <minecrell@minecrell.net>
|
||||
Date: Sat, 23 Sep 2017 21:07:20 +0200
|
||||
Subject: [PATCH] Improve Log4J Configuration / Plugin Loggers
|
||||
|
||||
Add full exceptions to log4j to not truncate stack traces
|
||||
|
||||
Disable logger prefix for various plugins bypassing the plugin logger
|
||||
|
||||
Some plugins bypass the plugin logger and add the plugin prefix
|
||||
manually to the log message. Since they use other logger names
|
||||
(e.g. qualified class names) these would now also appear in the
|
||||
log. Disable the logger prefix for these plugins so the messages
|
||||
show up correctly.
|
||||
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index bba2de3f3feb5e6a876f9df1eee6da0c7d93e271..88957220d5574e5590e8a545605d76c0c7b0a10e 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -6,19 +6,21 @@
|
||||
</Queue>
|
||||
<TerminalConsole name="TerminalConsole">
|
||||
<PatternLayout>
|
||||
- <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %msg%n%xEx}">
|
||||
+ <LoggerNamePatternSelector defaultPattern="%highlightError{[%d{HH:mm:ss} %level]: [%logger] %msg%n%xEx{full}}">
|
||||
<!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
||||
- <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
|
||||
- pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx}" />
|
||||
+ <!-- Disable prefix for various plugins that bypass the plugin logger -->
|
||||
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
|
||||
+ pattern="%highlightError{[%d{HH:mm:ss} %level]: %msg%n%xEx{full}}" />
|
||||
</LoggerNamePatternSelector>
|
||||
</PatternLayout>
|
||||
</TerminalConsole>
|
||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<PatternLayout>
|
||||
- <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %stripAnsi{%msg}%n">
|
||||
+ <LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level]: [%logger] %stripAnsi{%msg}%n%xEx{full}">
|
||||
<!-- Log root, Minecraft, Mojang and Bukkit loggers without prefix -->
|
||||
- <PatternMatch key=",net.minecraft.,Minecraft,com.mojang."
|
||||
- pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n" />
|
||||
+ <!-- Disable prefix for various plugins that bypass the plugin logger -->
|
||||
+ <PatternMatch key=",net.minecraft.,Minecraft,com.mojang.,com.sk89q.,ru.tehkode.,Minecraft.AWE"
|
||||
+ pattern="[%d{HH:mm:ss}] [%t/%level]: %stripAnsi{%msg}%n%xEx{full}" />
|
||||
</LoggerNamePatternSelector>
|
||||
</PatternLayout>
|
||||
<Policies>
|
|
@ -1,44 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Thu, 12 Aug 2021 04:46:41 -0700
|
||||
Subject: [PATCH] Use AsyncAppender to keep logging IO off main thread
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index ca6e0c77acbcf3b32eb8b5c0f86701f52dfeea0c..470c55412e7a4f09235947adc72944b903d6543d 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -36,6 +36,7 @@ dependencies {
|
||||
implementation("commons-lang:commons-lang:2.6")
|
||||
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
|
||||
runtimeOnly("com.mysql:mysql-connector-j:8.2.0")
|
||||
+ runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
|
||||
runtimeOnly("org.apache.maven:maven-resolver-provider:3.9.6")
|
||||
runtimeOnly("org.apache.maven.resolver:maven-resolver-connector-basic:1.9.18")
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index 88957220d5574e5590e8a545605d76c0c7b0a10e..ea4e2161c0bd43884055cc6b8d70b2139f70e720 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -29,15 +29,18 @@
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="1000"/>
|
||||
</RollingRandomAccessFile>
|
||||
+ <Async name="Async">
|
||||
+ <AppenderRef ref="File"/>
|
||||
+ <AppenderRef ref="TerminalConsole" level="info"/>
|
||||
+ <AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
+ </Async>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<filters>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
|
||||
</filters>
|
||||
- <AppenderRef ref="File"/>
|
||||
- <AppenderRef ref="TerminalConsole" level="info"/>
|
||||
- <AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
+ <AppenderRef ref="Async"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
|
@ -1,681 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Sun, 20 Jun 2021 18:19:09 -0700
|
||||
Subject: [PATCH] Deobfuscate stacktraces in log messages, crash reports, and
|
||||
etc.
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 9cd80bc7f2b47e1044eef2592274e38d1988ff5d..2b3da4b69e89a6b98ec98820e55044fefdafc061 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -35,6 +35,7 @@ dependencies {
|
||||
implementation("org.ow2.asm:asm-commons:9.7")
|
||||
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
|
||||
implementation("commons-lang:commons-lang:2.6")
|
||||
+ implementation("net.fabricmc:mapping-io:0.5.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
||||
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
|
||||
runtimeOnly("com.mysql:mysql-connector-j:8.2.0")
|
||||
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
@@ -121,6 +122,18 @@ tasks.check {
|
||||
}
|
||||
// Paper end
|
||||
|
||||
+// Paper start - include reobf mappings in jar for stacktrace deobfuscation
|
||||
+val includeMappings = tasks.register<io.papermc.paperweight.tasks.IncludeMappings>("includeMappings") {
|
||||
+ inputJar.set(tasks.fixJarForReobf.flatMap { it.outputJar })
|
||||
+ mappings.set(tasks.reobfJar.flatMap { it.mappingsFile })
|
||||
+ mappingsDest.set("META-INF/mappings/reobf.tiny")
|
||||
+}
|
||||
+
|
||||
+tasks.reobfJar {
|
||||
+ inputJar.set(includeMappings.flatMap { it.outputJar })
|
||||
+}
|
||||
+// Paper end - include reobf mappings in jar for stacktrace deobfuscation
|
||||
+
|
||||
tasks.test {
|
||||
exclude("org/bukkit/craftbukkit/inventory/ItemStack*Test.class")
|
||||
useJUnitPlatform()
|
||||
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..66b6011ee3684695b2ab9292961c80bf2a420ee9
|
||||
--- /dev/null
|
||||
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/StacktraceDeobfuscatingRewritePolicy.java
|
||||
@@ -0,0 +1,66 @@
|
||||
+package io.papermc.paper.logging;
|
||||
+
|
||||
+import java.lang.invoke.MethodHandle;
|
||||
+import java.lang.invoke.MethodHandles;
|
||||
+import java.lang.invoke.VarHandle;
|
||||
+import org.apache.logging.log4j.core.Core;
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
|
||||
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
+import org.apache.logging.log4j.core.impl.Log4jLogEvent;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+
|
||||
+@Plugin(
|
||||
+ name = "StacktraceDeobfuscatingRewritePolicy",
|
||||
+ category = Core.CATEGORY_NAME,
|
||||
+ elementType = "rewritePolicy",
|
||||
+ printObject = true
|
||||
+)
|
||||
+public final class StacktraceDeobfuscatingRewritePolicy implements RewritePolicy {
|
||||
+ private static final MethodHandle DEOBFUSCATE_THROWABLE;
|
||||
+
|
||||
+ static {
|
||||
+ try {
|
||||
+ final Class<?> cls = Class.forName("io.papermc.paper.util.StacktraceDeobfuscator");
|
||||
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
+ final VarHandle instanceHandle = lookup.findStaticVarHandle(cls, "INSTANCE", cls);
|
||||
+ final Object deobfuscator = instanceHandle.get();
|
||||
+ DEOBFUSCATE_THROWABLE = lookup
|
||||
+ .unreflect(cls.getDeclaredMethod("deobfuscateThrowable", Throwable.class))
|
||||
+ .bindTo(deobfuscator);
|
||||
+ } catch (final ReflectiveOperationException ex) {
|
||||
+ throw new IllegalStateException(ex);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private StacktraceDeobfuscatingRewritePolicy() {
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NonNull LogEvent rewrite(final @NonNull LogEvent rewrite) {
|
||||
+ final Throwable thrown = rewrite.getThrown();
|
||||
+ if (thrown != null) {
|
||||
+ deobfuscateThrowable(thrown);
|
||||
+ return new Log4jLogEvent.Builder(rewrite)
|
||||
+ .setThrownProxy(null)
|
||||
+ .build();
|
||||
+ }
|
||||
+ return rewrite;
|
||||
+ }
|
||||
+
|
||||
+ private static void deobfuscateThrowable(final Throwable thrown) {
|
||||
+ try {
|
||||
+ DEOBFUSCATE_THROWABLE.invoke(thrown);
|
||||
+ } catch (final Error e) {
|
||||
+ throw e;
|
||||
+ } catch (final Throwable e) {
|
||||
+ throw new RuntimeException(e);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @PluginFactory
|
||||
+ public static @NonNull StacktraceDeobfuscatingRewritePolicy createPolicy() {
|
||||
+ return new StacktraceDeobfuscatingRewritePolicy();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
index 404a8fd128043527d23f22ee26f7c8c739f09089..9f24003fffee14592e5ef22e75ec9826428438e6 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||
@@ -91,7 +91,7 @@ public class SyncLoadFinder {
|
||||
|
||||
final JsonArray traces = new JsonArray();
|
||||
|
||||
- for (StackTraceElement element : pair.getFirst().stacktrace) {
|
||||
+ for (StackTraceElement element : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(pair.getFirst().stacktrace)) {
|
||||
traces.add(String.valueOf(element));
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e8ff684d8bd994c64ff34f20e1e0601b678244c1
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
@@ -0,0 +1,147 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.io.InputStreamReader;
|
||||
+import java.nio.charset.StandardCharsets;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Map;
|
||||
+import java.util.Objects;
|
||||
+import java.util.Set;
|
||||
+import java.util.function.Function;
|
||||
+import java.util.stream.Collectors;
|
||||
+import net.fabricmc.mappingio.MappingReader;
|
||||
+import net.fabricmc.mappingio.format.MappingFormat;
|
||||
+import net.fabricmc.mappingio.tree.MappingTree;
|
||||
+import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public enum ObfHelper {
|
||||
+ INSTANCE;
|
||||
+
|
||||
+ public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn";
|
||||
+ public static final String SPIGOT_NAMESPACE = "spigot";
|
||||
+
|
||||
+ private final @Nullable Map<String, ClassMapping> mappingsByObfName;
|
||||
+ private final @Nullable Map<String, ClassMapping> mappingsByMojangName;
|
||||
+
|
||||
+ ObfHelper() {
|
||||
+ final @Nullable Set<ClassMapping> maps = loadMappingsIfPresent();
|
||||
+ if (maps != null) {
|
||||
+ this.mappingsByObfName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::obfName, map -> map));
|
||||
+ this.mappingsByMojangName = maps.stream().collect(Collectors.toUnmodifiableMap(ClassMapping::mojangName, map -> map));
|
||||
+ } else {
|
||||
+ this.mappingsByObfName = null;
|
||||
+ this.mappingsByMojangName = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable Map<String, ClassMapping> mappingsByObfName() {
|
||||
+ return this.mappingsByObfName;
|
||||
+ }
|
||||
+
|
||||
+ public @Nullable Map<String, ClassMapping> mappingsByMojangName() {
|
||||
+ return this.mappingsByMojangName;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Attempts to get the obf name for a given class by its Mojang name. Will
|
||||
+ * return the input string if mappings are not present.
|
||||
+ *
|
||||
+ * @param fullyQualifiedMojangName fully qualified class name (dotted)
|
||||
+ * @return mapped or original fully qualified (dotted) class name
|
||||
+ */
|
||||
+ public String reobfClassName(final String fullyQualifiedMojangName) {
|
||||
+ if (this.mappingsByMojangName == null) {
|
||||
+ return fullyQualifiedMojangName;
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = this.mappingsByMojangName.get(fullyQualifiedMojangName);
|
||||
+ if (map == null) {
|
||||
+ return fullyQualifiedMojangName;
|
||||
+ }
|
||||
+
|
||||
+ return map.obfName();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Attempts to get the Mojang name for a given class by its obf name. Will
|
||||
+ * return the input string if mappings are not present.
|
||||
+ *
|
||||
+ * @param fullyQualifiedObfName fully qualified class name (dotted)
|
||||
+ * @return mapped or original fully qualified (dotted) class name
|
||||
+ */
|
||||
+ public String deobfClassName(final String fullyQualifiedObfName) {
|
||||
+ if (this.mappingsByObfName == null) {
|
||||
+ return fullyQualifiedObfName;
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = this.mappingsByObfName.get(fullyQualifiedObfName);
|
||||
+ if (map == null) {
|
||||
+ return fullyQualifiedObfName;
|
||||
+ }
|
||||
+
|
||||
+ return map.mojangName();
|
||||
+ }
|
||||
+
|
||||
+ private static @Nullable Set<ClassMapping> loadMappingsIfPresent() {
|
||||
+ try (final @Nullable InputStream mappingsInputStream = ObfHelper.class.getClassLoader().getResourceAsStream("META-INF/mappings/reobf.tiny")) {
|
||||
+ if (mappingsInputStream == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ final MemoryMappingTree tree = new MemoryMappingTree();
|
||||
+ MappingReader.read(new InputStreamReader(mappingsInputStream, StandardCharsets.UTF_8), MappingFormat.TINY_2_FILE, tree);
|
||||
+ final Set<ClassMapping> classes = new HashSet<>();
|
||||
+
|
||||
+ final StringPool pool = new StringPool();
|
||||
+ for (final MappingTree.ClassMapping cls : tree.getClasses()) {
|
||||
+ final Map<String, String> methods = new HashMap<>();
|
||||
+
|
||||
+ for (final MappingTree.MethodMapping methodMapping : cls.getMethods()) {
|
||||
+ methods.put(
|
||||
+ pool.string(methodKey(
|
||||
+ Objects.requireNonNull(methodMapping.getName(SPIGOT_NAMESPACE)),
|
||||
+ Objects.requireNonNull(methodMapping.getDesc(SPIGOT_NAMESPACE))
|
||||
+ )),
|
||||
+ pool.string(Objects.requireNonNull(methodMapping.getName(MOJANG_PLUS_YARN_NAMESPACE)))
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ final ClassMapping map = new ClassMapping(
|
||||
+ Objects.requireNonNull(cls.getName(SPIGOT_NAMESPACE)).replace('/', '.'),
|
||||
+ Objects.requireNonNull(cls.getName(MOJANG_PLUS_YARN_NAMESPACE)).replace('/', '.'),
|
||||
+ Map.copyOf(methods)
|
||||
+ );
|
||||
+ classes.add(map);
|
||||
+ }
|
||||
+
|
||||
+ return Set.copyOf(classes);
|
||||
+ } catch (final IOException ex) {
|
||||
+ System.err.println("Failed to load mappings for stacktrace deobfuscation.");
|
||||
+ ex.printStackTrace();
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static String methodKey(final String obfName, final String obfDescriptor) {
|
||||
+ return obfName + obfDescriptor;
|
||||
+ }
|
||||
+
|
||||
+ private static final class StringPool {
|
||||
+ private final Map<String, String> pool = new HashMap<>();
|
||||
+
|
||||
+ public String string(final String string) {
|
||||
+ return this.pool.computeIfAbsent(string, Function.identity());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public record ClassMapping(
|
||||
+ String obfName,
|
||||
+ String mojangName,
|
||||
+ Map<String, String> methodsByObf
|
||||
+ ) {}
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..eb910d4abf91488fa7cf1f5d47e0ee916c47f512
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
@@ -0,0 +1,163 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import io.papermc.paper.configuration.GlobalConfiguration;
|
||||
+import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
+import it.unimi.dsi.fastutil.ints.IntList;
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.util.Collections;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.LinkedHashMap;
|
||||
+import java.util.Map;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import org.objectweb.asm.ClassReader;
|
||||
+import org.objectweb.asm.ClassVisitor;
|
||||
+import org.objectweb.asm.Label;
|
||||
+import org.objectweb.asm.MethodVisitor;
|
||||
+import org.objectweb.asm.Opcodes;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public enum StacktraceDeobfuscator {
|
||||
+ INSTANCE;
|
||||
+
|
||||
+ private final Map<Class<?>, Map<String, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
|
||||
+ @Override
|
||||
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<String, IntList>> eldest) {
|
||||
+ return this.size() > 127;
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ public void deobfuscateThrowable(final Throwable throwable) {
|
||||
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ throwable.setStackTrace(this.deobfuscateStacktrace(throwable.getStackTrace()));
|
||||
+ final Throwable cause = throwable.getCause();
|
||||
+ if (cause != null) {
|
||||
+ this.deobfuscateThrowable(cause);
|
||||
+ }
|
||||
+ for (final Throwable suppressed : throwable.getSuppressed()) {
|
||||
+ this.deobfuscateThrowable(suppressed);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public StackTraceElement[] deobfuscateStacktrace(final StackTraceElement[] traceElements) {
|
||||
+ if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
||||
+ return traceElements;
|
||||
+ }
|
||||
+
|
||||
+ final @Nullable Map<String, ObfHelper.ClassMapping> mappings = ObfHelper.INSTANCE.mappingsByObfName();
|
||||
+ if (mappings == null || traceElements.length == 0) {
|
||||
+ return traceElements;
|
||||
+ }
|
||||
+ final StackTraceElement[] result = new StackTraceElement[traceElements.length];
|
||||
+ for (int i = 0; i < traceElements.length; i++) {
|
||||
+ final StackTraceElement element = traceElements[i];
|
||||
+
|
||||
+ final String className = element.getClassName();
|
||||
+ final String methodName = element.getMethodName();
|
||||
+
|
||||
+ final ObfHelper.ClassMapping classMapping = mappings.get(className);
|
||||
+ if (classMapping == null) {
|
||||
+ result[i] = element;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ final Class<?> clazz;
|
||||
+ try {
|
||||
+ clazz = Class.forName(className);
|
||||
+ } catch (final ClassNotFoundException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ final @Nullable String methodKey = this.determineMethodForLine(clazz, element.getLineNumber());
|
||||
+ final @Nullable String mappedMethodName = methodKey == null ? null : classMapping.methodsByObf().get(methodKey);
|
||||
+
|
||||
+ result[i] = new StackTraceElement(
|
||||
+ element.getClassLoaderName(),
|
||||
+ element.getModuleName(),
|
||||
+ element.getModuleVersion(),
|
||||
+ classMapping.mojangName(),
|
||||
+ mappedMethodName != null ? mappedMethodName : methodName,
|
||||
+ sourceFileName(classMapping.mojangName()),
|
||||
+ element.getLineNumber()
|
||||
+ );
|
||||
+ }
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable String determineMethodForLine(final Class<?> clazz, final int lineNumber) {
|
||||
+ final Map<String, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
|
||||
+ for (final var entry : lineMap.entrySet()) {
|
||||
+ final String methodKey = entry.getKey();
|
||||
+ final IntList lines = entry.getValue();
|
||||
+ for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
+ final int num = lines.getInt(i);
|
||||
+ if (num == lineNumber) {
|
||||
+ return methodKey;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private static String sourceFileName(final String fullClassName) {
|
||||
+ final int dot = fullClassName.lastIndexOf('.');
|
||||
+ final String className = dot == -1
|
||||
+ ? fullClassName
|
||||
+ : fullClassName.substring(dot + 1);
|
||||
+ final String rootClassName = className.split("\\$")[0];
|
||||
+ return rootClassName + ".java";
|
||||
+ }
|
||||
+
|
||||
+ private static Map<String, IntList> buildLineMap(final Class<?> key) {
|
||||
+ final Map<String, IntList> lineMap = new HashMap<>();
|
||||
+ final class LineCollectingMethodVisitor extends MethodVisitor {
|
||||
+ private final IntList lines = new IntArrayList();
|
||||
+ private final String name;
|
||||
+ private final String descriptor;
|
||||
+
|
||||
+ LineCollectingMethodVisitor(String name, String descriptor) {
|
||||
+ super(Opcodes.ASM9);
|
||||
+ this.name = name;
|
||||
+ this.descriptor = descriptor;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void visitLineNumber(int line, Label start) {
|
||||
+ super.visitLineNumber(line, start);
|
||||
+ this.lines.add(line);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void visitEnd() {
|
||||
+ super.visitEnd();
|
||||
+ lineMap.put(ObfHelper.methodKey(this.name, this.descriptor), this.lines);
|
||||
+ }
|
||||
+ }
|
||||
+ final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9) {
|
||||
+ @Override
|
||||
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
+ return new LineCollectingMethodVisitor(name, descriptor);
|
||||
+ }
|
||||
+ };
|
||||
+ try {
|
||||
+ final @Nullable InputStream inputStream = StacktraceDeobfuscator.class.getClassLoader()
|
||||
+ .getResourceAsStream(key.getName().replace('.', '/') + ".class");
|
||||
+ if (inputStream == null) {
|
||||
+ throw new IllegalStateException("Could not find class file: " + key.getName());
|
||||
+ }
|
||||
+ final byte[] classData;
|
||||
+ try (inputStream) {
|
||||
+ classData = inputStream.readAllBytes();
|
||||
+ }
|
||||
+ final ClassReader reader = new ClassReader(classData);
|
||||
+ reader.accept(classVisitor, 0);
|
||||
+ } catch (final IOException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ return lineMap;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/TraceUtil.java b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
index 2d5494d2813b773e60ddba6790b750a9a08f21f8..0b210bdf7c1f5962afbd44195af6f84f625635e3 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/TraceUtil.java
|
||||
@@ -6,13 +6,20 @@ public final class TraceUtil {
|
||||
|
||||
public static void dumpTraceForThread(Thread thread, String reason) {
|
||||
Bukkit.getLogger().warning(thread.getName() + ": " + reason);
|
||||
- StackTraceElement[] trace = thread.getStackTrace();
|
||||
+ StackTraceElement[] trace = StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace());
|
||||
for (StackTraceElement traceElement : trace) {
|
||||
Bukkit.getLogger().warning("\tat " + traceElement);
|
||||
}
|
||||
}
|
||||
|
||||
public static void dumpTraceForThread(String reason) {
|
||||
- new Throwable(reason).printStackTrace();
|
||||
+ final Throwable throwable = new Throwable(reason);
|
||||
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(throwable);
|
||||
+ throwable.printStackTrace();
|
||||
+ }
|
||||
+
|
||||
+ public static void printStackTrace(Throwable thr) {
|
||||
+ StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(thr);
|
||||
+ thr.printStackTrace();
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/CrashReport.java b/src/main/java/net/minecraft/CrashReport.java
|
||||
index a9a0248b1bd1ac454064e977b61f9b7d80962ff8..6f2452de76e8f5fcc1367066e0e753740764eb98 100644
|
||||
--- a/src/main/java/net/minecraft/CrashReport.java
|
||||
+++ b/src/main/java/net/minecraft/CrashReport.java
|
||||
@@ -34,6 +34,7 @@ public class CrashReport {
|
||||
private final SystemReport systemReport = new SystemReport();
|
||||
|
||||
public CrashReport(String message, Throwable cause) {
|
||||
+ io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(cause); // Paper
|
||||
this.title = message;
|
||||
this.exception = cause;
|
||||
this.systemReport.setDetail("CraftBukkit Information", new org.bukkit.craftbukkit.CraftCrashReport()); // CraftBukkit
|
||||
diff --git a/src/main/java/net/minecraft/CrashReportCategory.java b/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
index f367ba058018074bfe6e4fe88bcc875ea9794d9e..2176171954609fd88f97f93408e14e018c1d6eaa 100644
|
||||
--- a/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
+++ b/src/main/java/net/minecraft/CrashReportCategory.java
|
||||
@@ -110,6 +110,7 @@ public class CrashReportCategory {
|
||||
} else {
|
||||
this.stackTrace = new StackTraceElement[stackTraceElements.length - 3 - ignoredCallCount];
|
||||
System.arraycopy(stackTraceElements, 3 + ignoredCallCount, this.stackTrace, 0, this.stackTrace.length);
|
||||
+ this.stackTrace = io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(this.stackTrace); // Paper
|
||||
return this.stackTrace.length;
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java
|
||||
index 7648b889bc488197f32545e8c3671a54102c01ec..44e62675a2d612a8d727d9ce6db5fb85d1a0bcc8 100644
|
||||
--- a/src/main/java/net/minecraft/network/Connection.java
|
||||
+++ b/src/main/java/net/minecraft/network/Connection.java
|
||||
@@ -75,13 +75,13 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_SERVERBOUND_PROTOCOL = AttributeKey.valueOf("serverbound_protocol");
|
||||
public static final AttributeKey<ConnectionProtocol.CodecData<?>> ATTRIBUTE_CLIENTBOUND_PROTOCOL = AttributeKey.valueOf("clientbound_protocol");
|
||||
public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
|
||||
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
|
||||
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
|
||||
- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
|
||||
+ return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
private final PacketFlow receiving;
|
||||
private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
|
||||
@@ -207,7 +207,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
}
|
||||
}
|
||||
- if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) throwable.printStackTrace(); // Spigot
|
||||
+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(throwable); // Spigot // Paper
|
||||
}
|
||||
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
|
||||
diff --git a/src/main/java/net/minecraft/network/PacketEncoder.java b/src/main/java/net/minecraft/network/PacketEncoder.java
|
||||
index 061eada043325142d33a0cec02e9e484d14a7fca..96d5448019d53ac3575e209c1c5c223a62a2638d 100644
|
||||
--- a/src/main/java/net/minecraft/network/PacketEncoder.java
|
||||
+++ b/src/main/java/net/minecraft/network/PacketEncoder.java
|
||||
@@ -47,7 +47,14 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
|
||||
|
||||
JvmProfiler.INSTANCE.onPacketSent(codecData.protocol(), i, channelHandlerContext.channel().remoteAddress(), k);
|
||||
} catch (Throwable var13) {
|
||||
- LOGGER.error("Packet encoding of packet ID {} threw (skippable? {})", i, packet.isSkippable(), var13); // Paper - Give proper error message
|
||||
+ // Paper start - Give proper error message
|
||||
+ String packetName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(packet.getClass().getName());
|
||||
+ if (packetName.contains(".")) {
|
||||
+ packetName = packetName.substring(packetName.lastIndexOf(".") + 1);
|
||||
+ }
|
||||
+
|
||||
+ LOGGER.error("Packet encoding of packet {} (ID: {}) threw (skippable? {})", packetName, i, packet.isSkippable(), var13);
|
||||
+ // Paper end
|
||||
if (packet.isSkippable()) {
|
||||
throw new SkipPacketException(var13);
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 28fe088d97bd5fbfcc29dcc7d2a657d54578b2be..c41c53ee3b1a8b5c2c41fc9846f557eeb4d10f9b 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -194,6 +194,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings"));
|
||||
org.spigotmc.SpigotConfig.registerCommands();
|
||||
// Spigot end
|
||||
+ io.papermc.paper.util.ObfHelper.INSTANCE.getClass(); // Paper - load mappings for stacktrace deobf and etc.
|
||||
// Paper start - initialize global and world-defaults configuration
|
||||
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||
index e2537760becdd5b3f3561f7159ed7fc2755e6260..87c5690774363edc1b7ebff10b041e48c0a6d56d 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
|
||||
@@ -137,7 +137,7 @@ public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketLis
|
||||
ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
|
||||
// Paper start - Debugging
|
||||
if (MinecraftServer.getServer().isDebugging()) {
|
||||
- exception.printStackTrace();
|
||||
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception);
|
||||
}
|
||||
// Paper end - Debugging
|
||||
this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
|
||||
diff --git a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
index 187b2cf175ba5cea94158d29b53993dc5a7c5b94..3b6bafb242d2623c15f26acdacd036478c7dc214 100644
|
||||
--- a/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
+++ b/src/main/java/net/minecraft/server/network/ServerConnectionListener.java
|
||||
@@ -52,10 +52,10 @@ public class ServerConnectionListener {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
public static final Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(() -> {
|
||||
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build());
|
||||
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
public static final Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(() -> {
|
||||
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
|
||||
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
|
||||
});
|
||||
final MinecraftServer server;
|
||||
public volatile boolean running;
|
||||
diff --git a/src/main/java/net/minecraft/server/players/OldUsersConverter.java b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
||||
index 86c88e81e275d52576122a5083b419e64cb011fc..45d4638d568ea2aee805aa1b0542533019e5870d 100644
|
||||
--- a/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
||||
+++ b/src/main/java/net/minecraft/server/players/OldUsersConverter.java
|
||||
@@ -357,7 +357,7 @@ public class OldUsersConverter {
|
||||
try {
|
||||
root = NbtIo.readCompressed(new java.io.FileInputStream(file5), NbtAccounter.unlimitedHeap());
|
||||
} catch (Exception exception) {
|
||||
- exception.printStackTrace();
|
||||
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
|
||||
com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ public class OldUsersConverter {
|
||||
try {
|
||||
NbtIo.writeCompressed(root, new java.io.FileOutputStream(file2));
|
||||
} catch (Exception exception) {
|
||||
- exception.printStackTrace();
|
||||
+ io.papermc.paper.util.TraceUtil.printStackTrace(exception); // Paper
|
||||
com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - ServerExceptionEvent
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
index 5403fc4fa2ed2526d2e67c230a46dd2a75e017be..af757309cb46af6df07872f7596b66df6d6f18d7 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/chunk/LevelChunk.java
|
||||
@@ -592,7 +592,7 @@ public class LevelChunk extends ChunkAccess {
|
||||
+ " (" + getBlockState(blockposition) + ") where there was no entity tile!\n" +
|
||||
"Chunk coordinates: " + (this.chunkPos.x * 16) + "," + (this.chunkPos.z * 16) +
|
||||
"\nWorld: " + level.getLevel().dimension().location());
|
||||
- e.printStackTrace();
|
||||
+ io.papermc.paper.util.TraceUtil.printStackTrace(e);
|
||||
ServerInternalException.reportInternalException(e);
|
||||
// Paper end - ServerExceptionEvent
|
||||
// CraftBukkit end
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
index 3c1992e212a6d6f1db4d5b807b38d71913619fc0..9c1aff17aabd062640e3f451a2ef8c50a7c62f10 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/scheduler/CraftAsyncScheduler.java
|
||||
@@ -40,9 +40,9 @@ public class CraftAsyncScheduler extends CraftScheduler {
|
||||
|
||||
private final ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
||||
4, Integer.MAX_VALUE,30L, TimeUnit.SECONDS, new SynchronousQueue<>(),
|
||||
- new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").build());
|
||||
+ new ThreadFactoryBuilder().setNameFormat("Craft Scheduler Thread - %1$d").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
|
||||
private final Executor management = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
|
||||
- .setNameFormat("Craft Async Scheduler Management Thread").build());
|
||||
+ .setNameFormat("Craft Async Scheduler Management Thread").setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(net.minecraft.server.MinecraftServer.LOGGER)).build()); // Paper
|
||||
private final List<CraftTask> temp = new ArrayList<>();
|
||||
|
||||
CraftAsyncScheduler() {
|
||||
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
index 017c2eadbc5dfec155c21b5d8a80f0b1e380398e..b1ac7338fa632611ea8332044b09070f78f8f5f1 100644
|
||||
--- a/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
|
||||
@@ -102,7 +102,7 @@ public class WatchdogThread extends Thread
|
||||
log.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");
|
||||
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
|
||||
- for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) {
|
||||
+ for (StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace())) { // Paper
|
||||
log.log( Level.SEVERE, "\t\t" + stack );
|
||||
}
|
||||
}
|
||||
@@ -172,7 +172,7 @@ public class WatchdogThread extends Thread
|
||||
}
|
||||
log.log( Level.SEVERE, "\tStack:" );
|
||||
//
|
||||
- for ( StackTraceElement stack : thread.getStackTrace() )
|
||||
+ for ( StackTraceElement stack : io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateStacktrace(thread.getStackTrace()) ) // Paper
|
||||
{
|
||||
log.log( Level.SEVERE, "\t\t" + stack );
|
||||
}
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index ea4e2161c0bd43884055cc6b8d70b2139f70e720..4e2ca9162450c1f54b7ab95a63c1bad8efe81a06 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -30,10 +30,14 @@
|
||||
<DefaultRolloverStrategy max="1000"/>
|
||||
</RollingRandomAccessFile>
|
||||
<Async name="Async">
|
||||
+ <AppenderRef ref="rewrite"/>
|
||||
+ </Async>
|
||||
+ <Rewrite name="rewrite">
|
||||
+ <StacktraceDeobfuscatingRewritePolicy />
|
||||
<AppenderRef ref="File"/>
|
||||
<AppenderRef ref="TerminalConsole" level="info"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="info"/>
|
||||
- </Async>
|
||||
+ </Rewrite>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
|
@ -1,246 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: SirYwell <hannesgreule@outlook.de>
|
||||
Date: Sat, 10 Jul 2021 11:12:30 +0200
|
||||
Subject: [PATCH] Rewrite LogEvents to contain the source jars in stack traces
|
||||
|
||||
|
||||
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/DelegateLogEvent.java b/src/log4jPlugins/java/io/papermc/paper/logging/DelegateLogEvent.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6ffd1befe64c6c3036c22e05ed1c44808d64bd28
|
||||
--- /dev/null
|
||||
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/DelegateLogEvent.java
|
||||
@@ -0,0 +1,130 @@
|
||||
+package io.papermc.paper.logging;
|
||||
+
|
||||
+import org.apache.logging.log4j.Level;
|
||||
+import org.apache.logging.log4j.Marker;
|
||||
+import org.apache.logging.log4j.ThreadContext;
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
|
||||
+import org.apache.logging.log4j.core.time.Instant;
|
||||
+import org.apache.logging.log4j.message.Message;
|
||||
+import org.apache.logging.log4j.util.ReadOnlyStringMap;
|
||||
+
|
||||
+import java.util.Map;
|
||||
+
|
||||
+public class DelegateLogEvent implements LogEvent {
|
||||
+ private final LogEvent original;
|
||||
+
|
||||
+ protected DelegateLogEvent(LogEvent original) {
|
||||
+ this.original = original;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public LogEvent toImmutable() {
|
||||
+ return this.original.toImmutable();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Map<String, String> getContextMap() {
|
||||
+ return this.original.getContextMap();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ReadOnlyStringMap getContextData() {
|
||||
+ return this.original.getContextData();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ThreadContext.ContextStack getContextStack() {
|
||||
+ return this.original.getContextStack();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getLoggerFqcn() {
|
||||
+ return this.original.getLoggerFqcn();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Level getLevel() {
|
||||
+ return this.original.getLevel();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getLoggerName() {
|
||||
+ return this.original.getLoggerName();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Marker getMarker() {
|
||||
+ return this.original.getMarker();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Message getMessage() {
|
||||
+ return this.original.getMessage();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public long getTimeMillis() {
|
||||
+ return this.original.getTimeMillis();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Instant getInstant() {
|
||||
+ return this.original.getInstant();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public StackTraceElement getSource() {
|
||||
+ return this.original.getSource();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getThreadName() {
|
||||
+ return this.original.getThreadName();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public long getThreadId() {
|
||||
+ return this.original.getThreadId();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getThreadPriority() {
|
||||
+ return this.original.getThreadPriority();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Throwable getThrown() {
|
||||
+ return this.original.getThrown();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ThrowableProxy getThrownProxy() {
|
||||
+ return this.original.getThrownProxy();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isEndOfBatch() {
|
||||
+ return this.original.isEndOfBatch();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isIncludeLocation() {
|
||||
+ return this.original.isIncludeLocation();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setEndOfBatch(boolean endOfBatch) {
|
||||
+ this.original.setEndOfBatch(endOfBatch);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void setIncludeLocation(boolean locationRequired) {
|
||||
+ this.original.setIncludeLocation(locationRequired);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public long getNanoTime() {
|
||||
+ return this.original.getNanoTime();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoLogEvent.java b/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoLogEvent.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..558427c65b4051923f73d15d85ee519be005060a
|
||||
--- /dev/null
|
||||
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoLogEvent.java
|
||||
@@ -0,0 +1,48 @@
|
||||
+package io.papermc.paper.logging;
|
||||
+
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.impl.ExtendedClassInfo;
|
||||
+import org.apache.logging.log4j.core.impl.ExtendedStackTraceElement;
|
||||
+import org.apache.logging.log4j.core.impl.ThrowableProxy;
|
||||
+
|
||||
+public class ExtraClassInfoLogEvent extends DelegateLogEvent {
|
||||
+
|
||||
+ private boolean fixed;
|
||||
+
|
||||
+ public ExtraClassInfoLogEvent(LogEvent original) {
|
||||
+ super(original);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public ThrowableProxy getThrownProxy() {
|
||||
+ if (fixed) {
|
||||
+ return super.getThrownProxy();
|
||||
+ }
|
||||
+ rewriteStackTrace(super.getThrownProxy());
|
||||
+ fixed = true;
|
||||
+ return super.getThrownProxy();
|
||||
+ }
|
||||
+
|
||||
+ private void rewriteStackTrace(ThrowableProxy throwable) {
|
||||
+ ExtendedStackTraceElement[] stackTrace = throwable.getExtendedStackTrace();
|
||||
+ for (int i = 0; i < stackTrace.length; i++) {
|
||||
+ ExtendedClassInfo classInfo = stackTrace[i].getExtraClassInfo();
|
||||
+ if (classInfo.getLocation().equals("?")) {
|
||||
+ StackTraceElement element = stackTrace[i].getStackTraceElement();
|
||||
+ String classLoaderName = element.getClassLoaderName();
|
||||
+ if (classLoaderName != null) {
|
||||
+ stackTrace[i] = new ExtendedStackTraceElement(element,
|
||||
+ new ExtendedClassInfo(classInfo.getExact(), classLoaderName, "?"));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (throwable.getCauseProxy() != null) {
|
||||
+ rewriteStackTrace(throwable.getCauseProxy());
|
||||
+ }
|
||||
+ if (throwable.getSuppressedProxies() != null) {
|
||||
+ for (ThrowableProxy proxy : throwable.getSuppressedProxies()) {
|
||||
+ rewriteStackTrace(proxy);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoRewritePolicy.java b/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoRewritePolicy.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..34734bb969a1a74c7a4f9c17d40ebf007ad5d701
|
||||
--- /dev/null
|
||||
+++ b/src/log4jPlugins/java/io/papermc/paper/logging/ExtraClassInfoRewritePolicy.java
|
||||
@@ -0,0 +1,29 @@
|
||||
+package io.papermc.paper.logging;
|
||||
+
|
||||
+import org.apache.logging.log4j.core.Core;
|
||||
+import org.apache.logging.log4j.core.LogEvent;
|
||||
+import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy;
|
||||
+import org.apache.logging.log4j.core.config.plugins.Plugin;
|
||||
+import org.apache.logging.log4j.core.config.plugins.PluginFactory;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+@Plugin(
|
||||
+ name = "ExtraClassInfoRewritePolicy",
|
||||
+ category = Core.CATEGORY_NAME,
|
||||
+ elementType = "rewritePolicy",
|
||||
+ printObject = true
|
||||
+)
|
||||
+public final class ExtraClassInfoRewritePolicy implements RewritePolicy {
|
||||
+ @Override
|
||||
+ public LogEvent rewrite(LogEvent source) {
|
||||
+ if (source.getThrown() != null) {
|
||||
+ return new ExtraClassInfoLogEvent(source);
|
||||
+ }
|
||||
+ return source;
|
||||
+ }
|
||||
+
|
||||
+ @PluginFactory
|
||||
+ public static @NotNull ExtraClassInfoRewritePolicy createPolicy() {
|
||||
+ return new ExtraClassInfoRewritePolicy();
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml
|
||||
index 4e2ca9162450c1f54b7ab95a63c1bad8efe81a06..32e64b3866bdd1489a90339bda2268adafbb15de 100644
|
||||
--- a/src/main/resources/log4j2.xml
|
||||
+++ b/src/main/resources/log4j2.xml
|
||||
@@ -34,6 +34,10 @@
|
||||
</Async>
|
||||
<Rewrite name="rewrite">
|
||||
<StacktraceDeobfuscatingRewritePolicy />
|
||||
+ <AppenderRef ref="rewrite2"/>
|
||||
+ </Rewrite>
|
||||
+ <Rewrite name="rewrite2">
|
||||
+ <ExtraClassInfoRewritePolicy />
|
||||
<AppenderRef ref="File"/>
|
||||
<AppenderRef ref="TerminalConsole" level="info"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="info"/>
|
|
@ -1,665 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Mon, 29 Feb 2016 21:02:09 -0600
|
||||
Subject: [PATCH] Paper command
|
||||
|
||||
Co-authored-by: Zach Brown <zach.brown@destroystokyo.com>
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/command/CallbackCommand.java b/src/main/java/io/papermc/paper/command/CallbackCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..fa4202abd13c1c286bd398938103d1103d5443e7
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/CallbackCommand.java
|
||||
@@ -0,0 +1,35 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import io.papermc.paper.adventure.providers.ClickCallbackProviderImpl;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import java.util.UUID;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public class CallbackCommand extends Command {
|
||||
+
|
||||
+ protected CallbackCommand(final String name) {
|
||||
+ super(name);
|
||||
+ this.description = "ClickEvent callback";
|
||||
+ this.usageMessage = "/callback <uuid>";
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String commandLabel, final String[] args) {
|
||||
+ if (args.length != 1) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ final UUID id;
|
||||
+ try {
|
||||
+ id = UUID.fromString(args[0]);
|
||||
+ } catch (final IllegalArgumentException ignored) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ ClickCallbackProviderImpl.CALLBACK_MANAGER.runCallback(sender, id);
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/CommandUtil.java b/src/main/java/io/papermc/paper/command/CommandUtil.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..953c30500892e5f0c55b8597bc708ea85bf56d6e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/CommandUtil.java
|
||||
@@ -0,0 +1,69 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import com.google.common.base.Functions;
|
||||
+import com.google.common.collect.Iterables;
|
||||
+import com.google.common.collect.Lists;
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Arrays;
|
||||
+import java.util.Collection;
|
||||
+import java.util.Iterator;
|
||||
+import java.util.List;
|
||||
+import net.minecraft.resources.ResourceLocation;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class CommandUtil {
|
||||
+ private CommandUtil() {
|
||||
+ }
|
||||
+
|
||||
+ // Code from Mojang - copyright them
|
||||
+ public static List<String> getListMatchingLast(
|
||||
+ final CommandSender sender,
|
||||
+ final String[] args,
|
||||
+ final String... matches
|
||||
+ ) {
|
||||
+ return getListMatchingLast(sender, args, Arrays.asList(matches));
|
||||
+ }
|
||||
+
|
||||
+ public static boolean matches(final String s, final String s1) {
|
||||
+ return s1.regionMatches(true, 0, s, 0, s.length());
|
||||
+ }
|
||||
+
|
||||
+ public static List<String> getListMatchingLast(
|
||||
+ final CommandSender sender,
|
||||
+ final String[] strings,
|
||||
+ final Collection<?> collection
|
||||
+ ) {
|
||||
+ String last = strings[strings.length - 1];
|
||||
+ ArrayList<String> results = Lists.newArrayList();
|
||||
+
|
||||
+ if (!collection.isEmpty()) {
|
||||
+ Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator();
|
||||
+
|
||||
+ while (iterator.hasNext()) {
|
||||
+ String s1 = (String) iterator.next();
|
||||
+
|
||||
+ if (matches(last, s1) && (sender.hasPermission(PaperCommand.BASE_PERM + s1) || sender.hasPermission("bukkit.command.paper"))) {
|
||||
+ results.add(s1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (results.isEmpty()) {
|
||||
+ iterator = collection.iterator();
|
||||
+
|
||||
+ while (iterator.hasNext()) {
|
||||
+ Object object = iterator.next();
|
||||
+
|
||||
+ if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) {
|
||||
+ results.add(String.valueOf(object));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return results;
|
||||
+ }
|
||||
+ // end copy stuff
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8ccc59473bac983ced6b9e4a57e0ec4ebd2b0f32
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +1,143 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import io.papermc.paper.command.subcommands.*;
|
||||
+import it.unimi.dsi.fastutil.Pair;
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Arrays;
|
||||
+import java.util.Collections;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.List;
|
||||
+import java.util.Locale;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+import java.util.stream.Collectors;
|
||||
+import net.minecraft.Util;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.Location;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.permissions.Permission;
|
||||
+import org.bukkit.permissions.PermissionDefault;
|
||||
+import org.bukkit.plugin.PluginManager;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+import static net.kyori.adventure.text.Component.text;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class PaperCommand extends Command {
|
||||
+ static final String BASE_PERM = "bukkit.command.paper.";
|
||||
+ // subcommand label -> subcommand
|
||||
+ private static final Map<String, PaperSubcommand> SUBCOMMANDS = Util.make(() -> {
|
||||
+ final Map<Set<String>, PaperSubcommand> commands = new HashMap<>();
|
||||
+
|
||||
+ commands.put(Set.of("heap"), new HeapDumpCommand());
|
||||
+ commands.put(Set.of("entity"), new EntityCommand());
|
||||
+ commands.put(Set.of("reload"), new ReloadCommand());
|
||||
+ commands.put(Set.of("version"), new VersionCommand());
|
||||
+
|
||||
+ return commands.entrySet().stream()
|
||||
+ .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
|
||||
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
+ });
|
||||
+ private static final Set<String> COMPLETABLE_SUBCOMMANDS = SUBCOMMANDS.entrySet().stream().filter(entry -> entry.getValue().tabCompletes()).map(Map.Entry::getKey).collect(Collectors.toSet());
|
||||
+ // alias -> subcommand label
|
||||
+ private static final Map<String, String> ALIASES = Util.make(() -> {
|
||||
+ final Map<String, Set<String>> aliases = new HashMap<>();
|
||||
+
|
||||
+ aliases.put("version", Set.of("ver"));
|
||||
+
|
||||
+ return aliases.entrySet().stream()
|
||||
+ .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey())))
|
||||
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
+ });
|
||||
+
|
||||
+ public PaperCommand(final String name) {
|
||||
+ super(name);
|
||||
+ this.description = "Paper related commands";
|
||||
+ this.usageMessage = "/paper [" + String.join(" | ", SUBCOMMANDS.keySet()) + "]";
|
||||
+ final List<String> permissions = new ArrayList<>();
|
||||
+ permissions.add("bukkit.command.paper");
|
||||
+ permissions.addAll(SUBCOMMANDS.keySet().stream().map(s -> BASE_PERM + s).toList());
|
||||
+ this.setPermission(String.join(";", permissions));
|
||||
+ final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||
+ for (final String perm : permissions) {
|
||||
+ pluginManager.addPermission(new Permission(perm, PermissionDefault.OP));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static boolean testPermission(final CommandSender sender, final String permission) {
|
||||
+ if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.paper")) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ sender.sendMessage(text("I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.", RED));
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<String> tabComplete(
|
||||
+ final CommandSender sender,
|
||||
+ final String alias,
|
||||
+ final String[] args,
|
||||
+ final @Nullable Location location
|
||||
+ ) throws IllegalArgumentException {
|
||||
+ if (args.length <= 1) {
|
||||
+ return CommandUtil.getListMatchingLast(sender, args, COMPLETABLE_SUBCOMMANDS);
|
||||
+ }
|
||||
+
|
||||
+ final @Nullable Pair<String, PaperSubcommand> subCommand = resolveCommand(args[0]);
|
||||
+ if (subCommand != null) {
|
||||
+ return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length));
|
||||
+ }
|
||||
+
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean execute(
|
||||
+ final CommandSender sender,
|
||||
+ final String commandLabel,
|
||||
+ final String[] args
|
||||
+ ) {
|
||||
+ if (!testPermission(sender)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ if (args.length == 0) {
|
||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||
+ return false;
|
||||
+ }
|
||||
+ final @Nullable Pair<String, PaperSubcommand> subCommand = resolveCommand(args[0]);
|
||||
+
|
||||
+ if (subCommand == null) {
|
||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (!testPermission(sender, subCommand.first())) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
+ return subCommand.second().execute(sender, subCommand.first(), choppedArgs);
|
||||
+ }
|
||||
+
|
||||
+ private static @Nullable Pair<String, PaperSubcommand> resolveCommand(String label) {
|
||||
+ label = label.toLowerCase(Locale.ENGLISH);
|
||||
+ @Nullable PaperSubcommand subCommand = SUBCOMMANDS.get(label);
|
||||
+ if (subCommand == null) {
|
||||
+ final @Nullable String command = ALIASES.get(label);
|
||||
+ if (command != null) {
|
||||
+ label = command;
|
||||
+ subCommand = SUBCOMMANDS.get(command);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (subCommand != null) {
|
||||
+ return Pair.of(label, subCommand);
|
||||
+ }
|
||||
+
|
||||
+ return null;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..5e5ec700a368cfdaa1ea0b3f0fa82089895d4b92
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||
@@ -0,0 +1,28 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.command.Command;
|
||||
+
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class PaperCommands {
|
||||
+
|
||||
+ private PaperCommands() {
|
||||
+ }
|
||||
+
|
||||
+ private static final Map<String, Command> COMMANDS = new HashMap<>();
|
||||
+ static {
|
||||
+ COMMANDS.put("paper", new PaperCommand("paper"));
|
||||
+ COMMANDS.put("callback", new CallbackCommand("callback"));
|
||||
+ }
|
||||
+
|
||||
+ public static void registerCommands(final MinecraftServer server) {
|
||||
+ COMMANDS.forEach((s, command) -> {
|
||||
+ server.server.getCommandMap().register(s, "Paper", command);
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperSubcommand.java b/src/main/java/io/papermc/paper/command/PaperSubcommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..7e9e0ff8639be135bf8575e375cbada5b57164e1
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperSubcommand.java
|
||||
@@ -0,0 +1,20 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import java.util.Collections;
|
||||
+import java.util.List;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public interface PaperSubcommand {
|
||||
+ boolean execute(CommandSender sender, String subCommand, String[] args);
|
||||
+
|
||||
+ default List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ default boolean tabCompletes() {
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ff99336e0b8131ae161cfa5c4fc83c6905e3dbc8
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||
@@ -0,0 +1,158 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import com.google.common.collect.Maps;
|
||||
+import io.papermc.paper.command.CommandUtil;
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import java.util.Collections;
|
||||
+import java.util.List;
|
||||
+import java.util.Locale;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+import java.util.stream.Collectors;
|
||||
+
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.adventure.text.event.ClickEvent;
|
||||
+import net.kyori.adventure.text.event.HoverEvent;
|
||||
+import net.minecraft.core.registries.BuiltInRegistries;
|
||||
+import net.minecraft.resources.ResourceLocation;
|
||||
+import net.minecraft.server.level.ServerChunkCache;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.world.entity.EntityType;
|
||||
+import net.minecraft.world.level.ChunkPos;
|
||||
+import org.apache.commons.lang3.tuple.MutablePair;
|
||||
+import org.apache.commons.lang3.tuple.Pair;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.HeightMap;
|
||||
+import org.bukkit.World;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import org.bukkit.entity.Player;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+import static net.kyori.adventure.text.Component.text;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class EntityCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ this.listEntities(sender, args);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ if (args.length == 1) {
|
||||
+ return CommandUtil.getListMatchingLast(sender, args, "help", "list");
|
||||
+ } else if (args.length == 2) {
|
||||
+ return CommandUtil.getListMatchingLast(sender, args, BuiltInRegistries.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new));
|
||||
+ }
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
|
||||
+ */
|
||||
+ private void listEntities(final CommandSender sender, final String[] args) {
|
||||
+ // help
|
||||
+ if (args.length < 1 || !args[0].toLowerCase(Locale.ENGLISH).equals("list")) {
|
||||
+ sender.sendMessage(text("Use /paper entity [list] help for more information on a specific command", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ if ("list".equals(args[0].toLowerCase(Locale.ENGLISH))) {
|
||||
+ String filter = "*";
|
||||
+ if (args.length > 1) {
|
||||
+ if (args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
+ sender.sendMessage(text("Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ filter = args[1];
|
||||
+ }
|
||||
+ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
||||
+ Set<ResourceLocation> names = BuiltInRegistries.ENTITY_TYPE.keySet().stream()
|
||||
+ .filter(n -> n.toString().matches(cleanfilter))
|
||||
+ .collect(Collectors.toSet());
|
||||
+ if (names.isEmpty()) {
|
||||
+ sender.sendMessage(text("Invalid filter, does not match any entities. Use /paper entity list for a proper list", RED));
|
||||
+ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ String worldName;
|
||||
+ if (args.length > 2) {
|
||||
+ worldName = args[2];
|
||||
+ } else if (sender instanceof Player) {
|
||||
+ worldName = ((Player) sender).getWorld().getName();
|
||||
+ } else {
|
||||
+ sender.sendMessage(text("Please specify the name of a world", RED));
|
||||
+ sender.sendMessage(text("To do so without a filter, specify '*' as the filter", RED));
|
||||
+ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ Map<ResourceLocation, MutablePair<Integer, Map<ChunkPos, Integer>>> list = Maps.newHashMap();
|
||||
+ @Nullable World bukkitWorld = Bukkit.getWorld(worldName);
|
||||
+ if (bukkitWorld == null) {
|
||||
+ sender.sendMessage(text("Could not load world for " + worldName + ". Please select a valid world.", RED));
|
||||
+ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
|
||||
+ Map<ResourceLocation, Integer> nonEntityTicking = Maps.newHashMap();
|
||||
+ ServerChunkCache chunkProviderServer = world.getChunkSource();
|
||||
+ world.getAllEntities().forEach(e -> {
|
||||
+ ResourceLocation key = EntityType.getKey(e.getType());
|
||||
+
|
||||
+ MutablePair<Integer, Map<ChunkPos, Integer>> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap()));
|
||||
+ ChunkPos chunk = e.chunkPosition();
|
||||
+ info.left++;
|
||||
+ info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||
+ if (!chunkProviderServer.isPositionTicking(e)) {
|
||||
+ nonEntityTicking.merge(key, 1, Integer::sum);
|
||||
+ }
|
||||
+ });
|
||||
+ if (names.size() == 1) {
|
||||
+ ResourceLocation name = names.iterator().next();
|
||||
+ Pair<Integer, Map<ChunkPos, Integer>> info = list.get(name);
|
||||
+ int nonTicking = nonEntityTicking.getOrDefault(name, 0);
|
||||
+ if (info == null) {
|
||||
+ sender.sendMessage(text("No entities found.", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ sender.sendMessage("Entity: " + name + " Total Ticking: " + (info.getLeft() - nonTicking) + ", Total Non-Ticking: " + nonTicking);
|
||||
+ info.getRight().entrySet().stream()
|
||||
+ .sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
+ .limit(10).forEach(e -> {
|
||||
+ final int x = (e.getKey().x << 4) + 8;
|
||||
+ final int z = (e.getKey().z << 4) + 8;
|
||||
+ final Component message = text(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z + (chunkProviderServer.isPositionTicking(e.getKey().toLong()) ? " (Ticking)" : " (Non-Ticking)"))
|
||||
+ .hoverEvent(HoverEvent.showText(text("Click to teleport to chunk", GREEN)))
|
||||
+ .clickEvent(ClickEvent.clickEvent(ClickEvent.Action.RUN_COMMAND, "/minecraft:execute as @s in " + world.getWorld().getKey() + " run tp " + x + " " + (world.getWorld().getHighestBlockYAt(x, z, HeightMap.MOTION_BLOCKING) + 1) + " " + z));
|
||||
+ sender.sendMessage(message);
|
||||
+ });
|
||||
+ } else {
|
||||
+ List<Pair<ResourceLocation, Integer>> info = list.entrySet().stream()
|
||||
+ .filter(e -> names.contains(e.getKey()))
|
||||
+ .map(e -> Pair.of(e.getKey(), e.getValue().left))
|
||||
+ .sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString()))
|
||||
+ .toList();
|
||||
+
|
||||
+ if (info.isEmpty()) {
|
||||
+ sender.sendMessage(text("No entities found.", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ int count = info.stream().mapToInt(Pair::getRight).sum();
|
||||
+ int nonTickingCount = nonEntityTicking.values().stream().mapToInt(Integer::intValue).sum();
|
||||
+ sender.sendMessage("Total Ticking: " + (count - nonTickingCount) + ", Total Non-Ticking: " + nonTickingCount);
|
||||
+ info.forEach(e -> {
|
||||
+ int nonTicking = nonEntityTicking.getOrDefault(e.getKey(), 0);
|
||||
+ sender.sendMessage(" " + (e.getValue() - nonTicking) + " (" + nonTicking + ") " + ": " + e.getKey());
|
||||
+ });
|
||||
+ sender.sendMessage("* First number is ticking entities, second number is non-ticking entities");
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java b/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..cd2e4d792e972b8bf1e07b8961594a670ae949cf
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java
|
||||
@@ -0,0 +1,38 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import java.time.LocalDateTime;
|
||||
+import java.time.format.DateTimeFormatter;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftServer;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+import static net.kyori.adventure.text.Component.text;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class HeapDumpCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ this.dumpHeap(sender);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private void dumpHeap(final CommandSender sender) {
|
||||
+ java.nio.file.Path dir = java.nio.file.Paths.get("./dumps");
|
||||
+ String name = "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now());
|
||||
+
|
||||
+ Command.broadcastCommandMessage(sender, text("Writing JVM heap data...", YELLOW));
|
||||
+
|
||||
+ java.nio.file.Path file = CraftServer.dumpHeap(dir, name);
|
||||
+ if (file != null) {
|
||||
+ Command.broadcastCommandMessage(sender, text("Heap dump saved to " + file, GREEN));
|
||||
+ } else {
|
||||
+ Command.broadcastCommandMessage(sender, text("Failed to write heap dump, see server log for details", RED));
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..bd68139ae635f2ad7ec8e7a21e0056a139c4c62e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java
|
||||
@@ -0,0 +1,33 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftServer;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+import static net.kyori.adventure.text.Component.text;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class ReloadCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ this.doReload(sender);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private void doReload(final CommandSender sender) {
|
||||
+ Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED));
|
||||
+ Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED));
|
||||
+
|
||||
+ MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
|
||||
+ server.paperConfigurations.reloadConfigs(server);
|
||||
+ server.server.reloadCount++;
|
||||
+
|
||||
+ Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java b/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ae60bd96b5284d54676d8e7e4dd5d170b526ec1e
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java
|
||||
@@ -0,0 +1,21 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.command.Command;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class VersionCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version");
|
||||
+ if (ver != null) {
|
||||
+ ver.execute(sender, "paper", new String[0]);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 8ddfd4a148c95d0d9e93edf03ddac46332936d9a..8499eb5128d3269925ffb2c61d8532e9da47be4a 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -187,6 +187,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
+ io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
|
||||
|
||||
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
||||
this.setFlightAllowed(dedicatedserverproperties.allowFlight);
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 8441cbe4ebd676d1aacff223abdabeb32b5658e0..45ba78ae029f59efd16534a2d6453af50a7aa74a 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -951,6 +951,7 @@ public final class CraftServer implements Server {
|
||||
this.commandMap.clearCommands();
|
||||
this.reloadData();
|
||||
org.spigotmc.SpigotConfig.registerCommands(); // Spigot
|
||||
+ io.papermc.paper.command.PaperCommands.registerCommands(this.console); // Paper
|
||||
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
||||
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
||||
|
||||
@@ -2649,6 +2650,34 @@ public final class CraftServer implements Server {
|
||||
// Paper end
|
||||
|
||||
// Paper start
|
||||
+ @SuppressWarnings({"rawtypes", "unchecked"})
|
||||
+ public static java.nio.file.Path dumpHeap(java.nio.file.Path dir, String name) {
|
||||
+ try {
|
||||
+ java.nio.file.Files.createDirectories(dir);
|
||||
+
|
||||
+ javax.management.MBeanServer server = java.lang.management.ManagementFactory.getPlatformMBeanServer();
|
||||
+ java.nio.file.Path file;
|
||||
+
|
||||
+ try {
|
||||
+ Class clazz = Class.forName("openj9.lang.management.OpenJ9DiagnosticsMXBean");
|
||||
+ Object openj9Mbean = java.lang.management.ManagementFactory.newPlatformMXBeanProxy(server, "openj9.lang.management:type=OpenJ9Diagnostics", clazz);
|
||||
+ java.lang.reflect.Method m = clazz.getMethod("triggerDumpToFile", String.class, String.class);
|
||||
+ file = dir.resolve(name + ".phd");
|
||||
+ m.invoke(openj9Mbean, "heap", file.toString());
|
||||
+ } catch (ClassNotFoundException e) {
|
||||
+ Class clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
|
||||
+ Object hotspotMBean = java.lang.management.ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", clazz);
|
||||
+ java.lang.reflect.Method m = clazz.getMethod("dumpHeap", String.class, boolean.class);
|
||||
+ file = dir.resolve(name + ".hprof");
|
||||
+ m.invoke(hotspotMBean, file.toString(), true);
|
||||
+ }
|
||||
+
|
||||
+ return file;
|
||||
+ } catch (Throwable t) {
|
||||
+ Bukkit.getLogger().log(Level.SEVERE, "Could not write heap", t);
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
private Iterable<? extends net.kyori.adventure.audience.Audience> adventure$audiences;
|
||||
@Override
|
||||
public Iterable<? extends net.kyori.adventure.audience.Audience> audiences() {
|
|
@ -1,731 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Zach Brown <zach.brown@destroystokyo.com>
|
||||
Date: Fri, 24 Mar 2017 23:56:01 -0500
|
||||
Subject: [PATCH] Paper Metrics
|
||||
|
||||
Removes Spigot's mcstats metrics in favor of a system using bStats
|
||||
|
||||
To disable for privacy or other reasons go to the bStats folder in your plugins folder
|
||||
and edit the config.yml file present there.
|
||||
|
||||
Please keep in mind the data collected is anonymous and collection should have no
|
||||
tangible effect on server performance. The data is used to allow the authors of
|
||||
PaperMC to track version and platform usage so that we can make better management
|
||||
decisions on behalf of the project.
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/Metrics.java b/src/main/java/com/destroystokyo/paper/Metrics.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6aaed8e8bf8c721fc834da5c76ac72a4c3e92458
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/Metrics.java
|
||||
@@ -0,0 +1,678 @@
|
||||
+package com.destroystokyo.paper;
|
||||
+
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.configuration.file.YamlConfiguration;
|
||||
+import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
+import org.bukkit.plugin.Plugin;
|
||||
+
|
||||
+import org.json.simple.JSONArray;
|
||||
+import org.json.simple.JSONObject;
|
||||
+
|
||||
+import javax.net.ssl.HttpsURLConnection;
|
||||
+import java.io.ByteArrayOutputStream;
|
||||
+import java.io.DataOutputStream;
|
||||
+import java.io.File;
|
||||
+import java.io.IOException;
|
||||
+import java.net.URL;
|
||||
+import java.util.*;
|
||||
+import java.util.concurrent.Callable;
|
||||
+import java.util.concurrent.Executors;
|
||||
+import java.util.concurrent.ScheduledExecutorService;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+import java.util.logging.Level;
|
||||
+import java.util.logging.Logger;
|
||||
+import java.util.regex.Matcher;
|
||||
+import java.util.regex.Pattern;
|
||||
+import java.util.zip.GZIPOutputStream;
|
||||
+
|
||||
+/**
|
||||
+ * bStats collects some data for plugin authors.
|
||||
+ *
|
||||
+ * Check out https://bStats.org/ to learn more about bStats!
|
||||
+ */
|
||||
+public class Metrics {
|
||||
+
|
||||
+ // Executor service for requests
|
||||
+ // We use an executor service because the Bukkit scheduler is affected by server lags
|
||||
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||
+
|
||||
+ // The version of this bStats class
|
||||
+ public static final int B_STATS_VERSION = 1;
|
||||
+
|
||||
+ // The url to which the data is sent
|
||||
+ private static final String URL = "https://bStats.org/submitData/server-implementation";
|
||||
+
|
||||
+ // Should failed requests be logged?
|
||||
+ private static boolean logFailedRequests = false;
|
||||
+
|
||||
+ // The logger for the failed requests
|
||||
+ private static Logger logger = Logger.getLogger("bStats");
|
||||
+
|
||||
+ // The name of the server software
|
||||
+ private final String name;
|
||||
+
|
||||
+ // The uuid of the server
|
||||
+ private final String serverUUID;
|
||||
+
|
||||
+ // A list with all custom charts
|
||||
+ private final List<CustomChart> charts = new ArrayList<>();
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param name The name of the server software.
|
||||
+ * @param serverUUID The uuid of the server.
|
||||
+ * @param logFailedRequests Whether failed requests should be logged or not.
|
||||
+ * @param logger The logger for the failed requests.
|
||||
+ */
|
||||
+ public Metrics(String name, String serverUUID, boolean logFailedRequests, Logger logger) {
|
||||
+ this.name = name;
|
||||
+ this.serverUUID = serverUUID;
|
||||
+ Metrics.logFailedRequests = logFailedRequests;
|
||||
+ Metrics.logger = logger;
|
||||
+
|
||||
+ // Start submitting the data
|
||||
+ startSubmitting();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Adds a custom chart.
|
||||
+ *
|
||||
+ * @param chart The chart to add.
|
||||
+ */
|
||||
+ public void addCustomChart(CustomChart chart) {
|
||||
+ if (chart == null) {
|
||||
+ throw new IllegalArgumentException("Chart cannot be null!");
|
||||
+ }
|
||||
+ charts.add(chart);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Starts the Scheduler which submits our data every 30 minutes.
|
||||
+ */
|
||||
+ private void startSubmitting() {
|
||||
+ final Runnable submitTask = this::submitData;
|
||||
+
|
||||
+ // Many servers tend to restart at a fixed time at xx:00 which causes an uneven distribution of requests on the
|
||||
+ // bStats backend. To circumvent this problem, we introduce some randomness into the initial and second delay.
|
||||
+ // WARNING: You must not modify any part of this Metrics class, including the submit delay or frequency!
|
||||
+ // WARNING: Modifying this code will get your plugin banned on bStats. Just don't do it!
|
||||
+ long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3));
|
||||
+ long secondDelay = (long) (1000 * 60 * (Math.random() * 30));
|
||||
+ scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS);
|
||||
+ scheduler.scheduleAtFixedRate(submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS);
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Gets the plugin specific data.
|
||||
+ *
|
||||
+ * @return The plugin specific data.
|
||||
+ */
|
||||
+ private JSONObject getPluginData() {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+
|
||||
+ data.put("pluginName", name); // Append the name of the server software
|
||||
+ JSONArray customCharts = new JSONArray();
|
||||
+ for (CustomChart customChart : charts) {
|
||||
+ // Add the data of the custom charts
|
||||
+ JSONObject chart = customChart.getRequestJsonObject();
|
||||
+ if (chart == null) { // If the chart is null, we skip it
|
||||
+ continue;
|
||||
+ }
|
||||
+ customCharts.add(chart);
|
||||
+ }
|
||||
+ data.put("customCharts", customCharts);
|
||||
+
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Gets the server specific data.
|
||||
+ *
|
||||
+ * @return The server specific data.
|
||||
+ */
|
||||
+ private JSONObject getServerData() {
|
||||
+ // OS specific data
|
||||
+ String osName = System.getProperty("os.name");
|
||||
+ String osArch = System.getProperty("os.arch");
|
||||
+ String osVersion = System.getProperty("os.version");
|
||||
+ int coreCount = Runtime.getRuntime().availableProcessors();
|
||||
+
|
||||
+ JSONObject data = new JSONObject();
|
||||
+
|
||||
+ data.put("serverUUID", serverUUID);
|
||||
+
|
||||
+ data.put("osName", osName);
|
||||
+ data.put("osArch", osArch);
|
||||
+ data.put("osVersion", osVersion);
|
||||
+ data.put("coreCount", coreCount);
|
||||
+
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Collects the data and sends it afterwards.
|
||||
+ */
|
||||
+ private void submitData() {
|
||||
+ final JSONObject data = getServerData();
|
||||
+
|
||||
+ JSONArray pluginData = new JSONArray();
|
||||
+ pluginData.add(getPluginData());
|
||||
+ data.put("plugins", pluginData);
|
||||
+
|
||||
+ try {
|
||||
+ // We are still in the Thread of the timer, so nothing get blocked :)
|
||||
+ sendData(data);
|
||||
+ } catch (Exception e) {
|
||||
+ // Something went wrong! :(
|
||||
+ if (logFailedRequests) {
|
||||
+ logger.log(Level.WARNING, "Could not submit stats of " + name, e);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Sends the data to the bStats server.
|
||||
+ *
|
||||
+ * @param data The data to send.
|
||||
+ * @throws Exception If the request failed.
|
||||
+ */
|
||||
+ private static void sendData(JSONObject data) throws Exception {
|
||||
+ if (data == null) {
|
||||
+ throw new IllegalArgumentException("Data cannot be null!");
|
||||
+ }
|
||||
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
|
||||
+
|
||||
+ // Compress the data to save bandwidth
|
||||
+ byte[] compressedData = compress(data.toString());
|
||||
+
|
||||
+ // Add headers
|
||||
+ connection.setRequestMethod("POST");
|
||||
+ connection.addRequestProperty("Accept", "application/json");
|
||||
+ connection.addRequestProperty("Connection", "close");
|
||||
+ connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
|
||||
+ connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
|
||||
+ connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
|
||||
+ connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
|
||||
+
|
||||
+ // Send data
|
||||
+ connection.setDoOutput(true);
|
||||
+ DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
|
||||
+ outputStream.write(compressedData);
|
||||
+ outputStream.flush();
|
||||
+ outputStream.close();
|
||||
+
|
||||
+ connection.getInputStream().close(); // We don't care about the response - Just send our data :)
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Gzips the given String.
|
||||
+ *
|
||||
+ * @param str The string to gzip.
|
||||
+ * @return The gzipped String.
|
||||
+ * @throws IOException If the compression failed.
|
||||
+ */
|
||||
+ private static byte[] compress(final String str) throws IOException {
|
||||
+ if (str == null) {
|
||||
+ return null;
|
||||
+ }
|
||||
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
+ GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
|
||||
+ gzip.write(str.getBytes("UTF-8"));
|
||||
+ gzip.close();
|
||||
+ return outputStream.toByteArray();
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom chart.
|
||||
+ */
|
||||
+ public static abstract class CustomChart {
|
||||
+
|
||||
+ // The id of the chart
|
||||
+ final String chartId;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ */
|
||||
+ CustomChart(String chartId) {
|
||||
+ if (chartId == null || chartId.isEmpty()) {
|
||||
+ throw new IllegalArgumentException("ChartId cannot be null or empty!");
|
||||
+ }
|
||||
+ this.chartId = chartId;
|
||||
+ }
|
||||
+
|
||||
+ private JSONObject getRequestJsonObject() {
|
||||
+ JSONObject chart = new JSONObject();
|
||||
+ chart.put("chartId", chartId);
|
||||
+ try {
|
||||
+ JSONObject data = getChartData();
|
||||
+ if (data == null) {
|
||||
+ // If the data is null we don't send the chart.
|
||||
+ return null;
|
||||
+ }
|
||||
+ chart.put("data", data);
|
||||
+ } catch (Throwable t) {
|
||||
+ if (logFailedRequests) {
|
||||
+ logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+ return chart;
|
||||
+ }
|
||||
+
|
||||
+ protected abstract JSONObject getChartData() throws Exception;
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom simple pie.
|
||||
+ */
|
||||
+ public static class SimplePie extends CustomChart {
|
||||
+
|
||||
+ private final Callable<String> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public SimplePie(String chartId, Callable<String> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ String value = callable.call();
|
||||
+ if (value == null || value.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("value", value);
|
||||
+ return data;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom advanced pie.
|
||||
+ */
|
||||
+ public static class AdvancedPie extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Map<String, Integer>> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ JSONObject values = new JSONObject();
|
||||
+ Map<String, Integer> map = callable.call();
|
||||
+ if (map == null || map.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ boolean allSkipped = true;
|
||||
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
+ if (entry.getValue() == 0) {
|
||||
+ continue; // Skip this invalid
|
||||
+ }
|
||||
+ allSkipped = false;
|
||||
+ values.put(entry.getKey(), entry.getValue());
|
||||
+ }
|
||||
+ if (allSkipped) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("values", values);
|
||||
+ return data;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom drilldown pie.
|
||||
+ */
|
||||
+ public static class DrilldownPie extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Map<String, Map<String, Integer>>> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ JSONObject values = new JSONObject();
|
||||
+ Map<String, Map<String, Integer>> map = callable.call();
|
||||
+ if (map == null || map.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ boolean reallyAllSkipped = true;
|
||||
+ for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
|
||||
+ JSONObject value = new JSONObject();
|
||||
+ boolean allSkipped = true;
|
||||
+ for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
|
||||
+ value.put(valueEntry.getKey(), valueEntry.getValue());
|
||||
+ allSkipped = false;
|
||||
+ }
|
||||
+ if (!allSkipped) {
|
||||
+ reallyAllSkipped = false;
|
||||
+ values.put(entryValues.getKey(), value);
|
||||
+ }
|
||||
+ }
|
||||
+ if (reallyAllSkipped) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("values", values);
|
||||
+ return data;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom single line chart.
|
||||
+ */
|
||||
+ public static class SingleLineChart extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Integer> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public SingleLineChart(String chartId, Callable<Integer> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ int value = callable.call();
|
||||
+ if (value == 0) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("value", value);
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom multi line chart.
|
||||
+ */
|
||||
+ public static class MultiLineChart extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Map<String, Integer>> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ JSONObject values = new JSONObject();
|
||||
+ Map<String, Integer> map = callable.call();
|
||||
+ if (map == null || map.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ boolean allSkipped = true;
|
||||
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
+ if (entry.getValue() == 0) {
|
||||
+ continue; // Skip this invalid
|
||||
+ }
|
||||
+ allSkipped = false;
|
||||
+ values.put(entry.getKey(), entry.getValue());
|
||||
+ }
|
||||
+ if (allSkipped) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("values", values);
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom simple bar chart.
|
||||
+ */
|
||||
+ public static class SimpleBarChart extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Map<String, Integer>> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ JSONObject values = new JSONObject();
|
||||
+ Map<String, Integer> map = callable.call();
|
||||
+ if (map == null || map.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
|
||||
+ JSONArray categoryValues = new JSONArray();
|
||||
+ categoryValues.add(entry.getValue());
|
||||
+ values.put(entry.getKey(), categoryValues);
|
||||
+ }
|
||||
+ data.put("values", values);
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ /**
|
||||
+ * Represents a custom advanced bar chart.
|
||||
+ */
|
||||
+ public static class AdvancedBarChart extends CustomChart {
|
||||
+
|
||||
+ private final Callable<Map<String, int[]>> callable;
|
||||
+
|
||||
+ /**
|
||||
+ * Class constructor.
|
||||
+ *
|
||||
+ * @param chartId The id of the chart.
|
||||
+ * @param callable The callable which is used to request the chart data.
|
||||
+ */
|
||||
+ public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
|
||||
+ super(chartId);
|
||||
+ this.callable = callable;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected JSONObject getChartData() throws Exception {
|
||||
+ JSONObject data = new JSONObject();
|
||||
+ JSONObject values = new JSONObject();
|
||||
+ Map<String, int[]> map = callable.call();
|
||||
+ if (map == null || map.isEmpty()) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ boolean allSkipped = true;
|
||||
+ for (Map.Entry<String, int[]> entry : map.entrySet()) {
|
||||
+ if (entry.getValue().length == 0) {
|
||||
+ continue; // Skip this invalid
|
||||
+ }
|
||||
+ allSkipped = false;
|
||||
+ JSONArray categoryValues = new JSONArray();
|
||||
+ for (int categoryValue : entry.getValue()) {
|
||||
+ categoryValues.add(categoryValue);
|
||||
+ }
|
||||
+ values.put(entry.getKey(), categoryValues);
|
||||
+ }
|
||||
+ if (allSkipped) {
|
||||
+ // Null = skip the chart
|
||||
+ return null;
|
||||
+ }
|
||||
+ data.put("values", values);
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ public static class PaperMetrics {
|
||||
+ public static void startMetrics() {
|
||||
+ // Get the config file
|
||||
+ File configFile = new File(new File((File) MinecraftServer.getServer().options.valueOf("plugins"), "bStats"), "config.yml");
|
||||
+ YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
|
||||
+
|
||||
+ // Check if the config file exists
|
||||
+ if (!config.isSet("serverUuid")) {
|
||||
+
|
||||
+ // Add default values
|
||||
+ config.addDefault("enabled", true);
|
||||
+ // Every server gets it's unique random id.
|
||||
+ config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||
+ // Should failed request be logged?
|
||||
+ config.addDefault("logFailedRequests", false);
|
||||
+
|
||||
+ // Inform the server owners about bStats
|
||||
+ config.options().header(
|
||||
+ "bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
|
||||
+ "To honor their work, you should not disable it.\n" +
|
||||
+ "This has nearly no effect on the server performance!\n" +
|
||||
+ "Check out https://bStats.org/ to learn more :)"
|
||||
+ ).copyDefaults(true);
|
||||
+ try {
|
||||
+ config.save(configFile);
|
||||
+ } catch (IOException ignored) {
|
||||
+ }
|
||||
+ }
|
||||
+ // Load the data
|
||||
+ String serverUUID = config.getString("serverUuid");
|
||||
+ boolean logFailedRequests = config.getBoolean("logFailedRequests", false);
|
||||
+ // Only start Metrics, if it's enabled in the config
|
||||
+ if (config.getBoolean("enabled", true)) {
|
||||
+ Metrics metrics = new Metrics("Paper", serverUUID, logFailedRequests, Bukkit.getLogger());
|
||||
+
|
||||
+ metrics.addCustomChart(new Metrics.SimplePie("minecraft_version", () -> {
|
||||
+ String minecraftVersion = Bukkit.getVersion();
|
||||
+ minecraftVersion = minecraftVersion.substring(minecraftVersion.indexOf("MC: ") + 4, minecraftVersion.length() - 1);
|
||||
+ return minecraftVersion;
|
||||
+ }));
|
||||
+
|
||||
+ metrics.addCustomChart(new Metrics.SingleLineChart("players", () -> Bukkit.getOnlinePlayers().size()));
|
||||
+ metrics.addCustomChart(new Metrics.SimplePie("online_mode", () -> Bukkit.getOnlineMode() ? "online" : "offline"));
|
||||
+ final String paperVersion;
|
||||
+ final String implVersion = org.bukkit.craftbukkit.Main.class.getPackage().getImplementationVersion();
|
||||
+ if (implVersion != null) {
|
||||
+ final String buildOrHash = implVersion.substring(implVersion.lastIndexOf('-') + 1);
|
||||
+ paperVersion = "git-Paper-%s-%s".formatted(Bukkit.getServer().getMinecraftVersion(), buildOrHash);
|
||||
+ } else {
|
||||
+ paperVersion = "unknown";
|
||||
+ }
|
||||
+ metrics.addCustomChart(new Metrics.SimplePie("paper_version", () -> paperVersion));
|
||||
+
|
||||
+ metrics.addCustomChart(new Metrics.DrilldownPie("java_version", () -> {
|
||||
+ Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
+ String javaVersion = System.getProperty("java.version");
|
||||
+ Map<String, Integer> entry = new HashMap<>();
|
||||
+ entry.put(javaVersion, 1);
|
||||
+
|
||||
+ // http://openjdk.java.net/jeps/223
|
||||
+ // Java decided to change their versioning scheme and in doing so modified the java.version system
|
||||
+ // property to return $major[.$minor][.$secuity][-ea], as opposed to 1.$major.0_$identifier
|
||||
+ // we can handle pre-9 by checking if the "major" is equal to "1", otherwise, 9+
|
||||
+ String majorVersion = javaVersion.split("\\.")[0];
|
||||
+ String release;
|
||||
+
|
||||
+ int indexOf = javaVersion.lastIndexOf('.');
|
||||
+
|
||||
+ if (majorVersion.equals("1")) {
|
||||
+ release = "Java " + javaVersion.substring(0, indexOf);
|
||||
+ } else {
|
||||
+ // of course, it really wouldn't be all that simple if they didn't add a quirk, now would it
|
||||
+ // valid strings for the major may potentially include values such as -ea to deannotate a pre release
|
||||
+ Matcher versionMatcher = Pattern.compile("\\d+").matcher(majorVersion);
|
||||
+ if (versionMatcher.find()) {
|
||||
+ majorVersion = versionMatcher.group(0);
|
||||
+ }
|
||||
+ release = "Java " + majorVersion;
|
||||
+ }
|
||||
+ map.put(release, entry);
|
||||
+
|
||||
+ return map;
|
||||
+ }));
|
||||
+
|
||||
+ metrics.addCustomChart(new Metrics.DrilldownPie("legacy_plugins", () -> {
|
||||
+ Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||
+
|
||||
+ // count legacy plugins
|
||||
+ int legacy = 0;
|
||||
+ for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
|
||||
+ if (CraftMagicNumbers.isLegacy(plugin.getDescription())) {
|
||||
+ legacy++;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // insert real value as lower dimension
|
||||
+ Map<String, Integer> entry = new HashMap<>();
|
||||
+ entry.put(String.valueOf(legacy), 1);
|
||||
+
|
||||
+ // create buckets as higher dimension
|
||||
+ if (legacy == 0) {
|
||||
+ map.put("0 \uD83D\uDE0E", entry); // :sunglasses:
|
||||
+ } else if (legacy <= 5) {
|
||||
+ map.put("1-5", entry);
|
||||
+ } else if (legacy <= 10) {
|
||||
+ map.put("6-10", entry);
|
||||
+ } else if (legacy <= 25) {
|
||||
+ map.put("11-25", entry);
|
||||
+ } else if (legacy <= 50) {
|
||||
+ map.put("26-50", entry);
|
||||
+ } else {
|
||||
+ map.put("50+ \uD83D\uDE2D", entry); // :cry:
|
||||
+ }
|
||||
+
|
||||
+ return map;
|
||||
+ }));
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 8499eb5128d3269925ffb2c61d8532e9da47be4a..4917aa64505870a0521bc09ce4d4d3e37c03eaae 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -188,6 +188,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
io.papermc.paper.command.PaperCommands.registerCommands(this); // Paper - setup /paper command
|
||||
+ com.destroystokyo.paper.Metrics.PaperMetrics.startMetrics(); // Paper - start metrics
|
||||
|
||||
this.setPvpAllowed(dedicatedserverproperties.pvp);
|
||||
this.setFlightAllowed(dedicatedserverproperties.allowFlight);
|
||||
diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
index e6e070db9a6ee78e65dbff6f18cb5c8784202b9f..bd4c744c597201267418b07b9bcb522805c485eb 100644
|
||||
--- a/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
+++ b/src/main/java/org/spigotmc/SpigotConfig.java
|
||||
@@ -83,6 +83,7 @@ public class SpigotConfig
|
||||
MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", entry.getValue() );
|
||||
}
|
||||
|
||||
+ /* // Paper - Replace with our own
|
||||
if ( SpigotConfig.metrics == null )
|
||||
{
|
||||
try
|
||||
@@ -94,6 +95,7 @@ public class SpigotConfig
|
||||
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
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,763 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Nassim Jahnke <nassim@njahnke.dev>
|
||||
Date: Sun, 30 Oct 2022 23:47:26 +0100
|
||||
Subject: [PATCH] Remap reflection calls in plugins using internals
|
||||
|
||||
Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index 69f6e6aa7f71c155651826400c6d41f075eb9ea3..ccdf5120013adfe9604d503dd3570bc74c2cca91 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -36,7 +36,6 @@ dependencies {
|
||||
implementation("org.ow2.asm:asm-commons:9.7")
|
||||
implementation("org.spongepowered:configurate-yaml:4.2.0-SNAPSHOT") // Paper - config files
|
||||
implementation("commons-lang:commons-lang:2.6")
|
||||
- implementation("net.fabricmc:mapping-io:0.5.0") // Paper - needed to read mappings for stacktrace deobfuscation
|
||||
runtimeOnly("org.xerial:sqlite-jdbc:3.42.0.1")
|
||||
runtimeOnly("com.mysql:mysql-connector-j:8.2.0")
|
||||
runtimeOnly("com.lmax:disruptor:3.4.4") // Paper
|
||||
@@ -58,6 +57,12 @@ dependencies {
|
||||
testImplementation("org.junit-pioneer:junit-pioneer:2.2.0") // Paper - CartesianTest
|
||||
implementation("net.neoforged:AutoRenamingTool:2.0.3") // Paper - remap plugins
|
||||
implementation("net.neoforged:srgutils:1.0.9") // Paper - remap plugins - bump transitive of ART
|
||||
+ // Paper start - Remap reflection
|
||||
+ val reflectionRewriterVersion = "0.0.1-SNAPSHOT"
|
||||
+ implementation("io.papermc:reflection-rewriter:$reflectionRewriterVersion")
|
||||
+ implementation("io.papermc:reflection-rewriter-runtime:$reflectionRewriterVersion")
|
||||
+ implementation("io.papermc:reflection-rewriter-proxy-generator:$reflectionRewriterVersion")
|
||||
+ // Paper end - Remap reflection
|
||||
}
|
||||
|
||||
paperweight {
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
||||
index 59699c59fdfc611177fdb3136f84ab539b17d9c9..3be5e7629f0a45e32a70524eec13df7aab648f00 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/entity/ai/MobGoalHelper.java
|
||||
@@ -4,6 +4,7 @@ import com.destroystokyo.paper.entity.RangedEntity;
|
||||
import com.destroystokyo.paper.util.set.OptimizedSmallEnumSet;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
+import io.papermc.paper.util.MappingEnvironment;
|
||||
import io.papermc.paper.util.ObfHelper;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.EnumSet;
|
||||
@@ -249,7 +250,7 @@ public class MobGoalHelper {
|
||||
}
|
||||
|
||||
public static String getUsableName(Class<?> clazz) {
|
||||
- String name = ObfHelper.INSTANCE.deobfClassName(clazz.getName());
|
||||
+ String name = MappingEnvironment.reobf() ? ObfHelper.INSTANCE.deobfClassName(clazz.getName()) : clazz.getName();
|
||||
name = name.substring(name.lastIndexOf(".") + 1);
|
||||
boolean flag = false;
|
||||
// inner classes
|
||||
diff --git a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
|
||||
index 893ad5e7c2d32ccd64962d95d146bbd317c28ab8..3d73ea0e63c97b2b08e719b7be7af3894fb2d4e8 100644
|
||||
--- a/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
|
||||
+++ b/src/main/java/io/papermc/paper/configuration/serializer/PacketClassSerializer.java
|
||||
@@ -5,6 +5,7 @@ import com.google.common.collect.ImmutableBiMap;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.leangen.geantyref.TypeToken;
|
||||
import io.papermc.paper.configuration.serializer.collections.MapSerializer;
|
||||
+import io.papermc.paper.util.MappingEnvironment;
|
||||
import io.papermc.paper.util.ObfHelper;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
@@ -69,7 +70,7 @@ public final class PacketClassSerializer extends ScalarSerializer<Class<? extend
|
||||
@Override
|
||||
protected @Nullable Object serialize(final Class<? extends Packet<?>> packetClass, final Predicate<Class<?>> typeSupported) {
|
||||
final String name = packetClass.getName();
|
||||
- @Nullable String mojName = ObfHelper.INSTANCE.mappingsByMojangName() == null ? name : MOJANG_TO_OBF.inverse().get(name); // if the mappings are null, running on moj-mapped server
|
||||
+ @Nullable String mojName = ObfHelper.INSTANCE.mappingsByMojangName() == null || !MappingEnvironment.reobf() ? name : MOJANG_TO_OBF.inverse().get(name); // if the mappings are null, running on moj-mapped server
|
||||
if (mojName == null && MOJANG_TO_OBF.containsKey(name)) {
|
||||
mojName = name;
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java
|
||||
index f9a2c55a354c877749db3f92956de802ae575788..39182cdd17473da0123dc7172dce507eab29fedc 100644
|
||||
--- a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java
|
||||
+++ b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/PaperClassloaderBytecodeModifier.java
|
||||
@@ -1,12 +1,17 @@
|
||||
package io.papermc.paper.plugin.entrypoint.classloader;
|
||||
|
||||
import io.papermc.paper.plugin.configuration.PluginMeta;
|
||||
+import org.objectweb.asm.ClassReader;
|
||||
+import org.objectweb.asm.ClassWriter;
|
||||
|
||||
// Stub, implement in future.
|
||||
public class PaperClassloaderBytecodeModifier implements ClassloaderBytecodeModifier {
|
||||
|
||||
@Override
|
||||
public byte[] modify(PluginMeta configuration, byte[] bytecode) {
|
||||
- return bytecode;
|
||||
+ ClassReader classReader = new ClassReader(bytecode);
|
||||
+ ClassWriter classWriter = new ClassWriter(classReader, 0);
|
||||
+ classReader.accept(io.papermc.paper.pluginremap.reflect.ReflectionRemapper.visitor(classWriter), 0);
|
||||
+ return classWriter.toByteArray();
|
||||
}
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java b/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c653d1f5af8b407cfba715e6027dbb695046892a
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/pluginremap/reflect/PaperReflection.java
|
||||
@@ -0,0 +1,212 @@
|
||||
+package io.papermc.paper.pluginremap.reflect;
|
||||
+
|
||||
+import com.mojang.logging.LogUtils;
|
||||
+import io.papermc.paper.util.MappingEnvironment;
|
||||
+import io.papermc.paper.util.ObfHelper;
|
||||
+import io.papermc.reflectionrewriter.runtime.AbstractDefaultRulesReflectionProxy;
|
||||
+import io.papermc.reflectionrewriter.runtime.DefineClassReflectionProxy;
|
||||
+import java.lang.invoke.MethodHandles;
|
||||
+import java.nio.ByteBuffer;
|
||||
+import java.security.CodeSource;
|
||||
+import java.security.ProtectionDomain;
|
||||
+import java.util.Map;
|
||||
+import java.util.Objects;
|
||||
+import java.util.stream.Collectors;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import org.objectweb.asm.ClassReader;
|
||||
+import org.objectweb.asm.ClassWriter;
|
||||
+import org.slf4j.Logger;
|
||||
+
|
||||
+// todo proper inheritance handling
|
||||
+@SuppressWarnings("unused")
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class PaperReflection extends AbstractDefaultRulesReflectionProxy implements DefineClassReflectionProxy {
|
||||
+ // concat to avoid being rewritten by shadow
|
||||
+ private static final Logger LOGGER = LogUtils.getLogger();
|
||||
+ private static final String CB_PACKAGE_PREFIX = "org.bukkit.".concat("craftbukkit.");
|
||||
+ private static final String LEGACY_CB_PACKAGE_PREFIX = "org.bukkit.".concat("craftbukkit.") + MappingEnvironment.LEGACY_CB_VERSION + ".";
|
||||
+
|
||||
+ private final DefineClassReflectionProxy defineClassProxy;
|
||||
+ private final Map<String, ObfHelper.ClassMapping> mappingsByMojangName;
|
||||
+ private final Map<String, ObfHelper.ClassMapping> mappingsByObfName;
|
||||
+ // Reflection does not care about method return values, so this map removes the return value descriptor from the key
|
||||
+ private final Map<String, Map<String, String>> strippedMethodMappings;
|
||||
+
|
||||
+ PaperReflection() {
|
||||
+ this.defineClassProxy = DefineClassReflectionProxy.create(PaperReflection::processClass);
|
||||
+ if (!MappingEnvironment.hasMappings()) {
|
||||
+ this.mappingsByMojangName = Map.of();
|
||||
+ this.mappingsByObfName = Map.of();
|
||||
+ this.strippedMethodMappings = Map.of();
|
||||
+ return;
|
||||
+ }
|
||||
+ final ObfHelper obfHelper = ObfHelper.INSTANCE;
|
||||
+ this.mappingsByMojangName = Objects.requireNonNull(obfHelper.mappingsByMojangName(), "mappingsByMojangName");
|
||||
+ this.mappingsByObfName = Objects.requireNonNull(obfHelper.mappingsByObfName(), "mappingsByObfName");
|
||||
+ this.strippedMethodMappings = this.mappingsByMojangName.entrySet().stream().collect(Collectors.toUnmodifiableMap(
|
||||
+ Map.Entry::getKey,
|
||||
+ entry -> entry.getValue().strippedMethods()
|
||||
+ ));
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String mapClassName(final String name) {
|
||||
+ final ObfHelper.@Nullable ClassMapping mapping = this.mappingsByObfName.get(name);
|
||||
+ return mapping != null ? mapping.mojangName() : removeCraftBukkitRelocation(name);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String mapDeclaredMethodName(final Class<?> clazz, final String name, final Class<?>... parameterTypes) {
|
||||
+ final @Nullable Map<String, String> mapping = this.strippedMethodMappings.get(clazz.getName());
|
||||
+ if (mapping == null) {
|
||||
+ return name;
|
||||
+ }
|
||||
+ return mapping.getOrDefault(strippedMethodKey(name, parameterTypes), name);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String mapMethodName(final Class<?> clazz, final String name, final Class<?>... parameterTypes) {
|
||||
+ final @Nullable String mapped = this.findMappedMethodName(clazz, name, parameterTypes);
|
||||
+ return mapped != null ? mapped : name;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String mapDeclaredFieldName(final Class<?> clazz, final String name) {
|
||||
+ final ObfHelper.@Nullable ClassMapping mapping = this.mappingsByMojangName.get(clazz.getName());
|
||||
+ if (mapping == null) {
|
||||
+ return name;
|
||||
+ }
|
||||
+ return mapping.fieldsByObf().getOrDefault(name, name);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ protected String mapFieldName(final Class<?> clazz, final String name) {
|
||||
+ final @Nullable String mapped = this.findMappedFieldName(clazz, name);
|
||||
+ return mapped != null ? mapped : name;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable String findMappedMethodName(final Class<?> clazz, final String name, final Class<?>... parameterTypes) {
|
||||
+ final Map<String, String> map = this.strippedMethodMappings.get(clazz.getName());
|
||||
+ @Nullable String mapped = null;
|
||||
+ if (map != null) {
|
||||
+ mapped = map.get(strippedMethodKey(name, parameterTypes));
|
||||
+ if (mapped != null) {
|
||||
+ return mapped;
|
||||
+ }
|
||||
+ }
|
||||
+ // JVM checks super before interfaces
|
||||
+ final Class<?> superClass = clazz.getSuperclass();
|
||||
+ if (superClass != null) {
|
||||
+ mapped = this.findMappedMethodName(superClass, name, parameterTypes);
|
||||
+ }
|
||||
+ if (mapped == null) {
|
||||
+ for (final Class<?> i : clazz.getInterfaces()) {
|
||||
+ mapped = this.findMappedMethodName(i, name, parameterTypes);
|
||||
+ if (mapped != null) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return mapped;
|
||||
+ }
|
||||
+
|
||||
+ private @Nullable String findMappedFieldName(final Class<?> clazz, final String name) {
|
||||
+ final ObfHelper.ClassMapping mapping = this.mappingsByMojangName.get(clazz.getName());
|
||||
+ @Nullable String mapped = null;
|
||||
+ if (mapping != null) {
|
||||
+ mapped = mapping.fieldsByObf().get(name);
|
||||
+ if (mapped != null) {
|
||||
+ return mapped;
|
||||
+ }
|
||||
+ }
|
||||
+ // The JVM checks super before interfaces
|
||||
+ final Class<?> superClass = clazz.getSuperclass();
|
||||
+ if (superClass != null) {
|
||||
+ mapped = this.findMappedFieldName(superClass, name);
|
||||
+ }
|
||||
+ if (mapped == null) {
|
||||
+ for (final Class<?> i : clazz.getInterfaces()) {
|
||||
+ mapped = this.findMappedFieldName(i, name);
|
||||
+ if (mapped != null) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ return mapped;
|
||||
+ }
|
||||
+
|
||||
+ private static String strippedMethodKey(final String methodName, final Class<?>... parameterTypes) {
|
||||
+ return methodName + parameterDescriptor(parameterTypes);
|
||||
+ }
|
||||
+
|
||||
+ private static String parameterDescriptor(final Class<?>... parameterTypes) {
|
||||
+ final StringBuilder builder = new StringBuilder();
|
||||
+ builder.append('(');
|
||||
+ for (final Class<?> parameterType : parameterTypes) {
|
||||
+ builder.append(parameterType.descriptorString());
|
||||
+ }
|
||||
+ builder.append(')');
|
||||
+ return builder.toString();
|
||||
+ }
|
||||
+
|
||||
+ private static String removeCraftBukkitRelocation(final String name) {
|
||||
+ if (MappingEnvironment.hasMappings()) {
|
||||
+ // Relocation is applied in reobf, and when mappings are present they handle the relocation
|
||||
+ return name;
|
||||
+ }
|
||||
+ if (name.startsWith(LEGACY_CB_PACKAGE_PREFIX)) {
|
||||
+ return CB_PACKAGE_PREFIX + name.substring(LEGACY_CB_PACKAGE_PREFIX.length());
|
||||
+ }
|
||||
+ return name;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object loader, final byte[] b, final int off, final int len) throws ClassFormatError {
|
||||
+ return this.defineClassProxy.defineClass(loader, b, off, len);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object loader, final String name, final byte[] b, final int off, final int len) throws ClassFormatError {
|
||||
+ return this.defineClassProxy.defineClass(loader, name, b, off, len);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object loader, final @Nullable String name, final byte[] b, final int off, final int len, final @Nullable ProtectionDomain protectionDomain) throws ClassFormatError {
|
||||
+ return this.defineClassProxy.defineClass(loader, name, b, off, len, protectionDomain);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object loader, final String name, final ByteBuffer b, final ProtectionDomain protectionDomain) throws ClassFormatError {
|
||||
+ return this.defineClassProxy.defineClass(loader, name, b, protectionDomain);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object secureLoader, final String name, final byte[] b, final int off, final int len, final CodeSource cs) {
|
||||
+ return this.defineClassProxy.defineClass(secureLoader, name, b, off, len, cs);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final Object secureLoader, final String name, final ByteBuffer b, final CodeSource cs) {
|
||||
+ return this.defineClassProxy.defineClass(secureLoader, name, b, cs);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public Class<?> defineClass(final MethodHandles.Lookup lookup, final byte[] bytes) throws IllegalAccessException {
|
||||
+ return this.defineClassProxy.defineClass(lookup, bytes);
|
||||
+ }
|
||||
+
|
||||
+ // todo apply bytecode remap here as well
|
||||
+ private static byte[] processClass(final byte[] bytes) {
|
||||
+ try {
|
||||
+ final ClassReader reader = new ClassReader(bytes);
|
||||
+ final ClassWriter writer = new ClassWriter(reader, 0);
|
||||
+ reader.accept(ReflectionRemapper.visitor(writer), 0);
|
||||
+ return writer.toByteArray();
|
||||
+ } catch (final Exception ex) {
|
||||
+ LOGGER.warn("Failed to process class bytes", ex);
|
||||
+ return bytes;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java b/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..5fa5868e82d1f00498d0c5771369e1718b2df4ee
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/pluginremap/reflect/ReflectionRemapper.java
|
||||
@@ -0,0 +1,54 @@
|
||||
+package io.papermc.paper.pluginremap.reflect;
|
||||
+
|
||||
+import io.papermc.asm.ClassInfoProvider;
|
||||
+import io.papermc.asm.RewriteRuleVisitorFactory;
|
||||
+import io.papermc.paper.util.MappingEnvironment;
|
||||
+import io.papermc.reflectionrewriter.BaseReflectionRules;
|
||||
+import io.papermc.reflectionrewriter.DefineClassRule;
|
||||
+import io.papermc.reflectionrewriter.proxygenerator.ProxyGenerator;
|
||||
+import java.lang.invoke.MethodHandles;
|
||||
+import java.lang.reflect.Method;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+import org.objectweb.asm.ClassVisitor;
|
||||
+import org.objectweb.asm.Opcodes;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class ReflectionRemapper {
|
||||
+ private static final String PAPER_REFLECTION_HOLDER = "io.papermc.paper.pluginremap.reflect.PaperReflectionHolder";
|
||||
+ private static final String PAPER_REFLECTION_HOLDER_DESC = PAPER_REFLECTION_HOLDER.replace('.', '/');
|
||||
+ private static final RewriteRuleVisitorFactory VISITOR_FACTORY = RewriteRuleVisitorFactory.create(
|
||||
+ Opcodes.ASM9,
|
||||
+ chain -> chain.then(new BaseReflectionRules(PAPER_REFLECTION_HOLDER).rules())
|
||||
+ .then(DefineClassRule.create(PAPER_REFLECTION_HOLDER_DESC, true)),
|
||||
+ ClassInfoProvider.basic()
|
||||
+ );
|
||||
+
|
||||
+ static {
|
||||
+ if (!MappingEnvironment.reobf()) {
|
||||
+ setupProxy();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private ReflectionRemapper() {
|
||||
+ }
|
||||
+
|
||||
+ public static ClassVisitor visitor(final ClassVisitor parent) {
|
||||
+ if (MappingEnvironment.reobf()) {
|
||||
+ return parent;
|
||||
+ }
|
||||
+ return VISITOR_FACTORY.createVisitor(parent);
|
||||
+ }
|
||||
+
|
||||
+ private static void setupProxy() {
|
||||
+ try {
|
||||
+ final byte[] bytes = ProxyGenerator.generateProxy(PaperReflection.class, PAPER_REFLECTION_HOLDER_DESC);
|
||||
+ final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
+ final Class<?> generated = lookup.defineClass(bytes);
|
||||
+ final Method init = generated.getDeclaredMethod("init", PaperReflection.class);
|
||||
+ init.invoke(null, new PaperReflection());
|
||||
+ } catch (final ReflectiveOperationException ex) {
|
||||
+ throw new RuntimeException(ex);
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/MappingEnvironment.java b/src/main/java/io/papermc/paper/util/MappingEnvironment.java
|
||||
index 8e4229634d41a42b3d93948eebb77def7c0c72b1..450d20a7a43868c06c43a9da07345e47786c75d4 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/MappingEnvironment.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/MappingEnvironment.java
|
||||
@@ -10,6 +10,7 @@ import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
|
||||
@DefaultQualifier(NonNull.class)
|
||||
public final class MappingEnvironment {
|
||||
+ public static final String LEGACY_CB_VERSION = "v1_20_R3";
|
||||
private static final @Nullable String MAPPINGS_HASH = readMappingsHash();
|
||||
private static final boolean REOBF = checkReobf();
|
||||
|
||||
diff --git a/src/main/java/io/papermc/paper/util/ObfHelper.java b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
index 3f03d5efcd95e3adb76dc4292b3d2c420fdc58af..9eecb96cb7ec6d6a157ed2c5637e05138c3f55b9 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/ObfHelper.java
|
||||
@@ -2,19 +2,13 @@ package io.papermc.paper.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
-import java.io.InputStreamReader;
|
||||
-import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
-import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
-import net.fabricmc.mappingio.MappingReader;
|
||||
-import net.fabricmc.mappingio.format.MappingFormat;
|
||||
-import net.fabricmc.mappingio.tree.MappingTree;
|
||||
-import net.fabricmc.mappingio.tree.MemoryMappingTree;
|
||||
+import net.neoforged.srgutils.IMappingFile;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
@@ -23,9 +17,6 @@ import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
public enum ObfHelper {
|
||||
INSTANCE;
|
||||
|
||||
- public static final String MOJANG_PLUS_YARN_NAMESPACE = "mojang+yarn";
|
||||
- public static final String SPIGOT_NAMESPACE = "spigot";
|
||||
-
|
||||
private final @Nullable Map<String, ClassMapping> mappingsByObfName;
|
||||
private final @Nullable Map<String, ClassMapping> mappingsByMojangName;
|
||||
|
||||
@@ -93,55 +84,73 @@ public enum ObfHelper {
|
||||
return null;
|
||||
}
|
||||
try (final InputStream mappingsInputStream = MappingEnvironment.mappingsStream()) {
|
||||
- final MemoryMappingTree tree = new MemoryMappingTree();
|
||||
- MappingReader.read(new InputStreamReader(mappingsInputStream, StandardCharsets.UTF_8), MappingFormat.TINY_2_FILE, tree);
|
||||
+ final IMappingFile mappings = IMappingFile.load(mappingsInputStream); // Mappings are mojang->spigot
|
||||
final Set<ClassMapping> classes = new HashSet<>();
|
||||
|
||||
final StringPool pool = new StringPool();
|
||||
- for (final MappingTree.ClassMapping cls : tree.getClasses()) {
|
||||
+ for (final IMappingFile.IClass cls : mappings.getClasses()) {
|
||||
final Map<String, String> methods = new HashMap<>();
|
||||
+ final Map<String, String> fields = new HashMap<>();
|
||||
+ final Map<String, String> strippedMethods = new HashMap<>();
|
||||
|
||||
- for (final MappingTree.MethodMapping methodMapping : cls.getMethods()) {
|
||||
+ for (final IMappingFile.IMethod methodMapping : cls.getMethods()) {
|
||||
methods.put(
|
||||
pool.string(methodKey(
|
||||
- Objects.requireNonNull(methodMapping.getName(SPIGOT_NAMESPACE)),
|
||||
- Objects.requireNonNull(methodMapping.getDesc(SPIGOT_NAMESPACE))
|
||||
+ Objects.requireNonNull(methodMapping.getMapped()),
|
||||
+ Objects.requireNonNull(methodMapping.getMappedDescriptor())
|
||||
)),
|
||||
- pool.string(Objects.requireNonNull(methodMapping.getName(MOJANG_PLUS_YARN_NAMESPACE)))
|
||||
+ pool.string(Objects.requireNonNull(methodMapping.getOriginal()))
|
||||
+ );
|
||||
+
|
||||
+ strippedMethods.put(
|
||||
+ pool.string(pool.string(strippedMethodKey(
|
||||
+ methodMapping.getMapped(),
|
||||
+ methodMapping.getDescriptor()
|
||||
+ ))),
|
||||
+ pool.string(methodMapping.getOriginal())
|
||||
+ );
|
||||
+ }
|
||||
+ for (final IMappingFile.IField field : cls.getFields()) {
|
||||
+ fields.put(
|
||||
+ pool.string(field.getMapped()),
|
||||
+ pool.string(field.getOriginal())
|
||||
);
|
||||
}
|
||||
|
||||
final ClassMapping map = new ClassMapping(
|
||||
- Objects.requireNonNull(cls.getName(SPIGOT_NAMESPACE)).replace('/', '.'),
|
||||
- Objects.requireNonNull(cls.getName(MOJANG_PLUS_YARN_NAMESPACE)).replace('/', '.'),
|
||||
- Map.copyOf(methods)
|
||||
+ Objects.requireNonNull(cls.getMapped()).replace('/', '.'),
|
||||
+ Objects.requireNonNull(cls.getOriginal()).replace('/', '.'),
|
||||
+ Map.copyOf(methods),
|
||||
+ Map.copyOf(fields),
|
||||
+ Map.copyOf(strippedMethods)
|
||||
);
|
||||
classes.add(map);
|
||||
}
|
||||
|
||||
return Set.copyOf(classes);
|
||||
} catch (final IOException ex) {
|
||||
- System.err.println("Failed to load mappings for stacktrace deobfuscation.");
|
||||
+ System.err.println("Failed to load mappings.");
|
||||
ex.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
- public static String methodKey(final String obfName, final String obfDescriptor) {
|
||||
- return obfName + obfDescriptor;
|
||||
+ public static String strippedMethodKey(final String methodName, final String methodDescriptor) {
|
||||
+ final String methodKey = methodKey(methodName, methodDescriptor);
|
||||
+ final int returnDescriptorEnd = methodKey.indexOf(')');
|
||||
+ return methodKey.substring(0, returnDescriptorEnd + 1);
|
||||
}
|
||||
|
||||
- private static final class StringPool {
|
||||
- private final Map<String, String> pool = new HashMap<>();
|
||||
-
|
||||
- public String string(final String string) {
|
||||
- return this.pool.computeIfAbsent(string, Function.identity());
|
||||
- }
|
||||
+ public static String methodKey(final String methodName, final String methodDescriptor) {
|
||||
+ return methodName + methodDescriptor;
|
||||
}
|
||||
|
||||
public record ClassMapping(
|
||||
String obfName,
|
||||
String mojangName,
|
||||
- Map<String, String> methodsByObf
|
||||
+ Map<String, String> methodsByObf,
|
||||
+ Map<String, String> fieldsByObf,
|
||||
+ // obf name with mapped desc to mapped name. return value is excluded from desc as reflection doesn't use it
|
||||
+ Map<String, String> strippedMethods
|
||||
) {}
|
||||
}
|
||||
diff --git a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
index eb910d4abf91488fa7cf1f5d47e0ee916c47f512..0b65fdf53124f3dd042b2363b1b8df8e1ca7de00 100644
|
||||
--- a/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
+++ b/src/main/java/io/papermc/paper/util/StacktraceDeobfuscator.java
|
||||
@@ -1,12 +1,11 @@
|
||||
package io.papermc.paper.util;
|
||||
|
||||
import io.papermc.paper.configuration.GlobalConfiguration;
|
||||
-import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
-import it.unimi.dsi.fastutil.ints.IntList;
|
||||
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
+import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
-import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -22,14 +21,17 @@ import org.objectweb.asm.Opcodes;
|
||||
public enum StacktraceDeobfuscator {
|
||||
INSTANCE;
|
||||
|
||||
- private final Map<Class<?>, Map<String, IntList>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
|
||||
+ private final Map<Class<?>, Int2ObjectMap<String>> lineMapCache = Collections.synchronizedMap(new LinkedHashMap<>(128, 0.75f, true) {
|
||||
@Override
|
||||
- protected boolean removeEldestEntry(final Map.Entry<Class<?>, Map<String, IntList>> eldest) {
|
||||
+ protected boolean removeEldestEntry(final Map.Entry<Class<?>, Int2ObjectMap<String>> eldest) {
|
||||
return this.size() > 127;
|
||||
}
|
||||
});
|
||||
|
||||
public void deobfuscateThrowable(final Throwable throwable) {
|
||||
+ if (!MappingEnvironment.reobf()) {
|
||||
+ return;
|
||||
+ }
|
||||
if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
||||
return;
|
||||
}
|
||||
@@ -45,6 +47,9 @@ public enum StacktraceDeobfuscator {
|
||||
}
|
||||
|
||||
public StackTraceElement[] deobfuscateStacktrace(final StackTraceElement[] traceElements) {
|
||||
+ if (!MappingEnvironment.reobf()) {
|
||||
+ return traceElements;
|
||||
+ }
|
||||
if (GlobalConfiguration.get() != null && !GlobalConfiguration.get().logging.deobfuscateStacktraces) { // handle null as true
|
||||
return traceElements;
|
||||
}
|
||||
@@ -89,18 +94,7 @@ public enum StacktraceDeobfuscator {
|
||||
}
|
||||
|
||||
private @Nullable String determineMethodForLine(final Class<?> clazz, final int lineNumber) {
|
||||
- final Map<String, IntList> lineMap = this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap);
|
||||
- for (final var entry : lineMap.entrySet()) {
|
||||
- final String methodKey = entry.getKey();
|
||||
- final IntList lines = entry.getValue();
|
||||
- for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
- final int num = lines.getInt(i);
|
||||
- if (num == lineNumber) {
|
||||
- return methodKey;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- return null;
|
||||
+ return this.lineMapCache.computeIfAbsent(clazz, StacktraceDeobfuscator::buildLineMap).get(lineNumber);
|
||||
}
|
||||
|
||||
private static String sourceFileName(final String fullClassName) {
|
||||
@@ -112,34 +106,27 @@ public enum StacktraceDeobfuscator {
|
||||
return rootClassName + ".java";
|
||||
}
|
||||
|
||||
- private static Map<String, IntList> buildLineMap(final Class<?> key) {
|
||||
- final Map<String, IntList> lineMap = new HashMap<>();
|
||||
+ private static Int2ObjectMap<String> buildLineMap(final Class<?> key) {
|
||||
+ final StringPool pool = new StringPool();
|
||||
+ final Int2ObjectMap<String> lineMap = new Int2ObjectOpenHashMap<>();
|
||||
final class LineCollectingMethodVisitor extends MethodVisitor {
|
||||
- private final IntList lines = new IntArrayList();
|
||||
private final String name;
|
||||
private final String descriptor;
|
||||
|
||||
- LineCollectingMethodVisitor(String name, String descriptor) {
|
||||
+ LineCollectingMethodVisitor(final String name, final String descriptor) {
|
||||
super(Opcodes.ASM9);
|
||||
this.name = name;
|
||||
this.descriptor = descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void visitLineNumber(int line, Label start) {
|
||||
- super.visitLineNumber(line, start);
|
||||
- this.lines.add(line);
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public void visitEnd() {
|
||||
- super.visitEnd();
|
||||
- lineMap.put(ObfHelper.methodKey(this.name, this.descriptor), this.lines);
|
||||
+ public void visitLineNumber(final int line, final Label start) {
|
||||
+ lineMap.put(line, pool.string(ObfHelper.methodKey(this.name, this.descriptor)));
|
||||
}
|
||||
}
|
||||
final ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
- public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||
+ public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
|
||||
return new LineCollectingMethodVisitor(name, descriptor);
|
||||
}
|
||||
};
|
||||
diff --git a/src/main/java/io/papermc/paper/util/StringPool.java b/src/main/java/io/papermc/paper/util/StringPool.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c0a486cb46ff30353c3ff09567891cd36238eeb4
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/util/StringPool.java
|
||||
@@ -0,0 +1,34 @@
|
||||
+package io.papermc.paper.util;
|
||||
+
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+import java.util.function.Function;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+/**
|
||||
+ * De-duplicates {@link String} instances without using {@link String#intern()}.
|
||||
+ *
|
||||
+ * <p>Interning may not be desired as we may want to use the heap for our pool,
|
||||
+ * so it can be garbage collected as normal, etc.</p>
|
||||
+ *
|
||||
+ * <p>Additionally, interning can be slow due to the potentially large size of the
|
||||
+ * pool (as it is shared for the entire JVM), and because most JVMs implement
|
||||
+ * it using JNI.</p>
|
||||
+ */
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class StringPool {
|
||||
+ private final Map<String, String> pool;
|
||||
+
|
||||
+ public StringPool() {
|
||||
+ this(new HashMap<>());
|
||||
+ }
|
||||
+
|
||||
+ public StringPool(final Map<String, String> map) {
|
||||
+ this.pool = map;
|
||||
+ }
|
||||
+
|
||||
+ public String string(final String string) {
|
||||
+ return this.pool.computeIfAbsent(string, Function.identity());
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
||||
index 0a2e894d8bd1268fcbd4460745304816be46beff..93d6813c63618e39efb1f9f0097843dc9c014fed 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/behavior/Behavior.java
|
||||
@@ -32,7 +32,7 @@ public abstract class Behavior<E extends LivingEntity> implements BehaviorContro
|
||||
this.maxDuration = maxRunTime;
|
||||
this.entryCondition = requiredMemoryState;
|
||||
// Paper start - configurable behavior tick rate and timings
|
||||
- String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
|
||||
+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName();
|
||||
int lastSeparator = key.lastIndexOf('.');
|
||||
if (lastSeparator != -1) {
|
||||
key = key.substring(lastSeparator + 1);
|
||||
diff --git a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
||||
index de2dfaf3cb41bafd48fa4699096a0a7304cd3201..05d1087e318137323718f6ff30f68b70bbc5bec9 100644
|
||||
--- a/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
||||
+++ b/src/main/java/net/minecraft/world/entity/ai/sensing/Sensor.java
|
||||
@@ -33,7 +33,7 @@ public abstract class Sensor<E extends LivingEntity> {
|
||||
|
||||
public Sensor(int senseInterval) {
|
||||
// Paper start - configurable sensor tick rate and timings
|
||||
- String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
|
||||
+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName();
|
||||
int lastSeparator = key.lastIndexOf('.');
|
||||
if (lastSeparator != -1) {
|
||||
key = key.substring(lastSeparator + 1);
|
||||
diff --git a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
index 106af2b2c7ff72c7549975aef75cdcff8d9a7d97..c0adfa72a6b43b72825797572c5c2557042e2a42 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
+++ b/src/main/java/net/minecraft/world/level/redstone/CollectingNeighborUpdater.java
|
||||
@@ -141,7 +141,7 @@ public class CollectingNeighborUpdater implements NeighborUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
- interface NeighborUpdates {
|
||||
+ public interface NeighborUpdates { // Paper - TODO make package-private again (it is just made public for testing LambdaMetafactory remapping)
|
||||
boolean runNext(Level world);
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
|
||||
index bc008773f8cf0d416182152ecfc5a64b70744009..757be022ace25b816d4630d134e6b7ff74e584c3 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
|
||||
@@ -57,36 +57,20 @@ public class Commodore {
|
||||
|
||||
// Paper start - Plugin rewrites
|
||||
private static final String CB_PACKAGE = org.bukkit.Bukkit.getServer().getClass().getPackageName().replace('.', '/');
|
||||
- private static final Map<String, String> SEARCH_AND_REMOVE = initReplacementsMap();
|
||||
- private static Map<String, String> initReplacementsMap() {
|
||||
- Map<String, String> getAndRemove = new HashMap<>();
|
||||
- // Be wary of maven shade's relocations
|
||||
-
|
||||
- final java.util.jar.Manifest manifest = io.papermc.paper.util.JarManifests.manifest(Commodore.class);
|
||||
- if (Boolean.getBoolean( "debug.rewriteForIde") && manifest != null)
|
||||
- {
|
||||
- // unversion incoming calls for pre-relocate debug work
|
||||
- final String NMS_REVISION_PACKAGE = "v" + manifest.getMainAttributes().getValue("CraftBukkit-Package-Version") + "/";
|
||||
-
|
||||
- getAndRemove.put("org/bukkit/".concat("craftbukkit/" + NMS_REVISION_PACKAGE), NMS_REVISION_PACKAGE);
|
||||
- }
|
||||
-
|
||||
- return getAndRemove;
|
||||
- }
|
||||
+ private static final String CB_PACKAGE_PREFIX = "org/bukkit/".concat("craftbukkit/");
|
||||
+ private static final String LEGACY_CB_PACKAGE_PREFIX = CB_PACKAGE_PREFIX + io.papermc.paper.util.MappingEnvironment.LEGACY_CB_VERSION + "/";
|
||||
|
||||
@Nonnull
|
||||
private static String getOriginalOrRewrite(@Nonnull String original)
|
||||
{
|
||||
- String rewrite = null;
|
||||
- for ( Map.Entry<String, String> entry : SEARCH_AND_REMOVE.entrySet() )
|
||||
- {
|
||||
- if ( original.contains( entry.getKey() ) )
|
||||
- {
|
||||
- rewrite = original.replace( entry.getValue(), "" );
|
||||
+ // Relocation is applied in reobf, and when mappings are present they handle the relocation
|
||||
+ if (!io.papermc.paper.util.MappingEnvironment.reobf() && !io.papermc.paper.util.MappingEnvironment.hasMappings()) {
|
||||
+ if (original.contains(LEGACY_CB_PACKAGE_PREFIX)) {
|
||||
+ original = original.replace(LEGACY_CB_PACKAGE_PREFIX, CB_PACKAGE_PREFIX);
|
||||
}
|
||||
}
|
||||
|
||||
- return rewrite != null ? rewrite : original;
|
||||
+ return original;
|
||||
}
|
||||
// Paper end - Plugin rewrites
|
||||
|
||||
@@ -156,7 +140,7 @@ public class Commodore {
|
||||
ClassReader cr = new ClassReader(b);
|
||||
ClassWriter cw = new ClassWriter(cr, 0);
|
||||
|
||||
- cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, cw) {
|
||||
+ cr.accept(new ClassRemapper(new ClassVisitor(Opcodes.ASM9, io.papermc.paper.pluginremap.reflect.ReflectionRemapper.visitor(cw)) { // Paper
|
||||
|
||||
// Paper start - Rewrite plugins
|
||||
@Override
|
|
@ -1,48 +0,0 @@
|
|||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Jason Penilla <11360596+jpenilla@users.noreply.github.com>
|
||||
Date: Mon, 12 Feb 2024 22:19:03 -0700
|
||||
Subject: [PATCH] Add WorldEdit plugin flag test task
|
||||
|
||||
|
||||
diff --git a/build.gradle.kts b/build.gradle.kts
|
||||
index ccdf5120013adfe9604d503dd3570bc74c2cca91..73b3e61cbbdf5025c24d289597648f01f632a5e3 100644
|
||||
--- a/build.gradle.kts
|
||||
+++ b/build.gradle.kts
|
||||
@@ -1,9 +1,11 @@
|
||||
import io.papermc.paperweight.util.*
|
||||
+import xyz.jpenilla.runpaper.task.RunServer
|
||||
|
||||
plugins {
|
||||
java
|
||||
`maven-publish`
|
||||
id("com.github.johnrengelman.shadow")
|
||||
+ id("xyz.jpenilla.run-paper") version "2.2.3" apply false
|
||||
}
|
||||
|
||||
val log4jPlugins = sourceSets.create("log4jPlugins")
|
||||
@@ -186,6 +188,25 @@ tasks.registerRunTask("runDevServer") {
|
||||
jvmArgs("-DPaper.pushPaperAssetsRoot=true")
|
||||
}
|
||||
|
||||
+tasks.register<RunServer>("runWithPlugins") {
|
||||
+ version.set(providers.gradleProperty("mcVersion"))
|
||||
+ runJar(rootProject.tasks.named<io.papermc.paperweight.tasks.CreateBundlerJar>("createMojmapBundlerJar").flatMap { it.outputZip })
|
||||
+ downloadPlugins {
|
||||
+ url("https://ci.enginehub.org/repository/download/bt10/23382:id/worldedit-bukkit-7.2.19-dist.jar?branch=version/7.2.x&guest=1")
|
||||
+ url("https://www.patreon.com/file?h=89830486&i=15920178")
|
||||
+ url("https://dev.bukkit.org/projects/grief-prevention/files/4433061/download")
|
||||
+ github("EssentialsX", "Essentials", "2.20.1", "EssentialsX-2.20.1.jar")
|
||||
+ hangar("squaremap", "1.2.3")
|
||||
+ hangar("FancyHolograms", "2.0.5")
|
||||
+ hangar("Chunky", "1.3.92")
|
||||
+ hangar("Multiverse-Core", "4.3.12")
|
||||
+ // Once they fix package parsing
|
||||
+ // hangar("Denizen", "1.3.0-Build-1803")
|
||||
+ // hangar("GrimAnticheat", "2.3.58")
|
||||
+ // hangar("ProtocolLib", "5.1.0")
|
||||
+ }
|
||||
+ runDirectory.set(rootProject.layout.projectDirectory.dir("run"))
|
||||
+}
|
||||
tasks.registerRunTask("runBundler") {
|
||||
description = "Spin up a test server from the Mojang mapped bundler jar"
|
||||
classpath(rootProject.tasks.named<io.papermc.paperweight.tasks.CreateBundlerJar>("createMojmapBundlerJar").flatMap { it.outputZip })
|
|
@ -17,7 +17,7 @@ index 5a720c0a751e7a83976727d01cc5bc6282702255..48c40f12649735f14204c516eace6905
|
|||
+ // Paper start
|
||||
+ @Override
|
||||
+ public File getPluginsFolder() {
|
||||
+ return (File) this.console.options.valueOf("plugins");
|
||||
+ return this.console.getPluginsFolder();
|
||||
+ }
|
||||
+
|
||||
+ private List<File> extraPluginJars() {
|
||||
|
|
|
@ -273,7 +273,7 @@ index 0000000000000000000000000000000000000000..8117578ced94aa6bf01871f6526a3883
|
|||
+ }
|
||||
+
|
||||
+ public static String getUsableName(Class<?> clazz) {
|
||||
+ String name = ObfHelper.INSTANCE.deobfClassName(clazz.getName());
|
||||
+ String name = MappingEnvironment.reobf() ? ObfHelper.INSTANCE.deobfClassName(clazz.getName()) : clazz.getName();
|
||||
+ name = name.substring(name.lastIndexOf(".") + 1);
|
||||
+ boolean flag = false;
|
||||
+ // inner classes
|
||||
|
|
|
@ -47,7 +47,7 @@ index f639cafa64d98a001e622882c647701547f5c3ac..03092417cd8ab5c6d266f3af9f20f47b
|
|||
this.maxDuration = maxRunTime;
|
||||
this.entryCondition = requiredMemoryState;
|
||||
+ // Paper start - configurable behavior tick rate and timings
|
||||
+ String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
|
||||
+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName();
|
||||
+ int lastSeparator = key.lastIndexOf('.');
|
||||
+ if (lastSeparator != -1) {
|
||||
+ key = key.substring(lastSeparator + 1);
|
||||
|
@ -107,7 +107,7 @@ index 671fc7725d7c801a2ba009da5bd1bc1a9530f187..9e90cb2f51d1bacacb287e912d14ab91
|
|||
|
||||
public Sensor(int senseInterval) {
|
||||
+ // Paper start - configurable sensor tick rate and timings
|
||||
+ String key = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName());
|
||||
+ String key = io.papermc.paper.util.MappingEnvironment.reobf() ? io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(this.getClass().getName()) : this.getClass().getName();
|
||||
+ int lastSeparator = key.lastIndexOf('.');
|
||||
+ if (lastSeparator != -1) {
|
||||
+ key = key.substring(lastSeparator + 1);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue