package com.mojang.realmsclient.dto; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.logging.LogUtils; import com.mojang.realmsclient.util.JsonUtils; import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.Map.Entry; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.Util; import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.multiplayer.ServerData.Type; import org.apache.commons.lang3.builder.EqualsBuilder; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @Environment(EnvType.CLIENT) public class RealmsServer extends ValueObject { private static final Logger LOGGER = LogUtils.getLogger(); private static final int NO_VALUE = -1; public long id; @Nullable public String remoteSubscriptionId; @Nullable public String name; public String motd; public RealmsServer.State state; @Nullable public String owner; public UUID ownerUUID = Util.NIL_UUID; public List players; public Map slots; public boolean expired; public boolean expiredTrial; public int daysLeft; public RealmsServer.WorldType worldType; public boolean isHardcore; public int gameMode; public int activeSlot; @Nullable public String minigameName; public int minigameId; @Nullable public String minigameImage; public long parentRealmId = -1L; @Nullable public String parentWorldName; public String activeVersion = ""; public RealmsServer.Compatibility compatibility = RealmsServer.Compatibility.UNVERIFIABLE; public String getDescription() { return this.motd; } @Nullable public String getName() { return this.name; } @Nullable public String getMinigameName() { return this.minigameName; } public void setName(String name) { this.name = name; } public void setDescription(String motd) { this.motd = motd; } public static RealmsServer parse(JsonObject json) { RealmsServer realmsServer = new RealmsServer(); try { realmsServer.id = JsonUtils.getLongOr("id", json, -1L); realmsServer.remoteSubscriptionId = JsonUtils.getStringOr("remoteSubscriptionId", json, null); realmsServer.name = JsonUtils.getStringOr("name", json, null); realmsServer.motd = JsonUtils.getStringOr("motd", json, ""); realmsServer.state = getState(JsonUtils.getStringOr("state", json, RealmsServer.State.CLOSED.name())); realmsServer.owner = JsonUtils.getStringOr("owner", json, null); if (json.get("players") != null && json.get("players").isJsonArray()) { realmsServer.players = parseInvited(json.get("players").getAsJsonArray()); sortInvited(realmsServer); } else { realmsServer.players = Lists.newArrayList(); } realmsServer.daysLeft = JsonUtils.getIntOr("daysLeft", json, 0); realmsServer.expired = JsonUtils.getBooleanOr("expired", json, false); realmsServer.expiredTrial = JsonUtils.getBooleanOr("expiredTrial", json, false); realmsServer.worldType = getWorldType(JsonUtils.getStringOr("worldType", json, RealmsServer.WorldType.NORMAL.name())); realmsServer.isHardcore = JsonUtils.getBooleanOr("isHardcore", json, false); realmsServer.gameMode = JsonUtils.getIntOr("gameMode", json, -1); realmsServer.ownerUUID = JsonUtils.getUuidOr("ownerUUID", json, Util.NIL_UUID); if (json.get("slots") != null && json.get("slots").isJsonArray()) { realmsServer.slots = parseSlots(json.get("slots").getAsJsonArray()); } else { realmsServer.slots = createEmptySlots(); } realmsServer.minigameName = JsonUtils.getStringOr("minigameName", json, null); realmsServer.activeSlot = JsonUtils.getIntOr("activeSlot", json, -1); realmsServer.minigameId = JsonUtils.getIntOr("minigameId", json, -1); realmsServer.minigameImage = JsonUtils.getStringOr("minigameImage", json, null); realmsServer.parentRealmId = JsonUtils.getLongOr("parentWorldId", json, -1L); realmsServer.parentWorldName = JsonUtils.getStringOr("parentWorldName", json, null); realmsServer.activeVersion = JsonUtils.getStringOr("activeVersion", json, ""); realmsServer.compatibility = getCompatibility(JsonUtils.getStringOr("compatibility", json, RealmsServer.Compatibility.UNVERIFIABLE.name())); } catch (Exception var3) { LOGGER.error("Could not parse McoServer: {}", var3.getMessage()); } return realmsServer; } private static void sortInvited(RealmsServer realmsServer) { realmsServer.players .sort( (playerInfo, playerInfo2) -> ComparisonChain.start() .compareFalseFirst(playerInfo2.getAccepted(), playerInfo.getAccepted()) .compare(playerInfo.getName().toLowerCase(Locale.ROOT), playerInfo2.getName().toLowerCase(Locale.ROOT)) .result() ); } private static List parseInvited(JsonArray jsonArray) { List list = Lists.newArrayList(); for (JsonElement jsonElement : jsonArray) { try { JsonObject jsonObject = jsonElement.getAsJsonObject(); PlayerInfo playerInfo = new PlayerInfo(); playerInfo.setName(JsonUtils.getStringOr("name", jsonObject, null)); playerInfo.setUuid(JsonUtils.getUuidOr("uuid", jsonObject, Util.NIL_UUID)); playerInfo.setOperator(JsonUtils.getBooleanOr("operator", jsonObject, false)); playerInfo.setAccepted(JsonUtils.getBooleanOr("accepted", jsonObject, false)); playerInfo.setOnline(JsonUtils.getBooleanOr("online", jsonObject, false)); list.add(playerInfo); } catch (Exception var6) { } } return list; } private static Map parseSlots(JsonArray jsonArray) { Map map = Maps.newHashMap(); for (JsonElement jsonElement : jsonArray) { try { JsonObject jsonObject = jsonElement.getAsJsonObject(); JsonElement jsonElement2 = JsonParser.parseString(jsonObject.get("options").getAsString()); RealmsSettings realmsSettings = parseSettings(jsonObject.get("settings")); RealmsWorldOptions realmsWorldOptions; if (jsonElement2 == null) { realmsWorldOptions = RealmsWorldOptions.createDefaults(); } else { realmsWorldOptions = RealmsWorldOptions.parse(jsonElement2.getAsJsonObject(), realmsSettings); } int i = JsonUtils.getIntOr("slotId", jsonObject, -1); map.put(i, realmsWorldOptions); } catch (Exception var9) { } } for (int j = 1; j <= 3; j++) { if (!map.containsKey(j)) { map.put(j, RealmsWorldOptions.createEmptyDefaults()); } } return map; } private static RealmsSettings parseSettings(JsonElement json) { boolean bl = false; if (json.isJsonArray()) { for (JsonElement jsonElement : json.getAsJsonArray()) { JsonObject jsonObject = jsonElement.getAsJsonObject(); bl = readBoolean(jsonObject, "hardcore", bl); } } return new RealmsSettings(bl); } private static boolean readBoolean(JsonObject json, String memberName, boolean defaultValue) { String string = JsonUtils.getStringOr("name", json, null); return string != null && string.equals(memberName) ? JsonUtils.getBooleanOr("value", json, defaultValue) : defaultValue; } private static Map createEmptySlots() { Map map = Maps.newHashMap(); map.put(1, RealmsWorldOptions.createEmptyDefaults()); map.put(2, RealmsWorldOptions.createEmptyDefaults()); map.put(3, RealmsWorldOptions.createEmptyDefaults()); return map; } public static RealmsServer parse(String json) { try { return parse(new JsonParser().parse(json).getAsJsonObject()); } catch (Exception var2) { LOGGER.error("Could not parse McoServer: {}", var2.getMessage()); return new RealmsServer(); } } private static RealmsServer.State getState(String name) { try { return RealmsServer.State.valueOf(name); } catch (Exception var2) { return RealmsServer.State.CLOSED; } } private static RealmsServer.WorldType getWorldType(String name) { try { return RealmsServer.WorldType.valueOf(name); } catch (Exception var2) { return RealmsServer.WorldType.NORMAL; } } public static RealmsServer.Compatibility getCompatibility(@Nullable String id) { try { return RealmsServer.Compatibility.valueOf(id); } catch (Exception var2) { return RealmsServer.Compatibility.UNVERIFIABLE; } } public boolean isCompatible() { return this.compatibility.isCompatible(); } public boolean needsUpgrade() { return this.compatibility.needsUpgrade(); } public boolean needsDowngrade() { return this.compatibility.needsDowngrade(); } public int hashCode() { return Objects.hash(new Object[]{this.id, this.name, this.motd, this.state, this.owner, this.expired}); } public boolean equals(Object object) { if (object == null) { return false; } else if (object == this) { return true; } else if (object.getClass() != this.getClass()) { return false; } else { RealmsServer realmsServer = (RealmsServer)object; return new EqualsBuilder() .append(this.id, realmsServer.id) .append(this.name, realmsServer.name) .append(this.motd, realmsServer.motd) .append(this.state, realmsServer.state) .append(this.owner, realmsServer.owner) .append(this.expired, realmsServer.expired) .append(this.worldType, this.worldType) .isEquals(); } } public RealmsServer clone() { RealmsServer realmsServer = new RealmsServer(); realmsServer.id = this.id; realmsServer.remoteSubscriptionId = this.remoteSubscriptionId; realmsServer.name = this.name; realmsServer.motd = this.motd; realmsServer.state = this.state; realmsServer.owner = this.owner; realmsServer.players = this.players; realmsServer.slots = this.cloneSlots(this.slots); realmsServer.expired = this.expired; realmsServer.expiredTrial = this.expiredTrial; realmsServer.daysLeft = this.daysLeft; realmsServer.worldType = this.worldType; realmsServer.isHardcore = this.isHardcore; realmsServer.gameMode = this.gameMode; realmsServer.ownerUUID = this.ownerUUID; realmsServer.minigameName = this.minigameName; realmsServer.activeSlot = this.activeSlot; realmsServer.minigameId = this.minigameId; realmsServer.minigameImage = this.minigameImage; realmsServer.parentWorldName = this.parentWorldName; realmsServer.parentRealmId = this.parentRealmId; realmsServer.activeVersion = this.activeVersion; realmsServer.compatibility = this.compatibility; return realmsServer; } public Map cloneSlots(Map slots) { Map map = Maps.newHashMap(); for (Entry entry : slots.entrySet()) { map.put((Integer)entry.getKey(), ((RealmsWorldOptions)entry.getValue()).clone()); } return map; } public boolean isSnapshotRealm() { return this.parentRealmId != -1L; } public boolean isMinigameActive() { return this.worldType == RealmsServer.WorldType.MINIGAME; } public String getWorldName(int slot) { return this.name == null ? ((RealmsWorldOptions)this.slots.get(slot)).getSlotName(slot) : this.name + " (" + ((RealmsWorldOptions)this.slots.get(slot)).getSlotName(slot) + ")"; } public ServerData toServerData(String ip) { return new ServerData((String)Objects.requireNonNullElse(this.name, "unknown server"), ip, Type.REALM); } @Environment(EnvType.CLIENT) public static enum Compatibility { UNVERIFIABLE, INCOMPATIBLE, RELEASE_TYPE_INCOMPATIBLE, NEEDS_DOWNGRADE, NEEDS_UPGRADE, COMPATIBLE; public boolean isCompatible() { return this == COMPATIBLE; } public boolean needsUpgrade() { return this == NEEDS_UPGRADE; } public boolean needsDowngrade() { return this == NEEDS_DOWNGRADE; } } @Environment(EnvType.CLIENT) public static class McoServerComparator implements Comparator { private final String refOwner; public McoServerComparator(String refOwner) { this.refOwner = refOwner; } public int compare(RealmsServer first, RealmsServer second) { return ComparisonChain.start() .compareTrueFirst(first.isSnapshotRealm(), second.isSnapshotRealm()) .compareTrueFirst(first.state == RealmsServer.State.UNINITIALIZED, second.state == RealmsServer.State.UNINITIALIZED) .compareTrueFirst(first.expiredTrial, second.expiredTrial) .compareTrueFirst(Objects.equals(first.owner, this.refOwner), Objects.equals(second.owner, this.refOwner)) .compareFalseFirst(first.expired, second.expired) .compareTrueFirst(first.state == RealmsServer.State.OPEN, second.state == RealmsServer.State.OPEN) .compare(first.id, second.id) .result(); } } @Environment(EnvType.CLIENT) public static enum State { CLOSED, OPEN, UNINITIALIZED; } @Environment(EnvType.CLIENT) public static enum WorldType { NORMAL, MINIGAME, ADVENTUREMAP, EXPERIENCE, INSPIRATION; } }