Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
13 changes: 11 additions & 2 deletions FLB_API.slnx
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
<Solution>
<Project Path="FLB_API/FLB_API.csproj" />
<Project Path="FusionAPI/FusionAPI.csproj" />
<Configurations>
<BuildType Name="Debug" />
<BuildType Name="EOS_PLATFORM_WINDOWS_64" />
<BuildType Name="Release" />
</Configurations>
<Project Path="FLB_API/FLB_API.csproj">
<BuildType Solution="EOS_PLATFORM_WINDOWS_64|*" Project="Release" />
</Project>
<Project Path="FusionAPI/FusionAPI.csproj">
<BuildType Solution="EOS_PLATFORM_WINDOWS_64|*" Project="Release" />
</Project>
</Solution>
11 changes: 8 additions & 3 deletions FLB_API/Controllers/LobbyListController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ namespace FLB_API.Controllers
public class LobbyListController : ControllerBase
{
[HttpGet(Name = "GetServers")]
public IActionResult Get()
public IActionResult Get([FromQuery(Name = "service")] string service = "Steam")
{
if (Program.FusionClient?.Handler.IsInitialized != true)
return StatusCode(500, "Server is not connected to Steam.");

Response.Headers.AccessControlExposeHeaders = new Microsoft.Extensions.Primitives.StringValues("Server-Uptime");
Response.Headers.Append("Server-Uptime", ((DateTimeOffset)Program.Uptime).ToUnixTimeSeconds().ToString() ?? "-1");

return Ok(new LobbyListResponse(Program.Lobbies ?? [], Program.Date, Program.PlayerCount ?? new(0, 0)));
if (service.Equals("Steam", StringComparison.OrdinalIgnoreCase))
return Ok(new LobbyListResponse(Program.SteamLobbies ?? [], Program.Date));
else if (service.Equals("Epic", StringComparison.OrdinalIgnoreCase))
return Ok(new LobbyListResponse(Program.EOSLobbies ?? [], Program.Date));
else
return NotFound("The provided service does not exist. Choose from the following: Steam, Epic");
}
}
}
}
1 change: 1 addition & 0 deletions FLB_API/FLB_API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Configurations>Debug;Release</Configurations>
</PropertyGroup>

<ItemGroup>
Expand Down
11 changes: 1 addition & 10 deletions FLB_API/LobbyListResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,12 @@
namespace FLB_API
{
[JsonSourceGenerationOptions(WriteIndented = true)]
public class LobbyListResponse(LobbyInfo[] lobbies, DateTime date, PlayerCount count)
public class LobbyListResponse(LobbyInfo[] lobbies, DateTime date)
{
public LobbyInfo[] Lobbies { get; set; } = lobbies;

public long Date { get; set; } = ((DateTimeOffset)date).ToUnixTimeSeconds();

public PlayerCount PlayerCount { get; set; } = count;

public int Interval { get; set; } = Program.Settings?.Interval ?? 30;
}

public class PlayerCount(int players, int lobbies)
{
public int Players { get; set; } = players;

public int Lobbies { get; set; } = lobbies;
}
}
30 changes: 22 additions & 8 deletions FLB_API/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ public static class Program
{
public static Fusion? FusionClient { get; private set; }

public static Fusion? EOSClient { get; private set; }

internal static Serilog.Core.Logger? Logger { get; private set; }

internal static LobbyInfo[]? Lobbies { get; private set; }
internal static LobbyInfo[]? SteamLobbies { get; private set; }

internal static DateTime Date { get; private set; } = DateTime.UtcNow;
internal static LobbyInfo[]? EOSLobbies { get; private set; }

internal static PlayerCount? PlayerCount { get; private set; }
internal static DateTime Date { get; private set; } = DateTime.UtcNow;

internal static DateTime Uptime { get; private set; }

Expand Down Expand Up @@ -107,7 +109,10 @@ public static async Task Main(string[] args)

var logger = new Logger(level);
await FusionClient.Initialize(logger, metadata);
Logger?.Information("Successfully initialized Fusion API");
Logger?.Information("Successfully initialized Steam Fusion API! Initializing EOS (Epic Online Services)...");
EOSClient = new Fusion(new EOSHandler());
await EOSClient.Initialize(logger, []);
Logger?.Information("Successfully initialized EOS API");
Uptime = DateTime.UtcNow;
}
catch (Exception e)
Expand Down Expand Up @@ -250,15 +255,24 @@ private static async Task GetLobbies(CancellationToken token)
{
try
{
Logger?.Information("Fetching lobbies...");
Logger?.Information("Fetching Steam lobbies...");
var lobbies = await FusionClient.GetLobbies(includeFull: true, includePrivate: false, includeSelf: true);
int players = 0;
foreach (var lobby in lobbies)
players += lobby.PlayerCount;
PlayerCount = new(players, lobbies.Length);
Date = DateTime.UtcNow;
Lobbies = lobbies;
Logger?.Information($"Successfully fetched lobbies ({lobbies.Length})...");
SteamLobbies = lobbies;

Logger?.Information($"Successfully fetched Steam lobbies ({lobbies.Length})...");

Logger?.Information("Fetching EOS lobbies...");
var eLobbies = await EOSClient.GetLobbies(includeFull: true, includePrivate: false, includeSelf: true);
int ePlayers = 0;
foreach (var lobby in eLobbies)
ePlayers += lobby.PlayerCount;
EOSLobbies = eLobbies;

Logger?.Information($"Successfully fetched EOS lobbies ({eLobbies.Length})...");
LoadSettings();
}
catch (Exception e)
Expand Down
198 changes: 198 additions & 0 deletions FusionAPI/CoRoutine.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
using System.Collections;
using System.Reflection;
using System.Text;

namespace FusionAPI;

// Wrapper class for IEnumerator objects
// This class is nice because it allows IEnumerator's to return other IEnumerator's just like Unity
// We call it CoRoutine instead of Coroutine to differentiate it from UnityEngine.CoRoutine
public class CoRoutine
{
readonly Stack<IEnumerator> _processStack;
readonly List<IEnumerator> _finished = new List<IEnumerator>();

object _returnValue;

public CoRoutine(IEnumerator enumerator)
{
_processStack = new Stack<IEnumerator>();
_processStack.Push(enumerator);
}

public object ReturnValue
{
get
{
Assert(!_processStack.Any());
return _returnValue;
}
}

public bool IsDone
{
get
{
return !_processStack.Any();
}
}

// Returns true if it needs to be called again
public bool Pump()
{
Assert(_processStack.Any());
Assert(_returnValue == null);

var topWorker = _processStack.Peek();

bool isFinished;

try
{
isFinished = !topWorker.MoveNext();
}
catch (CoRoutineException e)
{
var objectTrace = GenerateObjectTrace(_finished.Concat(_processStack));

if (!objectTrace.Any())
{
throw e;
}

throw new CoRoutineException(objectTrace.Concat(e.ObjectTrace).ToList(), e.InnerException);
}
catch (Exception e)
{
var objectTrace = GenerateObjectTrace(_finished.Concat(_processStack));

if (!objectTrace.Any())
{
throw e;
}

throw new CoRoutineException(objectTrace, e);
}

if (isFinished)
{
_finished.Add(_processStack.Pop());
}

if (topWorker.Current != null && typeof(IEnumerator).IsAssignableFrom(topWorker.Current.GetType()))
{
_processStack.Push((IEnumerator)topWorker.Current);
}

if (!_processStack.Any())
{
_returnValue = topWorker.Current;
}

return _processStack.Any();
}

static List<Type> GenerateObjectTrace(IEnumerable<IEnumerator> enumerators)
{
var objTrace = new List<Type>();

foreach (var enumerator in enumerators)
{
var field = enumerator.GetType().GetField("<>4__this", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

if (field == null)
{
// Mono seems to use a different name
field = enumerator.GetType().GetField("<>f__this", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

if (field == null)
{
continue;
}
}

var obj = field.GetValue(enumerator);

if (obj == null)
{
continue;
}

var objType = obj.GetType();

if (!objTrace.Any() || objType != objTrace.Last())
{
objTrace.Add(objType);
}
}

objTrace.Reverse();
return objTrace;
}

static void Assert(bool condition)
{
if (!condition)
{
throw new AssertException("Assert hit in CoRoutine!");
}
}

static void Assert(bool condition, string message)
{
if (!condition)
{
throw new AssertException(
"Assert hit in CoRoutine! " + message);
}
}

public class TimeoutException : Exception
{
}

public class AssertException : Exception
{
public AssertException(string message)
: base(message)
{
}
}

public class CoRoutineException : Exception
{
readonly List<Type> _objTrace;

public CoRoutineException(List<Type> objTrace, Exception innerException)
: base(CreateMessage(objTrace), innerException)
{
_objTrace = objTrace;
}

static string CreateMessage(List<Type> objTrace)
{
var result = new StringBuilder();

foreach (var objType in objTrace)
{
if (result.Length != 0)
{
result.Append(" -> ");
}

result.Append(objType.Name);
}

result.AppendLine();
return "Coroutine Object Trace: " + result.ToString();
}

public List<Type> ObjectTrace
{
get
{
return _objTrace;
}
}
}
}
2 changes: 1 addition & 1 deletion FusionAPI/Data/Containers/LobbyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class LobbyInfo

// Info
[JsonPropertyName("lobbyID")]
public ulong LobbyID { get; set; } = 0;
public string LobbyID { get; set; } = "0";

[JsonPropertyName("lobbyCode")]
public string? LobbyCode { get; set; } = null;
Expand Down
2 changes: 1 addition & 1 deletion FusionAPI/Data/Containers/PlayerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace FusionAPI.Data.Containers;
public class PlayerInfo()
{
[JsonPropertyName("platformID")]
public ulong PlatformID { get; set; }
public string PlatformID { get; set; }

[JsonPropertyName("username")]
public string? Username { get; set; }
Expand Down
Loading