PaperMC/Spigot-API-Patches/0175-Add-GS4-Query-event.patch
Zach Brown 70ce6ce831
Move version command update checking to the implementation
This makes it easier for downstream projects (forks) to replace the
version fetching system with their own. It is as simple as implementing
an interface and overriding the default implementation of
org.bukkit.UnsafeValues#getVersionFetcher()

It also makes it easier for us to organize things like the version
history feature.

Lastly I have updated the paper implementation to check against the site
API rather than against jenkins.
2019-05-27 04:13:41 -05:00

426 lines
13 KiB
Diff

From 8af8a8fbf521cac34d701cd5436545c70e88ccaa Mon Sep 17 00:00:00 2001
From: Mark Vainomaa <mikroskeem@mikroskeem.eu>
Date: Sun, 17 Mar 2019 21:46:27 +0200
Subject: [PATCH] Add GS4 Query event
diff --git a/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java b/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java
new file mode 100644
index 00000000..2ead0466
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/event/server/GS4QueryEvent.java
@@ -0,0 +1,411 @@
+package com.destroystokyo.paper.event.server;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This event is fired if server is getting queried over GS4 Query protocol
+ *
+ * Adapted from Velocity's ProxyQueryEvent
+ *
+ * @author Mark Vainomaa
+ */
+public final class GS4QueryEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+
+ private final QueryType queryType;
+ private final InetAddress querierAddress;
+ private QueryResponse response;
+
+ public GS4QueryEvent(@NotNull QueryType queryType, @NotNull InetAddress querierAddress, @NotNull QueryResponse response) {
+ this.queryType = Preconditions.checkNotNull(queryType, "queryType");
+ this.querierAddress = Preconditions.checkNotNull(querierAddress, "querierAddress");
+ this.response = Preconditions.checkNotNull(response, "response");
+ }
+
+ /**
+ * Get query type
+ * @return query type
+ */
+ @NotNull
+ public QueryType getQueryType() {
+ return queryType;
+ }
+
+ /**
+ * Get querier address
+ * @return querier address
+ */
+ @NotNull
+ public InetAddress getQuerierAddress() {
+ return querierAddress;
+ }
+
+ /**
+ * Get query response
+ * @return query response
+ */
+ @NotNull
+ public QueryResponse getResponse() {
+ return response;
+ }
+
+ /**
+ * Set query response
+ * @param response query response
+ */
+ public void setResponse(@NotNull QueryResponse response) {
+ this.response = Preconditions.checkNotNull(response, "response");
+ }
+
+ @Override
+ public String toString() {
+ return "GS4QueryEvent{" +
+ "queryType=" + queryType +
+ ", querierAddress=" + querierAddress +
+ ", response=" + response +
+ '}';
+ }
+
+ @NotNull
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ @NotNull
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ /**
+ * The type of query
+ */
+ public enum QueryType {
+ /**
+ * Basic query asks only a subset of information, such as motd, game type (hardcoded to <pre>MINECRAFT</pre>), map,
+ * current players, max players, server port and server motd
+ */
+ BASIC,
+
+ /**
+ * Full query asks pretty much everything present on this event (only hardcoded values cannot be modified here).
+ */
+ FULL
+ ;
+ }
+
+ public final static class QueryResponse {
+ private final String motd;
+ private final String gameVersion;
+ private final String map;
+ private final int currentPlayers;
+ private final int maxPlayers;
+ private final String hostname;
+ private final int port;
+ private final Collection<String> players;
+ private final String serverVersion;
+ private final Collection<PluginInformation> plugins;
+
+ private QueryResponse(String motd, String gameVersion, String map, int currentPlayers, int maxPlayers, String hostname, int port, Collection<String> players, String serverVersion, Collection<PluginInformation> plugins) {
+ this.motd = motd;
+ this.gameVersion = gameVersion;
+ this.map = map;
+ this.currentPlayers = currentPlayers;
+ this.maxPlayers = maxPlayers;
+ this.hostname = hostname;
+ this.port = port;
+ this.players = players;
+ this.serverVersion = serverVersion;
+ this.plugins = plugins;
+ }
+
+ /**
+ * Get motd which will be used to reply to the query. By default it is {@link org.bukkit.Server#getMotd()}.
+ * @return motd
+ */
+ @NotNull
+ public String getMotd() {
+ return motd;
+ }
+
+ /**
+ * Get game version which will be used to reply to the query. By default supported Minecraft versions range is sent.
+ * @return game version
+ */
+ @NotNull
+ public String getGameVersion() {
+ return gameVersion;
+ }
+
+ /**
+ * Get map name which will be used to reply to the query. By default {@code world} is sent.
+ * @return map name
+ */
+ @NotNull
+ public String getMap() {
+ return map;
+ }
+
+ /**
+ * Get current online player count which will be used to reply to the query.
+ * @return online player count
+ */
+ public int getCurrentPlayers() {
+ return currentPlayers;
+ }
+
+ /**
+ * Get max player count which will be used to reply to the query.
+ * @return max player count
+ */
+ public int getMaxPlayers() {
+ return maxPlayers;
+ }
+
+ /**
+ * Get server (public facing) hostname
+ * @return server hostname
+ */
+ @NotNull
+ public String getHostname() {
+ return hostname;
+ }
+
+ /**
+ * Get server (public facing) port
+ * @return server port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Get collection of players which will be used to reply to the query.
+ * @return collection of players
+ */
+ @NotNull
+ public Collection<String> getPlayers() {
+ return players;
+ }
+
+ /**
+ * Get server software (name and version) which will be used to reply to the query.
+ * @return server software
+ */
+ @NotNull
+ public String getServerVersion() {
+ return serverVersion;
+ }
+
+ /**
+ * Get list of plugins which will be used to reply to the query.
+ * @return collection of plugins
+ */
+ @NotNull
+ public Collection<PluginInformation> getPlugins() {
+ return plugins;
+ }
+
+
+ /**
+ * Creates a new {@link Builder} instance from data represented by this response
+ * @return {@link QueryResponse} builder
+ */
+ @NotNull
+ public Builder toBuilder() {
+ return QueryResponse.builder()
+ .motd(getMotd())
+ .gameVersion(getGameVersion())
+ .map(getMap())
+ .currentPlayers(getCurrentPlayers())
+ .maxPlayers(getMaxPlayers())
+ .hostname(getHostname())
+ .port(getPort())
+ .players(getPlayers())
+ .serverVersion(getServerVersion())
+ .plugins(getPlugins());
+ }
+
+ /**
+ * Creates a new {@link Builder} instance
+ * @return {@link QueryResponse} builder
+ */
+ @NotNull
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A builder for {@link QueryResponse} objects.
+ */
+ public static final class Builder {
+ private String motd;
+ private String gameVersion;
+ private String map;
+ private String hostname;
+ private String serverVersion;
+
+ private int currentPlayers;
+ private int maxPlayers;
+ private int port;
+
+ private List<String> players = new ArrayList<>();
+ private List<PluginInformation> plugins = new ArrayList<>();
+
+ private Builder() {}
+
+ @NotNull
+ public Builder motd(@NotNull String motd) {
+ this.motd = Preconditions.checkNotNull(motd, "motd");
+ return this;
+ }
+
+ @NotNull
+ public Builder gameVersion(@NotNull String gameVersion) {
+ this.gameVersion = Preconditions.checkNotNull(gameVersion, "gameVersion");
+ return this;
+ }
+
+ @NotNull
+ public Builder map(@NotNull String map) {
+ this.map = Preconditions.checkNotNull(map, "map");
+ return this;
+ }
+
+ @NotNull
+ public Builder currentPlayers(int currentPlayers) {
+ Preconditions.checkArgument(currentPlayers >= 0, "currentPlayers cannot be negative");
+ this.currentPlayers = currentPlayers;
+ return this;
+ }
+
+ @NotNull
+ public Builder maxPlayers(int maxPlayers) {
+ Preconditions.checkArgument(maxPlayers >= 0, "maxPlayers cannot be negative");
+ this.maxPlayers = maxPlayers;
+ return this;
+ }
+
+ @NotNull
+ public Builder hostname(@NotNull String hostname) {
+ this.hostname = Preconditions.checkNotNull(hostname, "hostname");
+ return this;
+ }
+
+ @NotNull
+ public Builder port(int port) {
+ Preconditions.checkArgument(port >= 1 && port <= 65535, "port must be between 1-65535");
+ this.port = port;
+ return this;
+ }
+
+ @NotNull
+ public Builder players(@NotNull Collection<String> players) {
+ this.players.addAll(Preconditions.checkNotNull(players, "players"));
+ return this;
+ }
+
+ @NotNull
+ public Builder players(@NotNull String... players) {
+ this.players.addAll(Arrays.asList(Preconditions.checkNotNull(players, "players")));
+ return this;
+ }
+
+ @NotNull
+ public Builder clearPlayers() {
+ this.players.clear();
+ return this;
+ }
+
+ @NotNull
+ public Builder serverVersion(@NotNull String serverVersion) {
+ this.serverVersion = Preconditions.checkNotNull(serverVersion, "serverVersion");
+ return this;
+ }
+
+ @NotNull
+ public Builder plugins(@NotNull Collection<PluginInformation> plugins) {
+ this.plugins.addAll(Preconditions.checkNotNull(plugins, "plugins"));
+ return this;
+ }
+
+ @NotNull
+ public Builder plugins(@NotNull PluginInformation... plugins) {
+ this.plugins.addAll(Arrays.asList(Preconditions.checkNotNull(plugins, "plugins")));
+ return this;
+ }
+
+ @NotNull
+ public Builder clearPlugins() {
+ this.plugins.clear();
+ return this;
+ }
+
+ /**
+ * Builds new {@link QueryResponse} with supplied data
+ * @return response
+ */
+ @NotNull
+ public QueryResponse build() {
+ return new QueryResponse(
+ Preconditions.checkNotNull(motd, "motd"),
+ Preconditions.checkNotNull(gameVersion, "gameVersion"),
+ Preconditions.checkNotNull(map, "map"),
+ currentPlayers,
+ maxPlayers,
+ Preconditions.checkNotNull(hostname, "hostname"),
+ port,
+ ImmutableList.copyOf(players),
+ Preconditions.checkNotNull(serverVersion, "serverVersion"),
+ ImmutableList.copyOf(plugins)
+ );
+ }
+ }
+
+ /**
+ * Plugin information
+ */
+ public static class PluginInformation {
+ private String name;
+ private String version;
+
+ public PluginInformation(@NotNull String name, @NotNull String version) {
+ this.name = Preconditions.checkNotNull(name, "name");
+ this.version = Preconditions.checkNotNull(version, "version");
+ }
+
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ public void setName(@NotNull String name) {
+ this.name = name;
+ }
+
+ public void setVersion(@NotNull String version) {
+ this.version = version;
+ }
+
+ @NotNull
+ public String getVersion() {
+ return version;
+ }
+
+ @NotNull
+ public static PluginInformation of(@NotNull String name, @NotNull String version) {
+ return new PluginInformation(name, version);
+ }
+ }
+ }
+}
--
2.21.0