aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0035-Implement-Paper-VersionChecker.patch
blob: 484e0425d5509595a77573c56f6c8a2c4d11f675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Zach Brown <zach@zachbr.io>
Date: Mon, 27 May 2019 03:40:05 -0500
Subject: [PATCH] Implement Paper VersionChecker


diff --git a/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..22a55be34fde453fedd987173d95b8b347a03588
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperVersionFetcher.java
@@ -0,0 +1,129 @@
+package com.destroystokyo.paper;
+
+import com.destroystokyo.paper.util.VersionFetcher;
+import com.google.common.base.Charsets;
+import com.google.common.io.Resources;
+import com.google.gson.*;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.event.ClickEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.stream.StreamSupport;
+
+public class PaperVersionFetcher implements VersionFetcher {
+    private static final java.util.regex.Pattern VER_PATTERN = java.util.regex.Pattern.compile("^([0-9\\.]*)\\-.*R"); // R is an anchor, will always give '-R' at end
+    private static final String GITHUB_BRANCH_NAME = "master";
+    private static final String DOWNLOAD_PAGE = "https://papermc.io/downloads/paper";
+    private static @Nullable String mcVer;
+
+    @Override
+    public long getCacheTime() {
+        return 720000;
+    }
+
+    @Nonnull
+    @Override
+    public Component getVersionMessage(@Nonnull String serverVersion) {
+        String[] parts = serverVersion.substring("git-Paper-".length()).split("[-\\s]");
+        return getUpdateStatusMessage("PaperMC/Paper", GITHUB_BRANCH_NAME, parts[0]);
+    }
+
+    private static @Nullable String getMinecraftVersion() {
+        if (mcVer == null) {
+            java.util.regex.Matcher matcher = VER_PATTERN.matcher(org.bukkit.Bukkit.getBukkitVersion());
+            if (matcher.find()) {
+                String result = matcher.group();
+                mcVer = result.substring(0, result.length() - 2); // strip 'R' anchor and trailing '-'
+            } else {
+                org.bukkit.Bukkit.getLogger().warning("Unable to match version to pattern! Report to PaperMC!");
+                org.bukkit.Bukkit.getLogger().warning("Pattern: " + VER_PATTERN.toString());
+                org.bukkit.Bukkit.getLogger().warning("Version: " + org.bukkit.Bukkit.getBukkitVersion());
+            }
+        }
+
+        return mcVer;
+    }
+
+    private static Component getUpdateStatusMessage(@Nonnull String repo, @Nonnull String branch, @Nonnull String versionInfo) {
+        int distance;
+        try {
+            int jenkinsBuild = Integer.parseInt(versionInfo);
+            distance = fetchDistanceFromSiteApi(jenkinsBuild, getMinecraftVersion());
+        } catch (NumberFormatException ignored) {
+            versionInfo = versionInfo.replace("\"", "");
+            distance = fetchDistanceFromGitHub(repo, branch, versionInfo);
+        }
+
+        switch (distance) {
+            case -1:
+                return Component.text("Error obtaining version information", NamedTextColor.YELLOW);
+            case 0:
+                return Component.text("You are running the latest version", NamedTextColor.GREEN);
+            case -2:
+                return Component.text("Unknown version", NamedTextColor.YELLOW);
+            default:
+                return Component.text("You are " + distance + " version(s) behind", NamedTextColor.YELLOW)
+                        .append(Component.newline())
+                        .append(Component.text("Download the new version at: ")
+                                .append(Component.text(DOWNLOAD_PAGE, NamedTextColor.GOLD)
+                                        .hoverEvent(Component.text("Click to open", NamedTextColor.WHITE))
+                                        .clickEvent(ClickEvent.openUrl(DOWNLOAD_PAGE))));
+        }
+    }
+
+    private static int fetchDistanceFromSiteApi(int jenkinsBuild, @Nullable String siteApiVersion) {
+        if (siteApiVersion == null) { return -1; }
+        try {
+            try (BufferedReader reader = Resources.asCharSource(
+                new URL("https://api.papermc.io/v2/projects/paper/versions/" + siteApiVersion),
+                Charsets.UTF_8
+            ).openBufferedStream()) {
+                JsonObject json = new Gson().fromJson(reader, JsonObject.class);
+                JsonArray builds = json.getAsJsonArray("builds");
+                int latest = StreamSupport.stream(builds.spliterator(), false)
+                    .mapToInt(e -> e.getAsInt())
+                    .max()
+                    .getAsInt();
+                return latest - jenkinsBuild;
+            } catch (JsonSyntaxException ex) {
+                ex.printStackTrace();
+                return -1;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            return -1;
+        }
+    }
+
+    // Contributed by Techcable <Techcable@outlook.com> in GH-65
+    private static int fetchDistanceFromGitHub(@Nonnull String repo, @Nonnull String branch, @Nonnull String hash) {
+        try {
+            HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/" + branch + "..." + hash).openConnection();
+            connection.connect();
+            if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))) {
+                JsonObject obj = new Gson().fromJson(reader, JsonObject.class);
+                String status = obj.get("status").getAsString();
+                switch (status) {
+                    case "identical":
+                        return 0;
+                    case "behind":
+                        return obj.get("behind_by").getAsInt();
+                    default:
+                        return -1;
+                }
+            } catch (JsonSyntaxException | NumberFormatException e) {
+                e.printStackTrace();
+                return -1;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            return -1;
+        }
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
index 9ae089c6dbb1cc919b005fd9672a690f89ca2b42..2f21520106aa848a9108937474570a8c4e723cfb 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java
@@ -474,6 +474,11 @@ public final class CraftMagicNumbers implements UnsafeValues {
     public String getTimingsServerName() {
         return io.papermc.paper.configuration.GlobalConfiguration.get().timings.serverName;
     }
+
+    @Override
+    public com.destroystokyo.paper.util.VersionFetcher getVersionFetcher() {
+        return new com.destroystokyo.paper.PaperVersionFetcher();
+    }
     // Paper end
 
     /**