Skip to content

Commit 2cdc534

Browse files
committed
feat: add some basic logic
1 parent 8c3b96c commit 2cdc534

File tree

3 files changed

+335
-0
lines changed

3 files changed

+335
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package at.helpch.placeholderapi.expansion.server;
2+
3+
import at.helpch.placeholderapi.expansion.server.util.ServerUtil;
4+
import com.google.common.cache.Cache;
5+
import com.google.common.cache.CacheBuilder;
6+
import me.clip.placeholderapi.expansion.Cacheable;
7+
import me.clip.placeholderapi.expansion.Configurable;
8+
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
9+
import org.bukkit.Bukkit;
10+
import org.bukkit.OfflinePlayer;
11+
import org.bukkit.World;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
import java.util.Map;
16+
import java.util.concurrent.Callable;
17+
import java.util.concurrent.ExecutionException;
18+
import java.util.concurrent.TimeUnit;
19+
import java.util.function.ToIntFunction;
20+
21+
public class ServerExpansion extends PlaceholderExpansion implements Cacheable, Configurable {
22+
23+
private final Cache<String, Integer> cache = CacheBuilder.newBuilder()
24+
.expireAfterWrite(1, TimeUnit.MINUTES)
25+
.build();
26+
27+
private final Runtime runtime = Runtime.getRuntime();
28+
29+
@Override
30+
public @NotNull String getIdentifier() {
31+
return "server";
32+
}
33+
34+
@Override
35+
public @NotNull String getAuthor() {
36+
return "HelpChat";
37+
}
38+
39+
@Override
40+
public @NotNull String getVersion() {
41+
return "3.0.0";
42+
}
43+
44+
@Override
45+
public void clear() {
46+
47+
}
48+
49+
@Override
50+
public Map<String, Object> getDefaults() {
51+
return null;
52+
}
53+
54+
private int sumAllWorlds(@NotNull final ToIntFunction<? super World> function) {
55+
return Bukkit.getWorlds()
56+
.stream()
57+
.mapToInt(function)
58+
.sum();
59+
}
60+
61+
private String getFromCache(@NotNull final String key, @NotNull final Callable<Integer> callable) {
62+
try {
63+
return String.valueOf(cache.get(key, callable));
64+
} catch (ExecutionException e) {
65+
e.printStackTrace();
66+
return "";
67+
}
68+
}
69+
70+
@Override
71+
public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) {
72+
switch (params) {
73+
case "variant":
74+
return ServerUtil.getVariant();
75+
case "total_chunks":
76+
return getFromCache("chunks", () -> sumAllWorlds(world -> world.getLoadedChunks().length));
77+
case "total_living_entities":
78+
return getFromCache("livingEntities", () -> sumAllWorlds(world -> world.getLivingEntities().size()));
79+
case "total_entities":
80+
return getFromCache("totalEntities", () -> sumAllWorlds(world -> world.getEntities().size()));
81+
}
82+
83+
return null;
84+
}
85+
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package at.helpch.placeholderapi.expansion.server.util;
2+
3+
import me.clip.placeholderapi.PlaceholderAPIPlugin;
4+
import org.bukkit.Bukkit;
5+
6+
import java.lang.reflect.Field;
7+
import java.lang.reflect.InvocationTargetException;
8+
import java.util.Map;
9+
import java.util.TreeMap;
10+
import java.util.logging.Level;
11+
12+
public final class ServerUtil {
13+
14+
private static final Map<String, String> variants = new TreeMap<>();
15+
private static final String variant;
16+
17+
// TPS stuff
18+
private static final Object craftServer;
19+
private static final Field tpsField;
20+
private static boolean hasTpsMethod; // Paper and its forks have Bukkit#getTps
21+
22+
static {
23+
variants.put("net.pl3x.purpur.PurpurConfig", "Purpur");
24+
variants.put("gg.airplane.AirplaneConfig", "Airplane");
25+
variants.put("com.tuinity.tuinity.config.TuinityConfig", "Tuinity");
26+
variants.put("io.papermc.paper.configuration.ConfigurationLoaders", "Paper"); // New config location for Paper 1.19+
27+
variants.put("com.destroystokyo.paper.PaperConfig", "Paper"); // Still supported by Paper, but deprecated.
28+
variants.put("org.spigotmc.SpigotConfig", "Spigot");
29+
30+
variant = findVariant();
31+
craftServer = getCraftServer();
32+
tpsField = getTpsHandler();
33+
}
34+
35+
private static String findVariant() {
36+
try {
37+
for (final Map.Entry<String, String> entry : variants.entrySet()) {
38+
Class.forName(entry.getKey());
39+
return entry.getValue();
40+
}
41+
} catch (ClassNotFoundException ignored) { }
42+
43+
// TODO attempt to get the variant from one extra place before returning Unknown
44+
// Example 1: https://github.com/PaperMC/Paper/blob/f7717c3712265fd480d6ff0ad808c430b9972004/patches/server/0027-Show-Paper-in-client-crashes-server-lists-and-Mojang.patch#L17
45+
// Example 2: https://github.com/PurpurMC/Purpur/blob/678eafef721b1a3d819fbaff7dd980bc9f4ee785/patches/server/0003-Rebrand.patch#L208
46+
return "Unknown";
47+
}
48+
49+
private static Object getCraftServer() {
50+
try {
51+
Class<?> clas;
52+
53+
if (VersionHelper.OVER_1_17) {
54+
clas = Class.forName("net.minecraft.server.MinecraftServer");
55+
} else {
56+
clas = Class.forName(String.format("net.minecraft.server.%s.MinecraftServer", VersionHelper.NMS_VERSION));
57+
}
58+
59+
return clas.getMethod("getServer").invoke(null);
60+
} catch (ClassNotFoundException e) {
61+
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, "[server] Could not find class MinecraftServer", e);
62+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
63+
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.SEVERE, "[server] Could not invoke method MinecraftServer#getServer", e);
64+
}
65+
66+
return null;
67+
}
68+
69+
private static Field getTpsHandler() {
70+
try {
71+
Bukkit.class.getMethod("getTPS");
72+
hasTpsMethod = true;
73+
} catch (NoSuchMethodException ignored) { }
74+
75+
if (craftServer == null) {
76+
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, "Could not get field 'recentTps' from class MinecraftServer because variable 'craftServer' is null");
77+
return null;
78+
}
79+
80+
try {
81+
return craftServer.getClass().getField("recentTps");
82+
} catch (NoSuchFieldException e) {
83+
PlaceholderAPIPlugin.getInstance().getLogger().log(Level.WARNING, "Could not find field 'recentTps' in class " + craftServer.getClass().getName(), e);
84+
return null;
85+
}
86+
}
87+
88+
public static String getVariant() {
89+
return variant;
90+
}
91+
92+
public static double[] getTps() {
93+
if (hasTpsMethod) {
94+
return Bukkit.getTPS();
95+
}
96+
97+
if (craftServer == null || tpsField == null) {
98+
return new double[]{0, 0, 0};
99+
}
100+
101+
try {
102+
return (double[]) tpsField.get(craftServer);
103+
} catch (IllegalAccessException e) {
104+
return new double[]{0, 0, 0};
105+
}
106+
}
107+
108+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* MIT License
3+
* <p>
4+
* Copyright (c) 2021 TriumphTeam
5+
* <p>
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
* <p>
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
* <p>
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package at.helpch.placeholderapi.expansion.server.util;
25+
26+
import com.google.common.primitives.Ints;
27+
import org.bukkit.Bukkit;
28+
import org.bukkit.OfflinePlayer;
29+
import org.jetbrains.annotations.NotNull;
30+
31+
import java.util.regex.Matcher;
32+
import java.util.regex.Pattern;
33+
34+
/**
35+
* Class for detecting server version for legacy support :(
36+
*/
37+
public final class VersionHelper {
38+
39+
public static final String NMS_VERSION = getNmsVersion();
40+
41+
// Unbreakable change
42+
private static final int V1_11 = 1110;
43+
// Material and components on items change
44+
private static final int V1_13 = 1130;
45+
// PDC and customModelData
46+
private static final int V1_14 = 1140;
47+
// Paper adventure changes
48+
private static final int V1_16_5 = 1165;
49+
// SkullMeta#setOwningPlayer was added
50+
private static final int V1_12_1 = 1121;
51+
private static final int V1_17 = 1_17_0;
52+
53+
private static final int CURRENT_VERSION = getCurrentVersion();
54+
55+
private static final boolean IS_PAPER = checkPaper();
56+
57+
/**
58+
* Checks if the version supports Components or not
59+
* Spigot always false
60+
*/
61+
public static final boolean IS_COMPONENT_LEGACY = CURRENT_VERSION < V1_16_5;
62+
63+
/**
64+
* Checks if the version is lower than 1.13 due to the item changes
65+
*/
66+
public static final boolean IS_ITEM_LEGACY = CURRENT_VERSION < V1_13;
67+
68+
/**
69+
* Checks if the version supports the {@link org.bukkit.inventory.meta.ItemMeta#setUnbreakable(boolean)} method
70+
*/
71+
public static final boolean IS_UNBREAKABLE_LEGACY = CURRENT_VERSION < V1_11;
72+
73+
/**
74+
* Checks if the version supports {@link org.bukkit.persistence.PersistentDataContainer}
75+
*/
76+
public static final boolean IS_PDC_VERSION = CURRENT_VERSION >= V1_14;
77+
78+
/**
79+
* Checks if the version doesn't have {@link org.bukkit.inventory.meta.SkullMeta#setOwningPlayer(OfflinePlayer)} and
80+
* {@link org.bukkit.inventory.meta.SkullMeta#setOwner(String)} should be used instead
81+
*/
82+
public static final boolean IS_SKULL_OWNER_LEGACY = CURRENT_VERSION < V1_12_1;
83+
84+
/**
85+
* Checks if the version has {@link org.bukkit.inventory.meta.ItemMeta#setCustomModelData(Integer)}
86+
*/
87+
public static final boolean IS_CUSTOM_MODEL_DATA = CURRENT_VERSION >= V1_14;
88+
89+
public static final boolean OVER_1_17 = CURRENT_VERSION >= V1_17;
90+
91+
/**
92+
* Check if the server has access to the Paper API
93+
* Taken from <a href="https://github.com/PaperMC/PaperLib">PaperLib</a>
94+
*
95+
* @return True if on Paper server (or forks), false anything else
96+
*/
97+
private static boolean checkPaper() {
98+
try {
99+
Class.forName("com.destroystokyo.paper.PaperConfig");
100+
return true;
101+
} catch (ClassNotFoundException ignored) {
102+
return false;
103+
}
104+
}
105+
106+
/**
107+
* Gets the current server version
108+
*
109+
* @return A protocol like number representing the version, for example 1.16.5 - 1165
110+
*/
111+
private static int getCurrentVersion() {
112+
// No need to cache since will only run once
113+
final Matcher matcher = Pattern.compile("(?<version>\\d+\\.\\d+)(?<patch>\\.\\d+)?").matcher(Bukkit.getBukkitVersion());
114+
115+
final StringBuilder stringBuilder = new StringBuilder();
116+
if (matcher.find()) {
117+
stringBuilder.append(matcher.group("version").replace(".", ""));
118+
final String patch = matcher.group("patch");
119+
if (patch == null) stringBuilder.append("0");
120+
else stringBuilder.append(patch.replace(".", ""));
121+
}
122+
123+
//noinspection UnstableApiUsage
124+
final Integer version = Ints.tryParse(stringBuilder.toString());
125+
126+
// Should never fail
127+
if (version == null) throw new IllegalArgumentException("Could not retrieve server version!");
128+
129+
return version;
130+
}
131+
132+
private static String getNmsVersion() {
133+
final String version = Bukkit.getServer().getClass().getPackage().getName();
134+
return version.substring(version.lastIndexOf('.') + 1);
135+
}
136+
137+
public static Class<?> craftClass(@NotNull final String name) throws ClassNotFoundException {
138+
return Class.forName("org.bukkit.craftbukkit." + NMS_VERSION + "." + name);
139+
}
140+
141+
}

0 commit comments

Comments
 (0)