219 lines
4.9 KiB
C#
219 lines
4.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Steamworks.Data;
|
|
|
|
namespace Steamworks.ServerList
|
|
{
|
|
public abstract class Base : IDisposable
|
|
{
|
|
|
|
#region ISteamMatchmakingServers
|
|
internal static ISteamMatchmakingServers? Internal => SteamMatchmakingServers.Internal;
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Which app we're querying. Defaults to the current app.
|
|
/// </summary>
|
|
public AppId AppId { get; set; }
|
|
|
|
/// <summary>
|
|
/// When a new server is added, this function will get called
|
|
/// </summary>
|
|
public Action? OnChanges;
|
|
|
|
/// <summary>
|
|
/// Called for every responsive server
|
|
/// </summary>
|
|
public Action<ServerInfo>? OnResponsiveServer;
|
|
|
|
/// <summary>
|
|
/// Called for every unresponsive server
|
|
/// </summary>
|
|
public Action<ServerInfo>? OnUnresponsiveServer;
|
|
|
|
/// <summary>
|
|
/// A list of servers that responded. If you're only interested in servers that responded since you
|
|
/// last updated, then simply clear this list.
|
|
/// </summary>
|
|
public List<ServerInfo> Responsive = new List<ServerInfo>();
|
|
|
|
/// <summary>
|
|
/// A list of servers that were in the master list but didn't respond.
|
|
/// </summary>
|
|
public List<ServerInfo> Unresponsive = new List<ServerInfo>();
|
|
|
|
public List<ServerInfo> Unqueried = new List<ServerInfo>();
|
|
|
|
public Base()
|
|
{
|
|
AppId = SteamClient.AppId; // Default AppId is this
|
|
}
|
|
|
|
/// <summary>
|
|
/// Query the server list. Task result will be true when finished
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public virtual async Task<bool> RunQueryAsync( float timeoutSeconds = 10 )
|
|
{
|
|
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
|
|
|
Reset();
|
|
LaunchQuery();
|
|
|
|
var thisRequest = request;
|
|
|
|
while ( true )
|
|
{
|
|
await Task.Delay( 33 );
|
|
|
|
//
|
|
// The request has been cancelled or changed in some way
|
|
//
|
|
if ( request.Value == IntPtr.Zero || thisRequest.Value != request.Value || !IsRefreshing )
|
|
return false;
|
|
|
|
if ( !SteamClient.IsValid )
|
|
return false;
|
|
|
|
var r = Responsive.Count;
|
|
|
|
UpdatePending();
|
|
UpdateResponsive();
|
|
|
|
if ( r != Responsive.Count )
|
|
{
|
|
InvokeChanges();
|
|
}
|
|
|
|
if ( stopwatch.Elapsed.TotalSeconds > timeoutSeconds )
|
|
break;
|
|
}
|
|
|
|
MovePendingToUnresponsive();
|
|
InvokeChanges();
|
|
|
|
return true;
|
|
}
|
|
|
|
public virtual void Cancel() => Internal?.CancelQuery( request );
|
|
|
|
// Overrides
|
|
internal abstract void LaunchQuery();
|
|
|
|
internal HServerListRequest request;
|
|
|
|
#region Filters
|
|
|
|
internal List<MatchMakingKeyValuePair> filters = new List<MatchMakingKeyValuePair>();
|
|
internal virtual MatchMakingKeyValuePair[] GetFilters() => filters.ToArray();
|
|
|
|
public void AddFilter( string key, string value )
|
|
{
|
|
filters.Add( new MatchMakingKeyValuePair { Key = key, Value = value } );
|
|
}
|
|
|
|
#endregion
|
|
|
|
internal int Count => Internal?.GetServerCount( request ) ?? 0;
|
|
internal bool IsRefreshing => request.Value != IntPtr.Zero && Internal != null && Internal.IsRefreshing( request );
|
|
internal List<int> watchList = new List<int>();
|
|
internal int LastCount = 0;
|
|
|
|
void Reset()
|
|
{
|
|
ReleaseQuery();
|
|
LastCount = 0;
|
|
watchList.Clear();
|
|
}
|
|
|
|
void ReleaseQuery()
|
|
{
|
|
if ( request.Value != IntPtr.Zero )
|
|
{
|
|
Cancel();
|
|
Internal?.ReleaseRequest( request );
|
|
request = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
public virtual void Dispose()
|
|
{
|
|
ReleaseQuery();
|
|
}
|
|
|
|
internal void InvokeChanges()
|
|
{
|
|
OnChanges?.Invoke();
|
|
}
|
|
|
|
void UpdatePending()
|
|
{
|
|
var count = Count;
|
|
if ( count == LastCount ) return;
|
|
|
|
for ( int i = LastCount; i < count; i++ )
|
|
{
|
|
watchList.Add( i );
|
|
}
|
|
|
|
LastCount = count;
|
|
}
|
|
|
|
public void UpdateResponsive()
|
|
{
|
|
watchList.RemoveAll( x =>
|
|
{
|
|
if (Internal is null) { return true; }
|
|
|
|
// First check if the server has responded without allocating server info
|
|
bool hasResponded = Internal.HasServerResponded( request, x );
|
|
if ( hasResponded )
|
|
{
|
|
// Now get all server info
|
|
var info = Internal.GetServerDetails( request, x );
|
|
if ( info.HadSuccessfulResponse )
|
|
{
|
|
OnServer( ServerInfo.From( info ), info.HadSuccessfulResponse );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
} );
|
|
}
|
|
|
|
void MovePendingToUnresponsive()
|
|
{
|
|
watchList.RemoveAll( x =>
|
|
{
|
|
if (Internal is null) { return true; }
|
|
|
|
var details = Internal.GetServerDetails( request, x );
|
|
var info = ServerInfo.From( details );
|
|
info.Ping = int.MaxValue;
|
|
Unqueried.Add( info );
|
|
return true;
|
|
} );
|
|
}
|
|
|
|
private void OnServer( ServerInfo serverInfo, bool responded )
|
|
{
|
|
if ( responded )
|
|
{
|
|
Responsive.Add( serverInfo );
|
|
OnResponsiveServer?.Invoke( serverInfo );
|
|
}
|
|
else
|
|
{
|
|
Unresponsive.Add( serverInfo );
|
|
OnUnresponsiveServer?.Invoke(serverInfo);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|