Core/PartyLobby.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace Prototype;

public readonly record struct PartyLobbyView( string Id, string Name, string OwnerId, bool Locked, bool Started, IReadOnlyList<string> Members )
{
    public const int Capacity = 4;
    public static bool HasConnectionSlot(IEnumerable<string> connectionIds, string incomingId) =>
        connectionIds.Where(id => id != incomingId).Distinct().Count() < Capacity;
	public string ServerAddress { get; init; }
    public int? AdvertisedMembers { get; init; }
    public int MemberCount => AdvertisedMembers ?? Members?.Count ?? 0;
	public bool IsFull => MemberCount >= Capacity;

	public static string Encode( PartyLobbyView room, string passwordToken )
	{
		var safeName = (room.Name ?? "Prototype").Replace( "\u001f", " " ).Trim();
		return string.Join( "\u001f", safeName, room.OwnerId ?? "", room.Locked ? "1" : "0", room.Started ? "1" : "0", passwordToken ?? "", string.Join( ",", room.Members ?? Array.Empty<string>() ) );
	}

	public static bool TryDecode( string id, string encoded, out PartyLobbyView room, out string passwordToken )
	{
		room = default; passwordToken = "";
		var fields = (encoded ?? "").Split( '\u001f' );
		if ( fields.Length < 6 ) return false;
		var members = fields[5].Split( ',', StringSplitOptions.RemoveEmptyEntries ).Distinct().ToArray();
		room = new PartyLobbyView( id, fields[0], fields[1], fields[2] == "1", fields[3] == "1", members );
		passwordToken = fields[4];
		return true;
	}
}