MultiplayerSession.Lobbies.cs
using Sandbox;
using Sandbox.Network;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace PaintballBaddies;

public sealed partial class MultiplayerSession
{
    public bool Searching {get;private set;}
    public int LobbyRevision {get;private set;}
    private CancellationTokenSource lobbySearch;

    public static bool CompatibleLobby(LobbyInformation lobby)=>lobby.Get("pb_protocol","")==Protocol;
    public static string LobbyLabel(LobbyInformation lobby)
        =>$"{lobby.Name} / {lobby.Members}/{lobby.MaxMembers} PLAYERS"
        +(lobby.IsFull ? " / FULL" : !CompatibleLobby(lobby) ? " / DIFFERENT BUILD" : "");

    public async void RefreshLobbies()=>await RefreshLobbiesAsync();
    public async Task RefreshLobbiesAsync()
    {
        if(Searching || Online)return;
        Searching=true;LobbyRevision++;Notice="Looking for games...";
        using var search=new CancellationTokenSource(TimeSpan.FromSeconds(12));
        lobbySearch=search;
        try
        {
            var found=await Networking.QueryLobbies(search.Token);
            if(!IsValid || search.IsCancellationRequested)return;
            Lobbies=found.Where(x=>!x.IsHidden).OrderBy(x=>x.IsFull || !CompatibleLobby(x)).ThenByDescending(x=>x.Members).ToList();
            Notice=Lobbies.Count==0 ? "No games found. Host a public match so others can join you."
                : "Choose a game. Full lobbies and different builds cannot be joined.";
        }
        catch(OperationCanceledException){if(IsValid)Notice="Search timed out. Check Steam and try again.";}
        catch(Exception){if(IsValid)Notice="Could not refresh games. Check Steam and try again.";}
        finally {if(IsValid){Searching=false;LobbyRevision++;}lobbySearch=null;}
    }
    public void JoinLobby(LobbyInformation lobby)
    {
        if(lobby.IsFull){Notice="That lobby is full. Refresh to find another game.";return;}
        if(!CompatibleLobby(lobby)){Notice="That lobby uses a different build. Update the game and refresh.";return;}
        Join(lobby.LobbyId);
    }
    protected override void OnDestroy()=>lobbySearch?.Cancel();
}