Merge pull request #820 from AlexProgrammerDE/better-status-packet

Allow using arbitrary json as the status packet
This commit is contained in:
basaigh 2024-06-16 02:42:25 +01:00 committed by GitHub
commit 66b326f8a4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 26 deletions

View file

@ -103,7 +103,7 @@ public class ClientListener extends SessionAdapter {
} }
} else if (protocol.getState() == ProtocolState.STATUS) { } else if (protocol.getState() == ProtocolState.STATUS) {
if (packet instanceof ClientboundStatusResponsePacket statusResponsePacket) { if (packet instanceof ClientboundStatusResponsePacket statusResponsePacket) {
ServerStatusInfo info = statusResponsePacket.getInfo(); ServerStatusInfo info = statusResponsePacket.parseInfo();
ServerInfoHandler handler = session.getFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY); ServerInfoHandler handler = session.getFlag(MinecraftConstants.SERVER_INFO_HANDLER_KEY);
if (handler != null) { if (handler != null) {
handler.handle(session, info); handler.handle(session, info);

View file

@ -31,13 +31,25 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
// vanilla behavior falls back to false if the field was not sent // vanilla behavior falls back to false if the field was not sent
private static final boolean ENFORCES_SECURE_CHAT_DEFAULT = false; private static final boolean ENFORCES_SECURE_CHAT_DEFAULT = false;
private final @NonNull ServerStatusInfo info; private final @NonNull JsonObject data;
public ClientboundStatusResponsePacket(@NonNull ServerStatusInfo info) {
this(toJson(info));
}
public ClientboundStatusResponsePacket(ByteBuf in, MinecraftCodecHelper helper) { public ClientboundStatusResponsePacket(ByteBuf in, MinecraftCodecHelper helper) {
JsonObject obj = new Gson().fromJson(helper.readString(in), JsonObject.class); data = new Gson().fromJson(helper.readString(in), JsonObject.class);
JsonElement desc = obj.get("description"); }
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) {
helper.writeString(out, data.toString());
}
public ServerStatusInfo parseInfo() {
JsonElement desc = data.get("description");
Component description = DefaultComponentSerializer.get().serializer().fromJson(desc, Component.class); Component description = DefaultComponentSerializer.get().serializer().fromJson(desc, Component.class);
JsonObject plrs = obj.get("players").getAsJsonObject(); JsonObject plrs = data.get("players").getAsJsonObject();
List<GameProfile> profiles = new ArrayList<>(); List<GameProfile> profiles = new ArrayList<>();
if (plrs.has("sample")) { if (plrs.has("sample")) {
JsonArray prof = plrs.get("sample").getAsJsonArray(); JsonArray prof = plrs.get("sample").getAsJsonArray();
@ -50,32 +62,36 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
} }
PlayerInfo players = new PlayerInfo(plrs.get("max").getAsInt(), plrs.get("online").getAsInt(), profiles); PlayerInfo players = new PlayerInfo(plrs.get("max").getAsInt(), plrs.get("online").getAsInt(), profiles);
JsonObject ver = obj.get("version").getAsJsonObject(); JsonObject ver = data.get("version").getAsJsonObject();
VersionInfo version = new VersionInfo(ver.get("name").getAsString(), ver.get("protocol").getAsInt()); VersionInfo version = new VersionInfo(ver.get("name").getAsString(), ver.get("protocol").getAsInt());
byte[] icon = null; byte[] icon = null;
if (obj.has("favicon")) { if (data.has("favicon")) {
icon = this.stringToIcon(obj.get("favicon").getAsString()); icon = stringToIcon(data.get("favicon").getAsString());
} }
boolean enforcesSecureChat = ENFORCES_SECURE_CHAT_DEFAULT; boolean enforcesSecureChat = ENFORCES_SECURE_CHAT_DEFAULT;
if (obj.has("enforcesSecureChat")) { if (data.has("enforcesSecureChat")) {
enforcesSecureChat = obj.get("enforcesSecureChat").getAsBoolean(); enforcesSecureChat = data.get("enforcesSecureChat").getAsBoolean();
}
this.info = new ServerStatusInfo(version, players, description, icon, enforcesSecureChat);
} }
@Override return new ServerStatusInfo(version, players, description, icon, enforcesSecureChat);
public void serialize(ByteBuf out, MinecraftCodecHelper helper) { }
public ClientboundStatusResponsePacket withInfo(@NonNull ServerStatusInfo info) {
return withData(toJson(info));
}
private static JsonObject toJson(ServerStatusInfo info) {
JsonObject obj = new JsonObject(); JsonObject obj = new JsonObject();
JsonObject ver = new JsonObject(); JsonObject ver = new JsonObject();
ver.addProperty("name", this.info.getVersionInfo().getVersionName()); ver.addProperty("name", info.getVersionInfo().getVersionName());
ver.addProperty("protocol", this.info.getVersionInfo().getProtocolVersion()); ver.addProperty("protocol", info.getVersionInfo().getProtocolVersion());
JsonObject plrs = new JsonObject(); JsonObject plrs = new JsonObject();
plrs.addProperty("max", this.info.getPlayerInfo().getMaxPlayers()); plrs.addProperty("max", info.getPlayerInfo().getMaxPlayers());
plrs.addProperty("online", this.info.getPlayerInfo().getOnlinePlayers()); plrs.addProperty("online", info.getPlayerInfo().getOnlinePlayers());
if (!this.info.getPlayerInfo().getPlayers().isEmpty()) { if (!info.getPlayerInfo().getPlayers().isEmpty()) {
JsonArray array = new JsonArray(); JsonArray array = new JsonArray();
for (GameProfile profile : this.info.getPlayerInfo().getPlayers()) { for (GameProfile profile : info.getPlayerInfo().getPlayers()) {
JsonObject o = new JsonObject(); JsonObject o = new JsonObject();
o.addProperty("name", profile.getName()); o.addProperty("name", profile.getName());
o.addProperty("id", profile.getIdAsString()); o.addProperty("id", profile.getIdAsString());
@ -85,18 +101,18 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
plrs.add("sample", array); plrs.add("sample", array);
} }
obj.add("description", new Gson().fromJson(DefaultComponentSerializer.get().serialize(this.info.getDescription()), JsonElement.class)); obj.add("description", new Gson().fromJson(DefaultComponentSerializer.get().serialize(info.getDescription()), JsonElement.class));
obj.add("players", plrs); obj.add("players", plrs);
obj.add("version", ver); obj.add("version", ver);
if (this.info.getIconPng() != null) { if (info.getIconPng() != null) {
obj.addProperty("favicon", this.iconToString(this.info.getIconPng())); obj.addProperty("favicon", iconToString(info.getIconPng()));
} }
obj.addProperty("enforcesSecureChat", this.info.isEnforcesSecureChat()); obj.addProperty("enforcesSecureChat", info.isEnforcesSecureChat());
helper.writeString(out, obj.toString()); return obj;
} }
private byte[] stringToIcon(String str) { public static byte[] stringToIcon(String str) {
if (str.startsWith("data:image/png;base64,")) { if (str.startsWith("data:image/png;base64,")) {
str = str.substring("data:image/png;base64,".length()); str = str.substring("data:image/png;base64,".length());
} }
@ -104,7 +120,7 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
return Base64.decode(str.getBytes(StandardCharsets.UTF_8)); return Base64.decode(str.getBytes(StandardCharsets.UTF_8));
} }
private String iconToString(byte[] icon) { public static String iconToString(byte[] icon) {
return "data:image/png;base64," + new String(Base64.encode(icon), StandardCharsets.UTF_8); return "data:image/png;base64," + new String(Base64.encode(icon), StandardCharsets.UTF_8);
} }
} }