mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-16 21:21:22 +01:00
Switch to per-version response data generation and implement NamedAndIdentified in ResponseData
This commit is contained in:
parent
0ac6d1aa37
commit
42e1811b7c
@ -12,8 +12,11 @@ import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.minestom.server.ping.PingResponse.*;
|
||||
|
||||
public class StatusRequestPacket implements ClientPreplayPacket {
|
||||
|
||||
@SuppressWarnings("deprecation") // we need to continue handling the ResponseDataConsumer until it's removed
|
||||
@Override
|
||||
public void process(@NotNull PlayerConnection connection) {
|
||||
ResponseDataConsumer consumer = MinecraftServer.getResponseDataConsumer();
|
||||
@ -27,15 +30,22 @@ public class StatusRequestPacket implements ClientPreplayPacket {
|
||||
responseData.setDescription(Component.text("Minestom Server"));
|
||||
responseData.setFavicon("");
|
||||
|
||||
if (consumer != null)
|
||||
if (consumer != null) {
|
||||
consumer.accept(connection, responseData);
|
||||
}
|
||||
|
||||
// Call event
|
||||
ServerListPingEvent statusRequestEvent = new ServerListPingEvent(responseData, connection);
|
||||
MinecraftServer.getGlobalEventHandler().callCancellableEvent(ServerListPingEvent.class, statusRequestEvent,
|
||||
() -> {
|
||||
ResponsePacket responsePacket = new ResponsePacket();
|
||||
responsePacket.jsonResponse = responseData.build().toString();
|
||||
final ResponsePacket responsePacket = new ResponsePacket();
|
||||
|
||||
// check if we need to use a legacy response
|
||||
if (connection.getProtocolVersion() >= 713) {
|
||||
responsePacket.jsonResponse = FULL_RGB.getResponse(statusRequestEvent.getResponseData()).toString();
|
||||
} else {
|
||||
responsePacket.jsonResponse = NAMED_COLORS.getResponse(statusRequestEvent.getResponseData()).toString();
|
||||
}
|
||||
|
||||
connection.sendPacket(responsePacket);
|
||||
});
|
||||
|
51
src/main/java/net/minestom/server/ping/PingResponse.java
Normal file
51
src/main/java/net/minestom/server/ping/PingResponse.java
Normal file
@ -0,0 +1,51 @@
|
||||
package net.minestom.server.ping;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Separation between the versions of response, used to determine how {@link ResponseData}
|
||||
* is serialized.
|
||||
*
|
||||
* @param <T> the type of the data returned in the response
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
@FunctionalInterface
|
||||
public interface PingResponse<T> {
|
||||
/**
|
||||
* Indicates the client supports full RGB with JSON text formatting.
|
||||
*/
|
||||
PingResponse<JsonObject> FULL_RGB = data -> VanillaPingResponses.getModernPingResponse(data, true);
|
||||
|
||||
/**
|
||||
* Indicates the client doesn't support full RGB but does support JSON text formatting.
|
||||
*/
|
||||
PingResponse<JsonObject> NAMED_COLORS = data -> VanillaPingResponses.getModernPingResponse(data, true);
|
||||
|
||||
/**
|
||||
* Indicates the client is incompatible with the Netty rewrite.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Server_List_Ping#1.6">https://wiki.vg/Server_List_Ping#1.6</a>
|
||||
* @deprecated This is not yet supported in Minestom
|
||||
*/
|
||||
@Deprecated(forRemoval = false)
|
||||
PingResponse<Object> LEGACY_PING = null;
|
||||
|
||||
/**
|
||||
* Indicates the client is on a beta version of Minecraft.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Server_List_Ping#Beta_1.8_to_1.3">https://wiki.vg/Server_List_Ping#Beta_1.8_to_1.3</a>
|
||||
* @deprecated This is not yet supported in Minestom
|
||||
*/
|
||||
@Deprecated(forRemoval = false)
|
||||
PingResponse<Object> LEGACY_PING_BETA = null;
|
||||
|
||||
/**
|
||||
* Creates a response from some data.
|
||||
*
|
||||
* @param data the data
|
||||
* @return the response
|
||||
*/
|
||||
@NotNull T getResponse(@NotNull ResponseData data);
|
||||
}
|
@ -1,24 +1,24 @@
|
||||
package net.minestom.server.ping;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.server.ServerListPingEvent;
|
||||
import net.minestom.server.utils.identity.NamedAndIdentified;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents the data sent to the player when refreshing the server list.
|
||||
*
|
||||
* <p>Edited by listening to the {@link net.minestom.server.event.server.ServerListPingEvent}.
|
||||
* @see ServerListPingEvent
|
||||
*/
|
||||
public class ResponseData {
|
||||
private final List<PingPlayer> pingPlayers;
|
||||
private final List<NamedAndIdentified> entries;
|
||||
private String version;
|
||||
private int protocol;
|
||||
private int maxPlayer;
|
||||
@ -31,7 +31,7 @@ public class ResponseData {
|
||||
* Constructs a new {@link ResponseData}.
|
||||
*/
|
||||
public ResponseData() {
|
||||
this.pingPlayers = new ArrayList<>();
|
||||
this.entries = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,7 +121,9 @@ public class ResponseData {
|
||||
* Adds some players to the response.
|
||||
*
|
||||
* @param players the players
|
||||
* @deprecated See {@link #addEntries(Collection)}}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(Iterable<Player> players) {
|
||||
for (Player player : players) {
|
||||
this.addPlayer(player);
|
||||
@ -132,9 +134,11 @@ public class ResponseData {
|
||||
* Adds a player to the response.
|
||||
*
|
||||
* @param player the player
|
||||
* @deprecated See {@link #addEntry(NamedAndIdentified)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(Player player) {
|
||||
this.addPlayer(player.getUsername(), player.getUuid());
|
||||
this.addEntry(player);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,10 +146,11 @@ public class ResponseData {
|
||||
*
|
||||
* @param name The name of the player.
|
||||
* @param uuid The unique identifier of the player.
|
||||
* @deprecated See {@link #addEntry(NamedAndIdentified)} using {@link NamedAndIdentified#of(String, UUID)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(String name, UUID uuid) {
|
||||
PingPlayer pingPlayer = PingPlayer.of(name, uuid);
|
||||
this.pingPlayers.add(pingPlayer);
|
||||
this.addEntry(NamedAndIdentified.of(name, uuid));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,27 +159,35 @@ public class ResponseData {
|
||||
* {@link UUID#randomUUID()} is used as the player's UUID.
|
||||
*
|
||||
* @param name The name of the player.
|
||||
* @deprecated See {@link #addEntry(NamedAndIdentified)} using {@link NamedAndIdentified#named(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void addPlayer(String name) {
|
||||
PingPlayer pingPlayer = PingPlayer.of(name, UUID.randomUUID());
|
||||
this.pingPlayers.add(pingPlayer);
|
||||
this.addEntry(NamedAndIdentified.named(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all of the ping players from this {@link #pingPlayers}. The {@link #pingPlayers} list
|
||||
* Removes all of the ping players from this {@link #entries}. The {@link #entries} list
|
||||
* will be empty this call returns.
|
||||
*
|
||||
* @deprecated See {@link #clearEntries()}
|
||||
*/
|
||||
@Deprecated
|
||||
public void clearPlayers() {
|
||||
this.pingPlayers.clear();
|
||||
this.clearEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of the response players.
|
||||
*
|
||||
* @return the list of the response players.
|
||||
* @deprecated See {@link #getEntries()}. This return value is now unmodifiable and this operation is incredibly costly.
|
||||
*/
|
||||
@Deprecated(forRemoval = true) // to throw an error for people using it - this method is *horrible*
|
||||
public List<PingPlayer> getPlayers() {
|
||||
return pingPlayers;
|
||||
return this.entries.stream()
|
||||
.map(entry -> PingPlayer.of(PlainComponentSerializer.plain().serialize(entry.getName()), entry.getUuid()))
|
||||
.collect(Collectors.toUnmodifiableList());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,48 +239,68 @@ public class ResponseData {
|
||||
return favicon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry to the response data sample. This can be a player or a custom object.
|
||||
*
|
||||
* @param entry the entry
|
||||
* @see NamedAndIdentified
|
||||
*/
|
||||
public void addEntry(@NotNull NamedAndIdentified entry) {
|
||||
this.entries.add(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a series of entries to the response data sample. These can be players or a custom object.
|
||||
*
|
||||
* @param entries the entries
|
||||
* @see NamedAndIdentified
|
||||
*/
|
||||
public void addEntries(@NotNull NamedAndIdentified... entries) {
|
||||
this.addEntries(Arrays.asList(entries));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a series of entries to the response data sample. These can be players or a custom object.
|
||||
*
|
||||
* @param entries the entries
|
||||
* @see NamedAndIdentified
|
||||
*/
|
||||
public void addEntries(@NotNull Collection<? extends NamedAndIdentified> entries) {
|
||||
this.entries.addAll(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the entries.
|
||||
*/
|
||||
public void clearEntries() {
|
||||
this.entries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a modifiable collection of the current entries.
|
||||
*
|
||||
* @return the entries
|
||||
*/
|
||||
public @NotNull Collection<NamedAndIdentified> getEntries() {
|
||||
return this.entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the response data into a {@link JsonObject}.
|
||||
*
|
||||
* @return The converted response data as a json tree.
|
||||
* @deprecated Use {@link PingResponse#getResponse(ResponseData)}
|
||||
*/
|
||||
@NotNull
|
||||
public JsonObject build() {
|
||||
// version
|
||||
final JsonObject versionObject = new JsonObject();
|
||||
versionObject.addProperty("name", this.version);
|
||||
versionObject.addProperty("protocol", this.protocol);
|
||||
|
||||
// players info
|
||||
final JsonObject playersObject = new JsonObject();
|
||||
playersObject.addProperty("max", this.maxPlayer);
|
||||
playersObject.addProperty("online", this.online);
|
||||
|
||||
// individual players
|
||||
final JsonArray sampleArray = new JsonArray();
|
||||
for (PingPlayer pingPlayer : this.pingPlayers) {
|
||||
JsonObject pingPlayerObject = new JsonObject();
|
||||
pingPlayerObject.addProperty("name", pingPlayer.name);
|
||||
pingPlayerObject.addProperty("id", pingPlayer.uuid.toString());
|
||||
sampleArray.add(pingPlayerObject);
|
||||
}
|
||||
playersObject.add("sample", sampleArray);
|
||||
|
||||
final JsonObject descriptionObject = GsonComponentSerializer.gson().serializer()
|
||||
.toJsonTree(this.description).getAsJsonObject();
|
||||
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.add("version", versionObject);
|
||||
jsonObject.add("players", playersObject);
|
||||
jsonObject.add("description", descriptionObject);
|
||||
jsonObject.addProperty("favicon", this.favicon);
|
||||
return jsonObject;
|
||||
@Deprecated
|
||||
public @NotNull JsonObject build() {
|
||||
return PingResponse.FULL_RGB.getResponse(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a player line in the server list hover.
|
||||
* @deprecated See {@link NamedAndIdentified}
|
||||
*/
|
||||
@Deprecated
|
||||
public static class PingPlayer {
|
||||
|
||||
private static @NotNull PingPlayer of(@NotNull String name, @NotNull UUID uuid) {
|
||||
|
@ -0,0 +1,64 @@
|
||||
package net.minestom.server.ping;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.minestom.server.utils.identity.NamedAndIdentified;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Vanilla ping responses.
|
||||
*/
|
||||
public class VanillaPingResponses {
|
||||
private static final GsonComponentSerializer FULL_RGB = GsonComponentSerializer.gson(),
|
||||
DOWNSAMPLE_RGB = GsonComponentSerializer.colorDownsamplingGson();
|
||||
private static final LegacyComponentSerializer SECTION = LegacyComponentSerializer.legacySection();
|
||||
|
||||
|
||||
private VanillaPingResponses() { }
|
||||
|
||||
/**
|
||||
* Creates a modern ping response for client versions above the Netty rewrite.
|
||||
*
|
||||
* @param data the response data
|
||||
* @param supportsFullRgb if the client supports full RGB
|
||||
* @return the response object
|
||||
*/
|
||||
public static @NotNull JsonObject getModernPingResponse(@NotNull ResponseData data, boolean supportsFullRgb) {
|
||||
// version
|
||||
final JsonObject versionObject = new JsonObject();
|
||||
versionObject.addProperty("name", data.getVersion());
|
||||
versionObject.addProperty("protocol", data.getProtocol());
|
||||
|
||||
// players info
|
||||
final JsonObject playersObject = new JsonObject();
|
||||
playersObject.addProperty("max", data.getMaxPlayer());
|
||||
playersObject.addProperty("online", data.getOnline());
|
||||
|
||||
// individual players
|
||||
final JsonArray sampleArray = new JsonArray();
|
||||
for (NamedAndIdentified entry : data.getEntries()) {
|
||||
JsonObject playerObject = new JsonObject();
|
||||
playerObject.addProperty("name", SECTION.serialize(entry.getName()));
|
||||
playerObject.addProperty("id", entry.getUuid().toString());
|
||||
sampleArray.add(playerObject);
|
||||
}
|
||||
|
||||
playersObject.add("sample", sampleArray);
|
||||
|
||||
final JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.add("version", versionObject);
|
||||
jsonObject.add("players", playersObject);
|
||||
jsonObject.addProperty("favicon", data.getFavicon());
|
||||
|
||||
// description
|
||||
if (supportsFullRgb) {
|
||||
jsonObject.add("description", FULL_RGB.serializeToTree(data.getDescription()));
|
||||
} else {
|
||||
jsonObject.add("description", DOWNSAMPLE_RGB.serializeToTree(data.getDescription()));
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ import net.minestom.server.instance.block.rule.vanilla.RedstonePlacementRule;
|
||||
import net.minestom.server.ping.ResponseData;
|
||||
import net.minestom.server.storage.StorageManager;
|
||||
import net.minestom.server.storage.systems.FileStorageSystem;
|
||||
import net.minestom.server.utils.identity.NamedAndIdentified;
|
||||
import net.minestom.server.utils.time.TimeUnit;
|
||||
import net.minestom.server.utils.time.UpdateOption;
|
||||
|
||||
@ -68,35 +69,27 @@ public class Main {
|
||||
ResponseData responseData = event.getResponseData();
|
||||
responseData.setMaxPlayer(0);
|
||||
responseData.setOnline(MinecraftServer.getConnectionManager().getOnlinePlayers().size());
|
||||
responseData.addPlayer("The first line is separated from the others", UUID.randomUUID());
|
||||
responseData.addPlayer("Could be a name, or a message", UUID.randomUUID());
|
||||
responseData.addPlayer("IP test: " + event.getConnection().getRemoteAddress().toString(), UUID.randomUUID());
|
||||
responseData.addPlayer("Use " + (char)0x00a7 + "7section characters", UUID.randomUUID());
|
||||
responseData.addPlayer((char)0x00a7 + "7" + (char)0x00a7 + "ofor formatting" + (char)0x00a7 + "r: (" + (char)0x00a7 + "6char" + (char)0x00a7 + "r)" + (char)0x00a7 + "90x00a7", UUID.randomUUID());
|
||||
responseData.addEntry(NamedAndIdentified.named("The first line is separated from the others"));
|
||||
responseData.addEntry(NamedAndIdentified.named("Could be a name, or a message"));
|
||||
responseData.addEntry(NamedAndIdentified.named("IP test: " + event.getConnection().getRemoteAddress().toString()));
|
||||
|
||||
responseData.addPlayer("Connection Info:");
|
||||
// components will be converted the legacy section sign format so they are displayed in the client
|
||||
responseData.addEntry(NamedAndIdentified.named(Component.text("You can use").append(Component.text("styling too!", NamedTextColor.RED))));
|
||||
|
||||
responseData.addEntry(NamedAndIdentified.named("Connection Info:"));
|
||||
String ip = event.getConnection().getServerAddress();
|
||||
responseData.addPlayer((char)0x00a7 + "8- " + (char)0x00a7 +"7IP: " + (char)0x00a7 + "e" + (ip != null ? ip : "???"));
|
||||
responseData.addPlayer((char)0x00a7 + "8- " + (char)0x00a7 +"7PORT: " + (char)0x00a7 + "e" + event.getConnection().getServerPort());
|
||||
responseData.addPlayer((char)0x00a7 + "8- " + (char)0x00a7 +"7VERSION: " + (char)0x00a7 + "e" + event.getConnection().getProtocolVersion());
|
||||
|
||||
// Check if client supports RGB color
|
||||
if (event.getConnection().getProtocolVersion() >= 713) { // Snapshot 20w17a
|
||||
responseData.setDescription(Component.text("You can do ")
|
||||
.append(Component.text("RGB", TextColor.color(0x66b3ff)))
|
||||
.append(Component.text(" color here")));
|
||||
} else {
|
||||
responseData.setDescription(Component.text("You can do ")
|
||||
.append(Component.text("RGB", NamedTextColor.nearestTo(TextColor.color(0x66b3ff))))
|
||||
.append(Component.text(" color here,"))
|
||||
.append(Component.newline())
|
||||
.append(Component.text("if you are on 1.16 or up"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
responseData.addEntry(NamedAndIdentified.named(Component.text('-', NamedTextColor.DARK_GRAY)
|
||||
.append(Component.text("IP: ", NamedTextColor.GRAY))
|
||||
.append(Component.text(ip != null ? ip : "???", NamedTextColor.YELLOW))));
|
||||
responseData.addEntry(NamedAndIdentified.named(Component.text('-', NamedTextColor.DARK_GRAY)
|
||||
.append(Component.text("PORT: ", NamedTextColor.GRAY))
|
||||
.append(Component.text(event.getConnection().getServerPort()))));
|
||||
responseData.addEntry(NamedAndIdentified.named(Component.text('-', NamedTextColor.DARK_GRAY)
|
||||
.append(Component.text("VERSION: ", NamedTextColor.GRAY))
|
||||
.append(Component.text(event.getConnection().getProtocolVersion()))));
|
||||
|
||||
// the data will be automatically converted to the correct format on response, so you can do RGB and it'll be downsampled!
|
||||
responseData.setDescription(Component.text("This will be downsampled on older versions!", TextColor.color(0x66b3ff)));
|
||||
});
|
||||
|
||||
PlayerInit.init();
|
||||
|
Loading…
Reference in New Issue
Block a user