Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server stats #133

Merged
merged 4 commits into from
Oct 3, 2024
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MatchmakingServer.Tests/MatchmakerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

using H2MLauncher.Core.Matchmaking.Models;

using MatchmakingServer.Matchmaking;
using MatchmakingServer.Matchmaking.Models;

namespace MatchmakingServer.Tests
{
public class MatchmakerTests
Expand Down
20 changes: 20 additions & 0 deletions MatchmakingServer/Api/EndpointRouteBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using MatchmakingServer.Authentication;
using MatchmakingServer.Stats;

namespace MatchmakingServer.Api
{
public static class EndpointRouteBuilderExtensions
{
public static void MapEndpoints(this WebApplication app)
{
app.MapEndpoint<AuthenticationEndpoint>();
app.MapEndpoint<StatsEndpoint>();
}

private static IEndpointRouteBuilder MapEndpoint<TEndpoint>(this IEndpointRouteBuilder app) where TEndpoint : IEndpoint
{
TEndpoint.Map(app);
return app;
}
}
}
7 changes: 7 additions & 0 deletions MatchmakingServer/Api/IEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MatchmakingServer.Api
{
public interface IEndpoint
{
static abstract void Map(IEndpointRouteBuilder app);
}
}
12 changes: 12 additions & 0 deletions MatchmakingServer/Api/RouteHandlerBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace MatchmakingServer.Api
{
public static class RouteHandlerBuilderExtensions
{
public static RouteHandlerBuilder WithValidation<TRequest>(this RouteHandlerBuilder builder)
{
return builder
.AddEndpointFilter<ValidationFilter<TRequest>>()
.ProducesValidationProblem();
}
}
}
24 changes: 24 additions & 0 deletions MatchmakingServer/Api/ValidationFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using FluentValidation;

namespace MatchmakingServer.Api;

public class ValidationFilter<T>(IValidator<T> validator) : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
T? request = context.Arguments.OfType<T>().First();

if (request is null)
{
return Results.BadRequest();
}

var validationResult = await validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return Results.ValidationProblem(validationResult.ToDictionary());
}

return await next(context);
}
}
42 changes: 42 additions & 0 deletions MatchmakingServer/ApplicationSetup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using MatchmakingServer.SignalR;

using Serilog;

namespace MatchmakingServer;

public static class ApplicationSetup
{
public static void UseRequestLogging(this IApplicationBuilder app)
{
app.UseSerilogRequestLogging(options =>
{
options.MessageTemplate = "HTTP {RequestMethod} {RequestPath} responded {StatusCode} in {Elapsed:0.0000} ms ({ClientAppName}/{ClientAppVersion})";
options.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
if (httpContext.Request.Headers.TryGetValue("X-App-Name", out var appNameValues) && appNameValues.Count != 0)
{
diagnosticContext.Set("ClientAppName", appNameValues.FirstOrDefault());
}
else
{
diagnosticContext.Set("ClientAppName", "Unknown");
}

if (httpContext.Request.Headers.TryGetValue("X-App-Version", out var appVersionValues) && appVersionValues.Count != 0)
{
diagnosticContext.Set("ClientAppVersion", appVersionValues.FirstOrDefault());
}
else
{
diagnosticContext.Set("ClientAppVersion", "?");
}
};
});
}

public static void MapHubs(this IEndpointRouteBuilder app)
{
app.MapHub<QueueingHub>("/Queue");
app.MapHub<PartyHub>("/Party");
}
}
41 changes: 41 additions & 0 deletions MatchmakingServer/Authentication/AuthenticationEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Security.Claims;

using FluentValidation;

using MatchmakingServer.Api;

using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Http.HttpResults;

namespace MatchmakingServer.Authentication
{
public class AuthenticationEndpoint : IEndpoint
{
public static void Map(IEndpointRouteBuilder app) => app
.MapGet("/login", Handle)
.WithSummary("Logs the user in with the UID and player name and returns a bearer token.")
.WithValidation<Request>();

public record Request(string Uid, string PlayerName);
public class RequestValidator : AbstractValidator<Request>
{
public RequestValidator()
{
RuleFor(x => x.Uid).NotEmpty().MaximumLength(36);
RuleFor(x => x.PlayerName).NotEmpty().MaximumLength(69);
}
}

private static SignInHttpResult Handle([AsParameters] Request request)
{
ClaimsPrincipal claimsPrincipal = new(
new ClaimsIdentity(
[new Claim(ClaimTypes.Name, request.PlayerName), new Claim(ClaimTypes.NameIdentifier, request.Uid)],
BearerTokenDefaults.AuthenticationScheme
)
);

return TypedResults.SignIn(claimsPrincipal);
}
}
}
1 change: 1 addition & 0 deletions MatchmakingServer/Controllers/PlaylistsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using H2MLauncher.Core.Networking.GameServer.HMW;
using H2MLauncher.Core.Services;

using MatchmakingServer.Matchmaking;
using MatchmakingServer.SignalR;

using Microsoft.AspNetCore.Mvc;
Expand Down
57 changes: 28 additions & 29 deletions MatchmakingServer/Matchmaking/MMServerPriorityComparer.cs
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
namespace MatchmakingServer
namespace MatchmakingServer.Matchmaking;

class MMServerPriorityComparer : IComparer<GameServer>
{
class MMServerPriorityComparer : IComparer<GameServer>
public int Compare(GameServer? x, GameServer? y)
{
public int Compare(GameServer? x, GameServer? y)
if (x?.LastServerInfo is null || y?.LastServerInfo is null)
{
if (x?.LastServerInfo is null || y?.LastServerInfo is null)
{
return 0;
}

// Check if we should prioritize based on TotalScore < 1000
bool xIsHalfFull = x.LastStatusResponse?.TotalScore < 1000 && x.LastServerInfo.RealPlayerCount < 6;
bool yIsHalfFull = y.LastStatusResponse?.TotalScore < 1000 && x.LastServerInfo.RealPlayerCount < 6;

// Case 1: If both servers are half empty and under the score limit,
// prioritize by player count (servers with fewer players should be prioritized)
if (xIsHalfFull && yIsHalfFull)
{
return x.LastServerInfo.RealPlayerCount.CompareTo(y.LastServerInfo.RealPlayerCount);
}
return 0;
}

// Case 2: If one server is half full and under the score limit and the other one is not,
// prioritize the half full server
if (xIsHalfFull && !yIsHalfFull)
{
return -1;
}
if (!xIsHalfFull && yIsHalfFull)
{
return 1;
}
// Check if we should prioritize based on TotalScore < 1000
bool xIsHalfFull = x.LastStatusResponse?.TotalScore < 1000 && x.LastServerInfo.RealPlayerCount < 6;
bool yIsHalfFull = y.LastStatusResponse?.TotalScore < 1000 && x.LastServerInfo.RealPlayerCount < 6;

// Case 3: If both servers are over the score limit, prioritize by fewer players
// Case 1: If both servers are half empty and under the score limit,
// prioritize by player count (servers with fewer players should be prioritized)
if (xIsHalfFull && yIsHalfFull)
{
return x.LastServerInfo.RealPlayerCount.CompareTo(y.LastServerInfo.RealPlayerCount);
}

// Case 2: If one server is half full and under the score limit and the other one is not,
// prioritize the half full server
if (xIsHalfFull && !yIsHalfFull)
{
return -1;
}
if (!xIsHalfFull && yIsHalfFull)
{
return 1;
}

// Case 3: If both servers are over the score limit, prioritize by fewer players
return x.LastServerInfo.RealPlayerCount.CompareTo(y.LastServerInfo.RealPlayerCount);
}
}
Loading
Loading