mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-31 21:48:08 +01:00
Make legacy server pings use the ServerListPingEvent
This commit is contained in:
parent
42e1811b7c
commit
a15e3aef44
@ -1,29 +1,59 @@
|
||||
package net.minestom.server.event.server;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.event.CancellableEvent;
|
||||
import net.minestom.server.event.Event;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.ping.ResponseData;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.ping.ServerListPingVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Called when a {@link PlayerConnection} sends a status packet,
|
||||
* usually to display information on the server list.
|
||||
*/
|
||||
public class ServerListPingEvent extends Event implements CancellableEvent {
|
||||
private boolean cancelled = false;
|
||||
|
||||
private final ResponseData responseData;
|
||||
private final PlayerConnection connection;
|
||||
private final ServerListPingVersion version;
|
||||
|
||||
private boolean cancelled = false;
|
||||
private ResponseData responseData;
|
||||
|
||||
public ServerListPingEvent(ResponseData responseData, PlayerConnection connection) {
|
||||
this.responseData = responseData;
|
||||
this.connection = connection;
|
||||
/**
|
||||
* Creates a new server list ping event with no player connection.
|
||||
*
|
||||
* @param version the ping version to respond with
|
||||
*/
|
||||
public ServerListPingEvent(@NotNull ServerListPingVersion version) {
|
||||
this(null, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* ResponseData being returned.
|
||||
* Creates a new server list ping event.
|
||||
*
|
||||
* @param connection the player connection, if the ping version is modern
|
||||
* @param version the ping version to respond with
|
||||
*/
|
||||
public ServerListPingEvent(@Nullable PlayerConnection connection, @NotNull ServerListPingVersion version) {
|
||||
//noinspection deprecation we need to continue doing this until the consumer is removed
|
||||
ResponseDataConsumer consumer = MinecraftServer.getResponseDataConsumer();
|
||||
this.responseData = new ResponseData();
|
||||
|
||||
if (consumer != null) {
|
||||
consumer.accept(connection, responseData);
|
||||
}
|
||||
|
||||
this.connection = connection;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response data that is sent to the client.
|
||||
* This is mutable and can be modified to change what is returned.
|
||||
*
|
||||
* @return the response data being returned
|
||||
*/
|
||||
@ -32,16 +62,34 @@ public class ServerListPingEvent extends Event implements CancellableEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* PlayerConnection of received packet.
|
||||
* Sets the response data, overwriting the exiting data.
|
||||
*
|
||||
* @param responseData the new data
|
||||
*/
|
||||
public void setResponseData(@NotNull ResponseData responseData) {
|
||||
this.responseData = Objects.requireNonNull(responseData);
|
||||
}
|
||||
|
||||
/**
|
||||
* PlayerConnection of received packet.
|
||||
* Note that the player has not joined the server at this time.
|
||||
* This will be null for legacy server list pings.
|
||||
*
|
||||
* @return the playerConnection.
|
||||
*/
|
||||
public @NotNull PlayerConnection getConnection() {
|
||||
public @Nullable PlayerConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ping version that the client is pinging with.
|
||||
*
|
||||
* @return the ping version
|
||||
* @see ServerListPingVersion
|
||||
*/
|
||||
public @NotNull ServerListPingVersion getPingVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
@ -57,5 +105,4 @@ public class ServerListPingEvent extends Event implements CancellableEvent {
|
||||
public void setCancelled(boolean cancel) {
|
||||
this.cancelled = cancel;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,22 +6,23 @@ import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.event.server.ServerListPingEvent;
|
||||
import net.minestom.server.ping.ServerListPingVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
// Copied from original minecraft :(
|
||||
public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
private ByteBuf buf;
|
||||
|
||||
@Override
|
||||
public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object object) {
|
||||
ByteBuf buf = (ByteBuf) object;
|
||||
final ByteBuf buf = (ByteBuf) object;
|
||||
|
||||
if (this.buf != null) {
|
||||
try {
|
||||
readLegacy1_6(ctx, buf);
|
||||
handle1_6(ctx, buf);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
@ -38,19 +39,17 @@ public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
switch (length) {
|
||||
case 0:
|
||||
this.writeResponse(ctx, this.createResponse(formatResponse(-2)));
|
||||
if (trySendResponse(ServerListPingVersion.LEGACY, ctx)) return;
|
||||
break;
|
||||
case 1:
|
||||
if (buf.readUnsignedByte() != 1) {
|
||||
return;
|
||||
}
|
||||
if (buf.readUnsignedByte() != 1) return;
|
||||
|
||||
this.writeResponse(ctx, this.createResponse(formatResponse(-1)));
|
||||
if (trySendResponse(ServerListPingVersion.LEGACY_VERSIONED, ctx)) return;
|
||||
break;
|
||||
default:
|
||||
if (buf.readUnsignedByte() != 0x01 || buf.readUnsignedByte() != 0xFA) return;
|
||||
|
||||
readLegacy1_6(ctx, buf);
|
||||
handle1_6(ctx, buf);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -66,19 +65,7 @@ public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private static String readLegacyString(ByteBuf buf) {
|
||||
int size = buf.readShort() * Character.BYTES;
|
||||
if (!buf.isReadable(size)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE);
|
||||
buf.skipBytes(size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) {
|
||||
private void handle1_6(ChannelHandlerContext ctx, ByteBuf part) {
|
||||
ByteBuf buf = this.buf;
|
||||
|
||||
if (buf == null) {
|
||||
@ -127,27 +114,7 @@ public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
this.buf = null;
|
||||
|
||||
this.writeResponse(ctx, this.createResponse(formatResponse(protocolVersion)));
|
||||
}
|
||||
|
||||
private String formatResponse(int playerProtocol) {
|
||||
final String motd = MinecraftServer.getBrandName();
|
||||
final String version = MinecraftServer.VERSION_NAME;
|
||||
final int online = MinecraftServer.getConnectionManager().getOnlinePlayers().size();
|
||||
final int max = 0;
|
||||
final int protocol = MinecraftServer.PROTOCOL_VERSION;
|
||||
|
||||
if (playerProtocol == -2) {
|
||||
return String.format(
|
||||
"%s\u00a7%d\u00a7%d",
|
||||
motd, online, max
|
||||
);
|
||||
}
|
||||
|
||||
return String.format(
|
||||
"\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d",
|
||||
protocol, version, motd, online, max
|
||||
);
|
||||
trySendResponse(ServerListPingVersion.LEGACY_VERSIONED, ctx);
|
||||
}
|
||||
|
||||
private void removeHandler(ChannelHandlerContext ctx) {
|
||||
@ -167,22 +134,51 @@ public class LegacyPingHandler extends ChannelInboundHandlerAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
private void writeResponse(ChannelHandlerContext ctx, ByteBuf buf) {
|
||||
ctx.pipeline().firstContext().writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE);
|
||||
/**
|
||||
* Calls a {@link ServerListPingEvent} and sends the response, if the event was not cancelled.
|
||||
*
|
||||
* @param version the version
|
||||
* @param ctx the context
|
||||
* @return {@code true} if the response was cancelled, {@code false} otherwise
|
||||
*/
|
||||
private static boolean trySendResponse(@NotNull ServerListPingVersion version, @NotNull ChannelHandlerContext ctx) {
|
||||
final ServerListPingEvent event = new ServerListPingEvent(version);
|
||||
MinecraftServer.getGlobalEventHandler().callEvent(ServerListPingEvent.class, event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return true;
|
||||
} else {
|
||||
// get the response string
|
||||
String s = version.getPingResponse(event.getResponseData());
|
||||
|
||||
// create the buffer
|
||||
ByteBuf response = Unpooled.buffer();
|
||||
response.writeByte(255);
|
||||
|
||||
final char[] chars = s.toCharArray();
|
||||
|
||||
response.writeShort(chars.length);
|
||||
|
||||
for (char c : chars) {
|
||||
response.writeChar(c);
|
||||
}
|
||||
|
||||
// write the buffer
|
||||
ctx.pipeline().firstContext().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuf createResponse(String s) {
|
||||
ByteBuf response = Unpooled.buffer();
|
||||
response.writeByte(255);
|
||||
|
||||
final char[] chars = s.toCharArray();
|
||||
|
||||
response.writeShort(chars.length);
|
||||
|
||||
for (char c : chars) {
|
||||
response.writeChar(c);
|
||||
private static String readLegacyString(ByteBuf buf) {
|
||||
int size = buf.readShort() * Character.BYTES;
|
||||
if (!buf.isReadable(size)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return response;
|
||||
final String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE);
|
||||
buf.skipBytes(size);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1,55 +1,27 @@
|
||||
package net.minestom.server.network.packet.client.status;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.event.server.ServerListPingEvent;
|
||||
import net.minestom.server.network.packet.client.ClientPreplayPacket;
|
||||
import net.minestom.server.network.packet.server.handshake.ResponsePacket;
|
||||
import net.minestom.server.network.player.PlayerConnection;
|
||||
import net.minestom.server.ping.ResponseData;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.ping.ServerListPingVersion;
|
||||
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();
|
||||
ResponseData responseData = new ResponseData();
|
||||
|
||||
// Fill default params
|
||||
responseData.setVersion(MinecraftServer.VERSION_NAME);
|
||||
responseData.setProtocol(MinecraftServer.PROTOCOL_VERSION);
|
||||
responseData.setMaxPlayer(0);
|
||||
responseData.setOnline(0);
|
||||
responseData.setDescription(Component.text("Minestom Server"));
|
||||
responseData.setFavicon("");
|
||||
|
||||
if (consumer != null) {
|
||||
consumer.accept(connection, responseData);
|
||||
}
|
||||
|
||||
// Call event
|
||||
ServerListPingEvent statusRequestEvent = new ServerListPingEvent(responseData, connection);
|
||||
MinecraftServer.getGlobalEventHandler().callCancellableEvent(ServerListPingEvent.class, statusRequestEvent,
|
||||
() -> {
|
||||
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);
|
||||
});
|
||||
final ServerListPingVersion pingVersion = ServerListPingVersion.fromModernProtocolVersion(connection.getProtocolVersion());
|
||||
final ServerListPingEvent statusRequestEvent = new ServerListPingEvent(connection, pingVersion);
|
||||
MinecraftServer.getGlobalEventHandler().callCancellableEvent(ServerListPingEvent.class, statusRequestEvent, () -> {
|
||||
final ResponsePacket responsePacket = new ResponsePacket();
|
||||
responsePacket.jsonResponse = pingVersion.getPingResponse(statusRequestEvent.getResponseData());
|
||||
|
||||
connection.sendPacket(responsePacket);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,51 +0,0 @@
|
||||
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);
|
||||
}
|
@ -4,6 +4,7 @@ import com.google.gson.JsonObject;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.Player;
|
||||
import net.minestom.server.event.server.ServerListPingEvent;
|
||||
import net.minestom.server.utils.identity.NamedAndIdentified;
|
||||
@ -18,13 +19,15 @@ import java.util.stream.Collectors;
|
||||
* @see ServerListPingEvent
|
||||
*/
|
||||
public class ResponseData {
|
||||
private static final Component DEFAULT_DESCRIPTION = Component.text("Minestom Server");
|
||||
|
||||
private final List<NamedAndIdentified> entries;
|
||||
|
||||
private String version;
|
||||
private int protocol;
|
||||
private int maxPlayer;
|
||||
private int online;
|
||||
private Component description;
|
||||
|
||||
private String favicon;
|
||||
|
||||
/**
|
||||
@ -32,6 +35,12 @@ public class ResponseData {
|
||||
*/
|
||||
public ResponseData() {
|
||||
this.entries = new ArrayList<>();
|
||||
this.version = MinecraftServer.VERSION_NAME;
|
||||
this.protocol = MinecraftServer.PROTOCOL_VERSION;
|
||||
this.online = MinecraftServer.getConnectionManager().getOnlinePlayers().size();
|
||||
this.maxPlayer = this.online + 1;
|
||||
this.description = DEFAULT_DESCRIPTION;
|
||||
this.favicon = "";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -289,11 +298,11 @@ public class ResponseData {
|
||||
* Converts the response data into a {@link JsonObject}.
|
||||
*
|
||||
* @return The converted response data as a json tree.
|
||||
* @deprecated Use {@link PingResponse#getResponse(ResponseData)}
|
||||
* @deprecated Use {@link ServerListPingVersion#getPingResponse(ResponseData)}
|
||||
*/
|
||||
@Deprecated
|
||||
public @NotNull JsonObject build() {
|
||||
return PingResponse.FULL_RGB.getResponse(this);
|
||||
return ServerListPingVersion.getModernPingResponse(this, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,135 @@
|
||||
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.MinecraftServer;
|
||||
import net.minestom.server.utils.identity.NamedAndIdentified;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* An enum containing the different types of server list ping responses.
|
||||
*
|
||||
* @see <a href="https://wiki.vg/Server_List_Ping">https://wiki.vg/Server_List_Ping</a>
|
||||
*/
|
||||
public enum ServerListPingVersion {
|
||||
/**
|
||||
* The client is on version 1.16 or higher and supports full RGB with JSON text formatting.
|
||||
*/
|
||||
MODERN_FULL_RGB(data -> getModernPingResponse(data, true).toString()),
|
||||
|
||||
/**
|
||||
* The client is on version 1.7 or higher and doesn't support full RGB but does support JSON text formatting.
|
||||
*/
|
||||
MODERN_NAMED_COLORS(data -> getModernPingResponse(data, false).toString()),
|
||||
|
||||
/**
|
||||
* The client is on version 1.6 and supports a description, the player count and the version information.
|
||||
*/
|
||||
LEGACY_VERSIONED(data -> getLegacyPingResponse(data, true)),
|
||||
|
||||
/**
|
||||
* The client is on version 1.5 or lower and supports a description and the player count.
|
||||
*/
|
||||
LEGACY(data -> getLegacyPingResponse(data, false));
|
||||
|
||||
private final Function<ResponseData, String> pingResponseCreator;
|
||||
|
||||
ServerListPingVersion(@NotNull Function<ResponseData, String> pingResponseCreator) {
|
||||
this.pingResponseCreator = pingResponseCreator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the ping response for this version.
|
||||
*
|
||||
* @param responseData the response data
|
||||
* @return the response
|
||||
*/
|
||||
public @NotNull String getPingResponse(@NotNull ResponseData responseData) {
|
||||
return this.pingResponseCreator.apply(responseData);
|
||||
}
|
||||
|
||||
private static final GsonComponentSerializer FULL_RGB = GsonComponentSerializer.gson(),
|
||||
NAMED_RGB = GsonComponentSerializer.colorDownsamplingGson();
|
||||
private static final LegacyComponentSerializer SECTION = LegacyComponentSerializer.legacySection();
|
||||
|
||||
/**
|
||||
* Creates a legacy ping response for client versions below the Netty rewrite (1.6-).
|
||||
*
|
||||
* @param data the response data
|
||||
* @param supportsVersions if the client supports recieving the versions of the server
|
||||
* @return the response
|
||||
*/
|
||||
public static @NotNull String getLegacyPingResponse(@NotNull ResponseData data, boolean supportsVersions) {
|
||||
final String motd = SECTION.serialize(data.getDescription());
|
||||
|
||||
if (supportsVersions) {
|
||||
return String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d",
|
||||
data.getProtocol(), data.getVersion(), motd, data.getOnline(), data.getMaxPlayer());
|
||||
} else {
|
||||
return String.format("%s\u00a7%d\u00a7%d", motd, data.getOnline(), data.getMaxPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a modern ping response for client versions above the Netty rewrite (1.7+).
|
||||
*
|
||||
* @param data the response data
|
||||
* @param supportsFullRgb if the client supports full RGB
|
||||
* @return the response
|
||||
*/
|
||||
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", NAMED_RGB.serializeToTree(data.getDescription()));
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server list ping version from the protocol version.
|
||||
* This only works for modern ping responses since the Netty rewrite.
|
||||
*
|
||||
* @param version the protocol version
|
||||
* @return the corresponding server list ping version
|
||||
*/
|
||||
public static @NotNull ServerListPingVersion fromModernProtocolVersion(int version) {
|
||||
if (version >= 713) {
|
||||
return MODERN_FULL_RGB;
|
||||
} else {
|
||||
return MODERN_NAMED_COLORS;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
@ -5,8 +5,10 @@ import demo.blocks.CustomBlockSample;
|
||||
import demo.blocks.UpdatableBlockDemo;
|
||||
import demo.commands.*;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.command.CommandManager;
|
||||
import net.minestom.server.event.server.ServerListPingEvent;
|
||||
@ -67,29 +69,32 @@ public class Main {
|
||||
|
||||
MinecraftServer.getGlobalEventHandler().addEventCallback(ServerListPingEvent.class, event -> {
|
||||
ResponseData responseData = event.getResponseData();
|
||||
responseData.setMaxPlayer(0);
|
||||
responseData.setOnline(MinecraftServer.getConnectionManager().getOnlinePlayers().size());
|
||||
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()));
|
||||
|
||||
// on modern versions, you can obtain the player connection directly from the event
|
||||
if (event.getConnection() != null) {
|
||||
responseData.addEntry(NamedAndIdentified.named("IP test: " + event.getConnection().getRemoteAddress().toString()));
|
||||
|
||||
responseData.addEntry(NamedAndIdentified.named("Connection Info:"));
|
||||
String ip = event.getConnection().getServerAddress();
|
||||
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()))));
|
||||
}
|
||||
|
||||
// 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.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()))));
|
||||
responseData.addEntry(NamedAndIdentified.named(Component.text("You can use ").append(Component.text("styling too!", NamedTextColor.RED, TextDecoration.BOLD))));
|
||||
|
||||
// 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)));
|
||||
// on legacy versions, colors will be converted to the section format so it'll work there too
|
||||
responseData.setDescription(Component.text("This is a Minestom Server", TextColor.color(0x66b3ff)));
|
||||
});
|
||||
|
||||
PlayerInit.init();
|
||||
|
Loading…
Reference in New Issue
Block a user