r2mods/ilspy_dump/ror2_csproj/RoR2.RemoteGameBrowser/SteamworksServerRemoteGameP...

463 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Facepunch.Steamworks;
using RoR2.Networking;
namespace RoR2.RemoteGameBrowser;
public class SteamworksServerRemoteGameProvider : BaseAsyncRemoteGameProvider
{
public new struct SearchFilters : IEquatable<SearchFilters>
{
public bool allowDedicatedServers;
public bool allowListenServers;
public bool mustHavePlayers;
public bool mustNotBeFull;
public bool allowInProgressGames;
public bool allowMismatchedMods;
public string requiredTags;
public string forbiddenTags;
public bool Equals(SearchFilters other)
{
if (allowDedicatedServers == other.allowDedicatedServers && allowListenServers == other.allowListenServers && mustHavePlayers == other.mustHavePlayers && mustNotBeFull == other.mustNotBeFull && allowInProgressGames == other.allowInProgressGames && allowMismatchedMods == other.allowMismatchedMods && string.Equals(requiredTags, other.requiredTags))
{
return string.Equals(forbiddenTags, other.forbiddenTags);
}
return false;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is SearchFilters other)
{
return Equals(other);
}
return false;
}
public override int GetHashCode()
{
return (((((((((((((allowDedicatedServers.GetHashCode() * 397) ^ allowListenServers.GetHashCode()) * 397) ^ mustHavePlayers.GetHashCode()) * 397) ^ mustNotBeFull.GetHashCode()) * 397) ^ allowInProgressGames.GetHashCode()) * 397) ^ allowMismatchedMods.GetHashCode()) * 397) ^ ((requiredTags != null) ? requiredTags.GetHashCode() : 0)) * 397) ^ ((forbiddenTags != null) ? forbiddenTags.GetHashCode() : 0);
}
}
public enum Mode
{
Internet,
Favorites,
History,
Local,
Friends
}
private struct FilterElement
{
public string key;
public string value;
public int operandCount;
public bool isOperator;
}
private Mode mode;
private ServerList.Filter filter;
private ServerList.Request currentRequest;
public new bool refreshOnFiltersChanged;
private static readonly int k_cbMaxGameServerGameData = 2048;
private static readonly int k_cbMaxGameServerGameDescription = 64;
private static readonly int k_cbMaxGameServerGameDir = 32;
private static readonly int k_cbMaxGameServerMapName = 32;
private static readonly int k_cbMaxGameServerName = 64;
private static readonly int k_cbMaxGameServerTags = 128;
private new SearchFilters searchFilters;
public SteamworksServerRemoteGameProvider(Mode mode)
{
this.mode = mode;
filter = new ServerList.Filter();
}
public new SearchFilters GetSearchFilters()
{
return searchFilters;
}
public void SetSearchFilters(SearchFilters newSearchFilters)
{
if (!searchFilters.Equals(newSearchFilters))
{
searchFilters = newSearchFilters;
BuildFilter();
if (refreshOnFiltersChanged)
{
RequestRefresh();
}
SetDirty();
}
}
private void BuildFilter()
{
filter.Clear();
FilterElement[] elements = new FilterElement[64];
int elementCount = 0;
Stack<(int, int)> operatorIndicesAndCounts = new Stack<(int, int)>();
int currentOperandCount = 0;
PushOperation("and");
AddOperand("appid", Client.Instance.AppId.ToString());
if (searchFilters.allowDedicatedServers != searchFilters.allowListenServers)
{
if (searchFilters.allowListenServers)
{
PushOperation("nor");
AddOperand("dedicated", "");
PopOperation();
}
else
{
AddOperand("dedicated", "");
}
}
if (searchFilters.mustHavePlayers)
{
AddOperand("hasplayers", "");
}
if (searchFilters.mustNotBeFull)
{
AddOperand("notfull", "");
}
if (!searchFilters.allowInProgressGames)
{
AddOperand("map", "lobby");
}
if (!string.IsNullOrEmpty(searchFilters.requiredTags))
{
AddOperand("gametagsand", searchFilters.requiredTags);
}
if (!string.IsNullOrEmpty(searchFilters.forbiddenTags))
{
AddOperand("gametagsnor", searchFilters.forbiddenTags);
}
AddOperand("gamedataand", ServerManagerBase<SteamworksServerManager>.GetVersionGameDataString());
if (!searchFilters.allowMismatchedMods)
{
AddOperand("gamedataand", NetworkModCompatibilityHelper.steamworksGameserverGameDataValue);
}
PopOperation();
for (int j = 0; j < elementCount; j++)
{
ref FilterElement reference = ref elements[j];
filter.Add(reference.key, reference.isOperator ? reference.operandCount.ToString() : reference.value);
}
void AddOperand(string key, string value)
{
int num2 = currentOperandCount + 1;
currentOperandCount = num2;
GetElement(elementCount++) = new FilterElement
{
key = key,
value = value,
isOperator = false
};
}
ref FilterElement GetElement(int i)
{
if (elements.Length < i)
{
Array.Resize(ref elements, elements.Length * 2);
}
return ref elements[i];
}
void PopOperation()
{
int num = currentOperandCount;
int i2;
(i2, currentOperandCount) = operatorIndicesAndCounts.Pop();
currentOperandCount += num;
GetElement(i2).operandCount = num;
}
void PushOperation(string operationCode)
{
int num3 = currentOperandCount + 1;
currentOperandCount = num3;
int num4 = elementCount++;
GetElement(num4) = new FilterElement
{
key = operationCode,
isOperator = true
};
operatorIndicesAndCounts.Push((num4, currentOperandCount));
currentOperandCount = 0;
}
}
public override bool RequestRefresh()
{
CancelCurrentRequest();
currentRequest = CreateRequest(mode, filter);
currentRequest.OnUpdate = OnCurrentRequestUpdated;
currentRequest.OnFinished = OnCurrentRequestFinished;
currentRequest.OnServerResponded = OnCurrentRequestServerResponded;
return true;
}
private static Func<ServerList.Filter, ServerList.Request> GetRequestMethod(Mode mode)
{
ServerList serverList = Client.Instance.ServerList;
return mode switch
{
Mode.Internet => serverList.Internet,
Mode.Favorites => serverList.Favourites,
Mode.History => serverList.History,
Mode.Local => serverList.Local,
Mode.Friends => serverList.Friends,
_ => throw new ArgumentOutOfRangeException(),
};
}
private static ServerList.Request CreateRequest(Mode mode, ServerList.Filter filter)
{
return GetRequestMethod(mode)(filter);
}
private void OnCurrentRequestServerResponded(ServerList.Server server)
{
SetDirty();
}
private void OnCurrentRequestFinished()
{
SetDirty();
}
private void OnCurrentRequestUpdated()
{
SetDirty();
}
private void CancelCurrentRequest()
{
if (currentRequest != null)
{
currentRequest.OnFinished = null;
currentRequest.OnServerResponded = null;
currentRequest.OnUpdate = null;
currentRequest.Dispose();
currentRequest = null;
}
}
private static RemoteGameInfo[] GenerateRemoteGameInfoCache(ServerList.Server[] knownServers, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
RemoteGameInfo[] array = new RemoteGameInfo[knownServers.Length];
for (int i = 0; i < knownServers.Length; i++)
{
cancellationToken.ThrowIfCancellationRequested();
ServerList.Server serverInfo = knownServers[i];
GenerateRemoteGameInfo(ref array[i], serverInfo);
}
return array;
}
protected override Task<RemoteGameInfo[]> CreateTask(CancellationToken cancellationToken)
{
if (currentRequest == null)
{
return new Task<RemoteGameInfo[]>(() => Array.Empty<RemoteGameInfo>());
}
ServerList.Server[] servers = currentRequest.Responded.ToArray();
return new Task<RemoteGameInfo[]>(() => GenerateRemoteGameInfoCache(servers, cancellationToken), cancellationToken);
}
public override void Dispose()
{
CancelCurrentRequest();
base.Dispose();
}
public override bool IsBusy()
{
if (!base.IsBusy())
{
return !(currentRequest?.Finished ?? true);
}
return true;
}
private static void GenerateRemoteGameInfo(ref RemoteGameInfo remoteGameInfo, ServerList.Server serverInfo)
{
remoteGameInfo.name = serverInfo.Name;
remoteGameInfo.serverAddress = new AddressPortPair(serverInfo.Address, (ushort)serverInfo.ConnectionPort);
remoteGameInfo.serverName = serverInfo.Name;
remoteGameInfo.serverId = serverInfo.SteamId;
remoteGameInfo.hasPassword = serverInfo.Passworded;
remoteGameInfo.serverPlayerCount = serverInfo.Players;
remoteGameInfo.serverMaxPlayers = serverInfo.MaxPlayers;
remoteGameInfo.ping = serverInfo.Ping;
remoteGameInfo.SetTags(serverInfo.Tags);
remoteGameInfo.isFavorite = serverInfo.Favourite;
remoteGameInfo.userData = serverInfo;
remoteGameInfo.getPlayersImplementation = RemoteGameInfoGetPlayers;
remoteGameInfo.requestRefreshImplementation = RemoteGameInfoRequestRefresh;
remoteGameInfo.getRuleBookImplementation = RemoteGameInfoGetRuleBook;
remoteGameInfo.currentSceneName = serverInfo.Map;
remoteGameInfo.currentSceneIndex = SceneCatalog.GetSceneDefFromSceneName(serverInfo.Map)?.sceneDefIndex;
remoteGameInfo.CalcExtraFields();
}
private static void RemoteGameInfoRequestRefresh(in RemoteGameInfo remoteGameInfo, RemoteGameInfo.RequestRefreshSuccessCallback successCallback, Action failureCallback, bool fetchDetails)
{
if (remoteGameInfo.userData is ServerList.Server)
{
ServerList.Filter filter = new ServerList.Filter();
filter.Add("and", "2");
filter.Add("appid", Client.Instance.AppId.ToString());
filter.Add("gameaddr", remoteGameInfo.serverAddress.ToString());
ServerList.Request lanRefreshRequest = CreateRequest(Mode.Local, filter);
lanRefreshRequest.OnFinished = delegate
{
ServerList.Server refreshedServerInfoFromRequest = GetRefreshedServerInfoFromRequest(lanRefreshRequest);
if (refreshedServerInfoFromRequest != null)
{
HandleNewInfo(refreshedServerInfoFromRequest);
}
else
{
ServerList.Request internetRefreshRequest = CreateRequest(Mode.Internet, filter);
internetRefreshRequest.OnFinished = delegate
{
ServerList.Server refreshedServerInfoFromRequest2 = GetRefreshedServerInfoFromRequest(internetRefreshRequest);
if (refreshedServerInfoFromRequest2 != null)
{
HandleNewInfo(refreshedServerInfoFromRequest2);
}
else
{
failureCallback?.Invoke();
}
};
}
};
}
else
{
failureCallback?.Invoke();
}
static ServerList.Server GetRefreshedServerInfoFromRequest(ServerList.Request request)
{
if (request.Responded.Count > 0)
{
return request.Responded[0];
}
if (request.Unresponsive.Count > 0)
{
return request.Unresponsive[0];
}
return null;
}
void HandleNewInfo(ServerList.Server refreshedServerInfo)
{
RemoteGameInfo result = default(RemoteGameInfo);
GenerateRemoteGameInfo(ref result, refreshedServerInfo);
int awaitingRequests;
if (fetchDetails)
{
awaitingRequests = 2;
ServerList.Server server = refreshedServerInfo;
server.OnReceivedPlayerInfos = (Action<bool>)Delegate.Combine(server.OnReceivedPlayerInfos, new Action<bool>(OnReceivedPlayerInfos));
ServerList.Server server2 = refreshedServerInfo;
server2.OnReceivedRules = (Action<bool>)Delegate.Combine(server2.OnReceivedRules, new Action<bool>(OnReceivedRules));
refreshedServerInfo.FetchPlayerInfos();
refreshedServerInfo.FetchRules();
}
else
{
successCallback?.Invoke(in result);
}
void OnReceivedPlayerInfos(bool success)
{
ServerList.Server server4 = refreshedServerInfo;
server4.OnReceivedPlayerInfos = (Action<bool>)Delegate.Remove(server4.OnReceivedPlayerInfos, new Action<bool>(OnReceivedPlayerInfos));
int num2 = awaitingRequests - 1;
awaitingRequests = num2;
TryComplete();
}
void OnReceivedRules(bool success)
{
ServerList.Server server3 = refreshedServerInfo;
server3.OnReceivedRules = (Action<bool>)Delegate.Remove(server3.OnReceivedRules, new Action<bool>(OnReceivedRules));
if (success && refreshedServerInfo.HasRules)
{
refreshedServerInfo.Rules.TryGetValue("gameMode", out result.gameModeName);
refreshedServerInfo.Rules.TryGetValue("buildId", out result.buildId);
refreshedServerInfo.Rules.TryGetValue("modHash", out result.modHash);
result.buildId = result.buildId ?? "UNKNOWN";
result.modHash = result.modHash ?? "UNKNOWN";
}
int num = awaitingRequests - 1;
awaitingRequests = num;
TryComplete();
}
void TryComplete()
{
if (awaitingRequests == 0)
{
successCallback?.Invoke(in result);
}
}
}
}
private static bool RemoteGameInfoGetRuleBook(in RemoteGameInfo remoteGameInfo, RuleBook dest)
{
if (remoteGameInfo.userData is ServerList.Server { HasRules: not false } server)
{
dest.SetToDefaults();
RuleBook.ReadBase64(new KeyValueUnsplitter("ruleBook").GetValue(server.Rules), dest);
return true;
}
return false;
}
private static void RemoteGameInfoGetPlayers(in RemoteGameInfo remoteGameInfo, List<RemotePlayerInfo> output)
{
if (remoteGameInfo.userData is ServerList.Server { PlayerInfos: { } playerInfos })
{
for (int i = 0; i < playerInfos.Count; i++)
{
output.Add(new RemotePlayerInfo
{
name = playerInfos[i].name
});
}
}
}
}