Paper/patches/api/0015-Expose-server-build-information.patch
Bjarne Koll 4d20922b79
Updated Upstream (Bukkit/CraftBukkit/Spigot) (#11024)
* Updated Upstream (Bukkit/CraftBukkit/Spigot)

Upstream has released updates that appear to apply and compile correctly.
This update has not been tested by PaperMC and as with ANY update, please do your own testing

Bukkit Changes:
e86f4dc4 PR-1041: Improve getPlayer(String) docs to clarify it matches the name
9738f005 Fix spawner API documentation
69ebd9fd PR-1034: Add TrialSpawnerSpawnEvent
23cffd9c PR-973: Improve spawner API and add API for Trial Spawners
8bf19163 PR-1038: Clarify HumanEntity#openInventory(InventoryView) JavaDoc
1ff76351 SPIGOT-7732, SPIGOT-7786: Add freezing damage modifier
02161cb4 PR-1034: Add CreatureSpawnEvent.SpawnReason#TRIAL_SPAWNER
f9cb6f34 SPIGOT-7777: All entity potion effects are removed on death
25d548eb PR-1031: Expose Creeper igniter
ccbf0915 SPIGOT-7770: Reserve spaces in shaped recipes for blank ingredients
17f7097c Clarify ambiguity around what is API
71714f0c Remove note from InventoryView JavaDoc
aaf49731 PR-1030: Deprecate more unused methods in UnsafeValues
3a9dc689 SPIGOT-7771: Material.getDefaultAttributes always returns an empty map

CraftBukkit Changes:
c3ceeb6f7 SPIGOT-7814: Call PlayerShearEntityEvent for Bogged
97b1e4f58 Fix wolf armor not dropping from use of shears
fd2ef563a SPIGOT-7812: Revert "SPIGOT-7809: Restore shield/banner conversion for base colours"
f672c351b SPIGOT-7811: Enchantments are applied on sweeping attack even if damage event is cancelled
cfe29350b SPIGOT-7808: Fix implementation of Enchantment#getName() for bad name return
19335f69e SPIGOT-7809: Restore shield/banner conversion for base colours
ae4f5a0be SPIGOT-7805: Fix jukebox deserialization
62e3b73a4 SPIGOT-7804: Fix written book serialization
aac911d26 SPIGOT-7800, SPIGOT-7801: Keep vanilla behaviour for items dropped on player death
13ece474f PR-1429: Implement TrialSpawnerSpawnEvent
bf13e9113 PR-1354: Improve spawner API and add API for Trial Spawners
515fe49e1 Increase outdated build delay
9cd5a19a0 SPIGOT-7794: Cancelling InventoryItemMoveEvent destroys items
ce40c7b14 SPIGOT-7796: Kickplayer newlines not working
5167256ff SPIGOT-7795: Fix damage/stats ignore the invulnerable damage time
f993563ee Improve cross-world teleportation handling
ab29122cf PR-1433: HumanEntity#openInventory(InventoryView) should only support views belonging to the player
764a541c5 SPIGOT-7732: Issue with the "hurt()" method of EntityLiving and invulnerable time
820084b5f SPIGOT-7791: Skull BlockState with null profile causes NullPointerException
5e46f1c82 SPIGOT-7785: Teleporting a player at the right moment can mess up vanilla teleportation
cbd95a6b3 SPIGOT-7772: Include hidden / non-sampled players in player count
3153debc5 SPIGOT-7790: Server crashes after bee nest is forced to update
e77bb26bb SPIGOT-7788: The healing power of friendship advancement is never granted
ee3d7258a SPIGOT-7789: Fix NPE in CraftMetaFirework applyToItem
2889b3a11 PR-1429: Add CreatureSpawnEvent.SpawnReason#TRIAL_SPAWNER
cdd05bc7f SPIGOT-7777: Speed attribute stays after death; missing EntityPotionEffectEvent call
d0e6af2d4 PR-1428: Expose Creeper igniter
d01c70e93 PR-1425: Fix bytecode transformation taking care of class-to-interface compatibility.
b2b08f68c SPIGOT-7770: Fix certain shaped recipes not registering
3f8e4161f PR-1426: Deprecate more unused methods in UnsafeValues
2c9dd879e SPIGOT-7771: Material.getDefaultAttributes always returns an empty map

Spigot Changes:
491f3675 Rebuild patches
0a642bd7 Rebuild patches
8897571b Rebuild patches
cb8cf80c Fix newlines in custom restart message
1aabe506 Rebuild patches
2024-07-06 21:19:14 +02:00

529 lines
20 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 27 May 2019 01:10:06 -0500
Subject: [PATCH] Expose server build information
Co-authored-by: Professor Bloodstone <git@bloodstone.dev>
Co-authored-by: Mark Vainomaa <mikroskeem@mikroskeem.eu>
Co-authored-by: masmc05 <masmc05@gmail.com>
Co-authored-by: Riley Park <rileysebastianpark@gmail.com>
diff --git a/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..a736d7bcdc5861a01b66ba36158db1c716339346
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/util/VersionFetcher.java
@@ -0,0 +1,45 @@
+package com.destroystokyo.paper.util;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+
+public interface VersionFetcher {
+ /**
+ * Amount of time to cache results for in milliseconds
+ * <p>
+ * Negative values will never cache.
+ *
+ * @return cache time
+ */
+ long getCacheTime();
+
+ /**
+ * Gets the version message to cache and show to command senders.
+ *
+ * <p>NOTE: This is run in a new thread separate from that of the command processing thread</p>
+ *
+ * @param serverVersion the current version of the server (will match {@link Bukkit#getVersion()})
+ * @return the message to show when requesting a version
+ */
+ @NotNull
+ Component getVersionMessage(@NotNull String serverVersion);
+
+ class DummyVersionFetcher implements VersionFetcher {
+
+ @Override
+ public long getCacheTime() {
+ return -1;
+ }
+
+ @NotNull
+ @Override
+ public Component getVersionMessage(@NotNull String serverVersion) {
+ Bukkit.getLogger().warning("Version provider has not been set, cannot check for updates!");
+ Bukkit.getLogger().info("Override the default implementation of org.bukkit.UnsafeValues#getVersionFetcher()");
+ new Throwable().printStackTrace();
+ return Component.text("Unable to check for updates. No version provider set.", NamedTextColor.RED);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/ServerBuildInfo.java b/src/main/java/io/papermc/paper/ServerBuildInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..9df9d09aa477d4cd3c496ba0933c816df1ef0964
--- /dev/null
+++ b/src/main/java/io/papermc/paper/ServerBuildInfo.java
@@ -0,0 +1,121 @@
+package io.papermc.paper;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.OptionalInt;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.util.Services;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Information about the current server build.
+ */
+@ApiStatus.NonExtendable
+public interface ServerBuildInfo {
+ /**
+ * The brand id for Paper.
+ */
+ Key BRAND_PAPER_ID = Key.key("papermc", "paper");
+
+ /**
+ * Gets the {@code ServerBuildInfo}.
+ *
+ * @return the {@code ServerBuildInfo}
+ */
+ static @NotNull ServerBuildInfo buildInfo() {
+ //<editor-fold defaultstate="collapsed" desc="Holder">
+ final class Holder {
+ static final Optional<ServerBuildInfo> INSTANCE = Services.service(ServerBuildInfo.class);
+ }
+ //</editor-fold>
+ return Holder.INSTANCE.orElseThrow();
+ }
+
+ /**
+ * Gets the brand id of the server.
+ *
+ * @return the brand id of the server (e.g. "papermc:paper")
+ */
+ @NotNull Key brandId();
+
+ /**
+ * Checks if the current server supports the specified brand.
+ *
+ * @param brandId the brand to check (e.g. "papermc:folia")
+ * @return {@code true} if the server supports the specified brand
+ */
+ @ApiStatus.Experimental
+ boolean isBrandCompatible(final @NotNull Key brandId);
+
+ /**
+ * Gets the brand name of the server.
+ *
+ * @return the brand name of the server (e.g. "Paper")
+ */
+ @NotNull String brandName();
+
+ /**
+ * Gets the Minecraft version id.
+ *
+ * @return the Minecraft version id (e.g. "1.20.4", "1.20.2-pre2", "23w31a")
+ */
+ @NotNull String minecraftVersionId();
+
+ /**
+ * Gets the Minecraft version name.
+ *
+ * @return the Minecraft version name (e.g. "1.20.4", "1.20.2 Pre-release 2", "23w31a")
+ */
+ @NotNull String minecraftVersionName();
+
+ /**
+ * Gets the build number.
+ *
+ * @return the build number
+ */
+ @NotNull OptionalInt buildNumber();
+
+ /**
+ * Gets the build time.
+ *
+ * @return the build time
+ */
+ @NotNull Instant buildTime();
+
+ /**
+ * Gets the git commit branch.
+ *
+ * @return the git commit branch
+ */
+ @NotNull Optional<String> gitBranch();
+
+ /**
+ * Gets the git commit hash.
+ *
+ * @return the git commit hash
+ */
+ @NotNull Optional<String> gitCommit();
+
+ /**
+ * Creates a string representation of the server build information.
+ *
+ * @param representation the type of representation
+ * @return a string
+ */
+ @NotNull String asString(final @NotNull StringRepresentation representation);
+
+ /**
+ * String representation types.
+ */
+ enum StringRepresentation {
+ /**
+ * A simple version string, in format {@code <minecraftVersionId>-<buildNumber>-<gitCommit>}.
+ */
+ VERSION_SIMPLE,
+ /**
+ * A simple version string, in format {@code <minecraftVersionId>-<buildNumber>-<gitBranch>@<gitCommit> (<buildTime>)}.
+ */
+ VERSION_FULL,
+ }
+}
diff --git a/src/main/java/io/papermc/paper/util/JarManifests.java b/src/main/java/io/papermc/paper/util/JarManifests.java
new file mode 100644
index 0000000000000000000000000000000000000000..909617079db61b675cc7b60b44ef96b306076343
--- /dev/null
+++ b/src/main/java/io/papermc/paper/util/JarManifests.java
@@ -0,0 +1,37 @@
+package io.papermc.paper.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.jar.Manifest;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+@ApiStatus.Internal
+public final class JarManifests {
+ private JarManifests() {
+ }
+
+ private static final Map<ClassLoader, Manifest> MANIFESTS = Collections.synchronizedMap(new WeakHashMap<>());
+
+ public static @Nullable Manifest manifest(final @NotNull Class<?> clazz) {
+ return MANIFESTS.computeIfAbsent(clazz.getClassLoader(), classLoader -> {
+ final String classLocation = "/" + clazz.getName().replace(".", "/") + ".class";
+ final URL resource = clazz.getResource(classLocation);
+ if (resource == null) {
+ return null;
+ }
+ final String classFilePath = resource.toString().replace("\\", "/");
+ final String archivePath = classFilePath.substring(0, classFilePath.length() - classLocation.length());
+ try (final InputStream stream = new URL(archivePath + "/META-INF/MANIFEST.MF").openStream()) {
+ return new Manifest(stream);
+ } catch (final IOException ex) {
+ return null;
+ }
+ });
+ }
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 5780003631beae773231afe3afc6b834bfe1f84d..a005d5f8879262c763d8c4fbd09b9a99810d5e8c 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -110,13 +110,26 @@ public final class Bukkit {
}
Bukkit.server = server;
- server.getLogger().info("This server is running " + getName() + " version " + getVersion() + " (Implementing API version " + getBukkitVersion() + ")");
+ // Paper start - add git information
+ server.getLogger().info(getVersionMessage());
+ }
+ /**
+ * Gets message describing the version server is running.
+ *
+ * @return message describing the version server is running
+ */
+ @NotNull
+ public static String getVersionMessage() {
+ final io.papermc.paper.ServerBuildInfo version = io.papermc.paper.ServerBuildInfo.buildInfo();
+ return "This server is running " + getName() + " version " + version.asString(io.papermc.paper.ServerBuildInfo.StringRepresentation.VERSION_FULL) + " (Implementing API version " + getBukkitVersion() + ")";
+ // Paper end
}
/**
* Gets the name of this server implementation.
*
* @return name of this server implementation
+ * @see io.papermc.paper.ServerBuildInfo#brandName()
*/
@NotNull
public static String getName() {
@@ -127,6 +140,7 @@ public final class Bukkit {
* Gets the version string of this server implementation.
*
* @return version of this server implementation
+ * @see io.papermc.paper.ServerBuildInfo
*/
@NotNull
public static String getVersion() {
@@ -143,6 +157,20 @@ public final class Bukkit {
return server.getBukkitVersion();
}
+ // Paper start - expose game version
+ /**
+ * Gets the version of game this server implements
+ *
+ * @return version of game
+ * @see io.papermc.paper.ServerBuildInfo#minecraftVersionId()
+ * @see io.papermc.paper.ServerBuildInfo#minecraftVersionName()
+ */
+ @NotNull
+ public static String getMinecraftVersion() {
+ return server.getMinecraftVersion();
+ }
+ // Paper end
+
/**
* Gets a view of all currently logged in players. This {@linkplain
* Collections#unmodifiableCollection(Collection) view} is a reused
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index ba28d9f3213ca4b5f15178dc637bff37a8896edc..8a07f21eeb04fb54032ce377a1478f60303e1824 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -120,6 +120,16 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
@NotNull
public String getBukkitVersion();
+ // Paper start - expose game version
+ /**
+ * Gets the version of game this server implements
+ *
+ * @return version of game
+ */
+ @NotNull
+ String getMinecraftVersion();
+ // Paper end
+
/**
* Gets a view of all currently logged in players. This {@linkplain
* Collections#unmodifiableCollection(Collection) view} is a reused
diff --git a/src/main/java/org/bukkit/UnsafeValues.java b/src/main/java/org/bukkit/UnsafeValues.java
index 7545e3489474aab3f935095c6057ea2eac1bf84b..6def69b973a76a4cee5aee6431a6538e4c27377d 100644
--- a/src/main/java/org/bukkit/UnsafeValues.java
+++ b/src/main/java/org/bukkit/UnsafeValues.java
@@ -159,5 +159,12 @@ public interface UnsafeValues {
* @return name
*/
String getTimingsServerName();
+
+ /**
+ * Called once by the version command on first use, then cached.
+ */
+ default com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
+ return new com.destroystokyo.paper.util.VersionFetcher.DummyVersionFetcher();
+ }
// Paper end
}
diff --git a/src/main/java/org/bukkit/command/defaults/VersionCommand.java b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
index 263208d3cba36cb80c9ee4e3022ef702ea113df2..e64bb57f74e6d6f78927be228825b3e0bdf41f48 100644
--- a/src/main/java/org/bukkit/command/defaults/VersionCommand.java
+++ b/src/main/java/org/bukkit/command/defaults/VersionCommand.java
@@ -25,8 +25,25 @@ import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.util.StringUtil;
import org.jetbrains.annotations.NotNull;
+// Paper start - version command 2.0
+import com.destroystokyo.paper.util.VersionFetcher;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.format.TextDecoration;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+// Paper end - version command 2.0
public class VersionCommand extends BukkitCommand {
+ private VersionFetcher versionFetcher; // Paper - version command 2.0
+ private VersionFetcher getVersionFetcher() { // lazy load because unsafe isn't available at command registration
+ if (versionFetcher == null) {
+ versionFetcher = Bukkit.getUnsafe().getVersionFetcher();
+ }
+
+ return versionFetcher;
+ }
+
public VersionCommand(@NotNull String name) {
super(name);
@@ -41,7 +58,7 @@ public class VersionCommand extends BukkitCommand {
if (!testPermission(sender)) return true;
if (args.length == 0) {
- sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")");
+ //sender.sendMessage("This server is running " + Bukkit.getName() + " version " + Bukkit.getVersion() + " (Implementing API version " + Bukkit.getBukkitVersion() + ")"); // Paper - moved to setVersionMessage
sendVersion(sender);
} else {
StringBuilder name = new StringBuilder();
@@ -80,8 +97,17 @@ public class VersionCommand extends BukkitCommand {
private void describeToSender(@NotNull Plugin plugin, @NotNull CommandSender sender) {
PluginDescriptionFile desc = plugin.getDescription();
- sender.sendMessage(ChatColor.GREEN + desc.getName() + ChatColor.WHITE + " version " + ChatColor.GREEN + desc.getVersion());
-
+ // Paper start - version command 2.0
+ sender.sendMessage(
+ Component.text()
+ .append(Component.text(desc.getName(), NamedTextColor.GREEN))
+ .append(Component.text(" version "))
+ .append(Component.text(desc.getVersion(), NamedTextColor.GREEN)
+ .hoverEvent(Component.text("Click to copy to clipboard", NamedTextColor.WHITE))
+ .clickEvent(ClickEvent.copyToClipboard(desc.getVersion()))
+ )
+ );
+ // Paper end - version command 2.0
if (desc.getDescription() != null) {
sender.sendMessage(desc.getDescription());
}
@@ -147,14 +173,14 @@ public class VersionCommand extends BukkitCommand {
private final ReentrantLock versionLock = new ReentrantLock();
private boolean hasVersion = false;
- private String versionMessage = null;
+ private Component versionMessage = null; // Paper
private final Set<CommandSender> versionWaiters = new HashSet<CommandSender>();
private boolean versionTaskStarted = false;
private long lastCheck = 0;
private void sendVersion(@NotNull CommandSender sender) {
if (hasVersion) {
- if (System.currentTimeMillis() - lastCheck > 21600000) {
+ if (System.currentTimeMillis() - lastCheck > getVersionFetcher().getCacheTime()) { // Paper - use version supplier
lastCheck = System.currentTimeMillis();
hasVersion = false;
} else {
@@ -169,7 +195,7 @@ public class VersionCommand extends BukkitCommand {
return;
}
versionWaiters.add(sender);
- sender.sendMessage("Checking version, please wait...");
+ sender.sendMessage(Component.text("Checking version, please wait...", NamedTextColor.WHITE, TextDecoration.ITALIC)); // Paper
if (!versionTaskStarted) {
versionTaskStarted = true;
new Thread(new Runnable() {
@@ -187,6 +213,13 @@ public class VersionCommand extends BukkitCommand {
private void obtainVersion() {
String version = Bukkit.getVersion();
+ // Paper start
+ if (version.startsWith("null")) { // running from ide?
+ setVersionMessage(Component.text("Unknown version, custom build?", NamedTextColor.YELLOW));
+ return;
+ }
+ setVersionMessage(getVersionFetcher().getVersionMessage(version));
+ /*
if (version == null) version = "Custom";
String[] parts = version.substring(0, version.indexOf(' ')).split("-");
if (parts.length == 4) {
@@ -216,11 +249,24 @@ public class VersionCommand extends BukkitCommand {
} else {
setVersionMessage("Unknown version, custom build?");
}
+ */
+ // Paper end
}
- private void setVersionMessage(@NotNull String msg) {
+ // Paper start
+ private void setVersionMessage(final @NotNull Component msg) {
lastCheck = System.currentTimeMillis();
- versionMessage = msg;
+ final Component message = Component.textOfChildren(
+ Component.text(Bukkit.getVersionMessage(), NamedTextColor.WHITE),
+ Component.newline(),
+ msg
+ );
+ this.versionMessage = Component.text()
+ .append(message)
+ .hoverEvent(Component.text("Click to copy to clipboard", NamedTextColor.WHITE))
+ .clickEvent(ClickEvent.copyToClipboard(PlainTextComponentSerializer.plainText().serialize(message)))
+ .build();
+ // Paper end
versionLock.lock();
try {
hasVersion = true;
diff --git a/src/test/java/io/papermc/paper/TestServerBuildInfo.java b/src/test/java/io/papermc/paper/TestServerBuildInfo.java
new file mode 100644
index 0000000000000000000000000000000000000000..17be27a869c1047a7a9440fb8f3717260d4abbd0
--- /dev/null
+++ b/src/test/java/io/papermc/paper/TestServerBuildInfo.java
@@ -0,0 +1,59 @@
+package io.papermc.paper;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.OptionalInt;
+import net.kyori.adventure.key.Key;
+import org.jetbrains.annotations.NotNull;
+
+public class TestServerBuildInfo implements ServerBuildInfo {
+ @Override
+ public @NotNull Key brandId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isBrandCompatible(final @NotNull Key brandId) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull String brandName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull String minecraftVersionId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull String minecraftVersionName() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull OptionalInt buildNumber() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull Instant buildTime() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull Optional<String> gitBranch() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull Optional<String> gitCommit() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public @NotNull String asString(final @NotNull StringRepresentation representation) {
+ return "";
+ }
+}
diff --git a/src/test/resources/META-INF/services/io.papermc.paper.ServerBuildInfo b/src/test/resources/META-INF/services/io.papermc.paper.ServerBuildInfo
new file mode 100644
index 0000000000000000000000000000000000000000..64e2f8559b9c5a52e0a3229d3d12f65e9af145b3
--- /dev/null
+++ b/src/test/resources/META-INF/services/io.papermc.paper.ServerBuildInfo
@@ -0,0 +1 @@
+io.papermc.paper.TestServerBuildInfo