From 692c71c0c5f13bfdceba425392dce5e172c10d3f Mon Sep 17 00:00:00 2001
From: NotBlue <64601123+NotBlue-Dev@users.noreply.github.com>
Date: Tue, 14 Nov 2023 18:29:54 +0100
Subject: [PATCH] Add public HTTP API endpoints for centralized app (#22)
---
.../Forms/Controls/ServerInfoControl.cs | 14 +++-
EchoRelay.App/Forms/MainWindow.cs | 9 +++
EchoRelay.Cli/Program.cs | 78 ++++++++++++++++---
EchoRelay.Core/Monitoring/ApiClient.cs | 42 ++++++++++
EchoRelay.Core/Monitoring/ApiManager.cs | 41 ++++++++++
EchoRelay.Core/Monitoring/GameServer.cs | 63 +++++++++++++++
EchoRelay.Core/Monitoring/GameServerObject.cs | 25 ++++++
EchoRelay.Core/Monitoring/PeerStats.cs | 30 +++++++
EchoRelay.Core/Monitoring/PeerStatsObject.cs | 23 ++++++
EchoRelay.Core/Monitoring/Server.cs | 63 +++++++++++++++
EchoRelay.Core/Monitoring/ServerObject.cs | 28 +++++++
EchoRelay.Core/Server/Server.cs | 36 ++++++++-
EchoRelay.Core/Server/ServerSettings.cs | 8 +-
.../Services/ServerDB/GameServerRegistry.cs | 18 +++++
.../Services/ServerDB/RegisteredGameServer.cs | 45 ++++++++++-
15 files changed, 509 insertions(+), 14 deletions(-)
create mode 100644 EchoRelay.Core/Monitoring/ApiClient.cs
create mode 100644 EchoRelay.Core/Monitoring/ApiManager.cs
create mode 100644 EchoRelay.Core/Monitoring/GameServer.cs
create mode 100644 EchoRelay.Core/Monitoring/GameServerObject.cs
create mode 100644 EchoRelay.Core/Monitoring/PeerStats.cs
create mode 100644 EchoRelay.Core/Monitoring/PeerStatsObject.cs
create mode 100644 EchoRelay.Core/Monitoring/Server.cs
create mode 100644 EchoRelay.Core/Monitoring/ServerObject.cs
diff --git a/EchoRelay.App/Forms/Controls/ServerInfoControl.cs b/EchoRelay.App/Forms/Controls/ServerInfoControl.cs
index 2d16b3f..7ccd0df 100644
--- a/EchoRelay.App/Forms/Controls/ServerInfoControl.cs
+++ b/EchoRelay.App/Forms/Controls/ServerInfoControl.cs
@@ -1,11 +1,13 @@
-using EchoRelay.Core.Server;
+using EchoRelay.Core.Monitoring;
using EchoRelay.Core.Utils;
using Newtonsoft.Json;
+using Server = EchoRelay.Core.Server.Server;
namespace EchoRelay.App.Forms.Controls
{
public partial class ServerInfoControl : UserControl
{
+ ApiManager _apiManager = ApiManager.Instance;
public ServerInfoControl()
{
InitializeComponent();
@@ -36,6 +38,16 @@ public void UpdateServerInfo(Server? server, bool updateServiceConfig)
rtbGeneratedServiceConfig.Text = "";
}
}
+
+
+ _apiManager.peerStatsObject.ServerIp = server?.PublicIPAddress?.ToString() ?? "localhost";
+ _apiManager.peerStatsObject.Login = server?.LoginService.Peers.Count ?? 0;
+ _apiManager.peerStatsObject.Matching = server?.MatchingService.Peers.Count ?? 0;
+ _apiManager.peerStatsObject.Config = server?.ConfigService.Peers.Count ?? 0;
+ _apiManager.peerStatsObject.Transaction = server?.TransactionService.Peers.Count ?? 0;
+ _apiManager.peerStatsObject.ServerDb = server?.ServerDBService.Peers.Count ?? 0;
+ Task.Run(() => _apiManager.PeerStats.EditPeerStats(_apiManager.peerStatsObject, server?.PublicIPAddress?.ToString() ?? "localhost"));
+
}
private void btnCopyServiceConfig_Click(object sender, EventArgs e)
diff --git a/EchoRelay.App/Forms/MainWindow.cs b/EchoRelay.App/Forms/MainWindow.cs
index 88ad27c..66bf803 100644
--- a/EchoRelay.App/Forms/MainWindow.cs
+++ b/EchoRelay.App/Forms/MainWindow.cs
@@ -13,6 +13,8 @@
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
+using EchoRelay.Core.Monitoring;
+using Server = EchoRelay.Core.Server.Server;
namespace EchoRelay
{
@@ -36,6 +38,8 @@ public partial class MainWindow : Form
/// The websocket server used to power central game services.
///
public Server Server { get; set; }
+
+ public ApiManager apiManager { get; set; }
///
/// The UI editors for different storage resources.
@@ -130,6 +134,8 @@ public MainWindow()
Server.ServerDBService.Registry.OnGameServerRegistered += Registry_OnGameServerRegistered;
Server.ServerDBService.Registry.OnGameServerUnregistered += Registry_OnGameServerUnregistered;
Server.ServerDBService.OnGameServerRegistrationFailure += ServerDBService_OnGameServerRegistrationFailure; ;
+
+ apiManager = ApiManager.Instance;
}
#endregion
@@ -360,6 +366,9 @@ private void Registry_OnGameServerRegistered(EchoRelay.Core.Server.Services.Serv
// Update server count on the game server tab.
tabGameServers.Text = $"Game Servers ({gameServersControl.GameServerCount})";
+ apiManager.peerStatsObject.GameServers = gameServersControl.GameServerCount;
+ Task.Run(() => apiManager.PeerStats.EditPeerStats(apiManager.peerStatsObject, Server.PublicIPAddress?.ToString() ?? "localhost"));
+
AppendLogText($"[{gameServer.Peer.Service.Name}] client({gameServer.Peer.Address}:{gameServer.Peer.Port}) registered game server (server_id={gameServer.ServerId}, region_symbol={gameServer.RegionSymbol}, version_lock={gameServer.VersionLock}, endpoint=<{gameServer.ExternalAddress}:{gameServer.Port}>)\n");
});
}
diff --git a/EchoRelay.Cli/Program.cs b/EchoRelay.Cli/Program.cs
index 42a6900..52456e3 100644
--- a/EchoRelay.Cli/Program.cs
+++ b/EchoRelay.Cli/Program.cs
@@ -8,6 +8,9 @@
using Microsoft.AspNetCore.Components.Web;
using Newtonsoft.Json;
using System.Reflection;
+using System.Runtime.InteropServices;
+using EchoRelay.Core.Monitoring;
+using Server = EchoRelay.Core.Server.Server;
namespace EchoRelay.Cli
{
@@ -20,11 +23,13 @@ class Program
///
/// The instance of the server hosting central services.
///
- private static Server Server;
+ private static Server? Server;
///
/// The update timer used to trigger a peer stats update on a given interval.
///
private static System.Timers.Timer? peerStatsUpdateTimer;
+
+ private static ApiManager? apiManager;
///
/// The time that the server was started.
///
@@ -33,6 +38,22 @@ class Program
/// A mutex lock object to be used when printing to console, to avoid color collisions.
///
private static object _printLock = new object();
+
+ [DllImport("Kernel32")]
+ private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
+
+ private delegate bool EventHandler(CtrlType sig);
+ private static EventHandler? consoleCloseHandler;
+
+ // Enum to represent different CtrlTypes
+ private enum CtrlType
+ {
+ CTRL_C_EVENT = 0,
+ CTRL_BREAK_EVENT = 1,
+ CTRL_CLOSE_EVENT = 2,
+ CTRL_LOGOFF_EVENT = 5,
+ CTRL_SHUTDOWN_EVENT = 6
+ }
///
/// The CLI argument options for the application.
@@ -139,6 +160,8 @@ static void Main(string[] args)
)
);
+ apiManager = ApiManager.Instance;
+
// Set up all event handlers.
Server.OnServerStarted += Server_OnServerStarted;
Server.OnServerStopped += Server_OnServerStopped;
@@ -149,6 +172,10 @@ static void Main(string[] args)
Server.ServerDBService.Registry.OnGameServerRegistered += Registry_OnGameServerRegistered;
Server.ServerDBService.Registry.OnGameServerUnregistered += Registry_OnGameServerUnregistered;
Server.ServerDBService.OnGameServerRegistrationFailure += ServerDBService_OnGameServerRegistrationFailure;
+
+ // Set up the event handler for the console close event
+ consoleCloseHandler += new EventHandler(ConsoleCloseHandler);
+ SetConsoleCtrlHandler(consoleCloseHandler, true);
// Set up all verbose event handlers.
if (options.Verbose)
@@ -187,6 +214,8 @@ private static void Server_OnServerStarted(Server server)
}
}
+ updateServerInfo();
+
// Start the peer stats update timer
startedTime = DateTime.UtcNow;
peerStatsUpdateTimer = new System.Timers.Timer(Options!.StatsUpdateInterval);
@@ -197,14 +226,14 @@ private static void Server_OnServerStarted(Server server)
private static void PeerStatsUpdateTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
{
Info($"[PEERSTATS] " +
- $"elapsed: {(DateTime.UtcNow - startedTime)}, " +
- $"gameservers: {Server.ServerDBService.Registry.RegisteredGameServers.Count}, " +
- $"login: {Server.LoginService.Peers.Count}, " +
- $"config: {Server.ConfigService.Peers.Count}, " +
- $"matching: {Server.MatchingService.Peers.Count}, " +
- $"serverdb: {Server.ServerDBService.Peers.Count}, " +
- $"transaction: {Server.TransactionService.Peers.Count}"
- );
+ $"elapsed: {(DateTime.UtcNow - startedTime)}, " +
+ $"gameservers: {Server.ServerDBService.Registry.RegisteredGameServers.Count}, " +
+ $"login: {Server.LoginService.Peers.Count}, " +
+ $"config: {Server.ConfigService.Peers.Count}, " +
+ $"matching: {Server.MatchingService.Peers.Count}, " +
+ $"serverdb: {Server.ServerDBService.Peers.Count}, " +
+ $"transaction: {Server.TransactionService.Peers.Count}"
+ );
}
private static void Server_OnServerStopped(Server server)
@@ -214,6 +243,8 @@ private static void Server_OnServerStopped(Server server)
// Print our server stopped message
Info("[SERVER] Server stopped");
+
+ updateServerInfo();
}
private static void Server_OnAuthorizationResult(Server server, System.Net.IPEndPoint client, bool authorized)
@@ -221,13 +252,27 @@ private static void Server_OnAuthorizationResult(Server server, System.Net.IPEnd
if(!authorized)
Error($"[SERVER] client({client.Address}:{client.Port}) failed authorization");
}
+
+ private static void updateServerInfo()
+ {
+ apiManager.peerStatsObject.ServerIp = Server?.PublicIPAddress?.ToString() ?? "localhost";
+ apiManager.peerStatsObject.Login = Server?.LoginService.Peers.Count;
+ apiManager.peerStatsObject.Matching = Server?.MatchingService.Peers.Count;
+ apiManager.peerStatsObject.Config = Server?.ConfigService.Peers.Count;
+ apiManager.peerStatsObject.Transaction = Server?.TransactionService.Peers.Count;
+ apiManager.peerStatsObject.ServerDb = Server?.ServerDBService.Peers.Count;
+ apiManager.peerStatsObject.GameServers = Server?.ServerDBService.Registry.RegisteredGameServers.Count;
+ Task.Run(() => apiManager.PeerStats.EditPeerStats(apiManager.peerStatsObject, apiManager.peerStatsObject.ServerIp));
+ }
private static void Server_OnServicePeerConnected(Core.Server.Services.Service service, Core.Server.Services.Peer peer)
{
Info($"[{service.Name}] client({peer.Address}:{peer.Port}) connected");
+ updateServerInfo();
}
private static void Server_OnServicePeerDisconnected(Core.Server.Services.Service service, Core.Server.Services.Peer peer)
{
Info($"[{service.Name}] client({peer.Address}:{peer.Port}) disconnected");
+ updateServerInfo();
}
private static void Server_OnServicePeerAuthenticated(Core.Server.Services.Service service, Core.Server.Services.Peer peer, Core.Game.XPlatformId userId)
{
@@ -237,11 +282,14 @@ private static void Server_OnServicePeerAuthenticated(Core.Server.Services.Servi
private static void Registry_OnGameServerRegistered(Core.Server.Services.ServerDB.RegisteredGameServer gameServer)
{
Info($"[{gameServer.Peer.Service.Name}] client({gameServer.Peer.Address}:{gameServer.Peer.Port}) registered game server (server_id={gameServer.ServerId}, region_symbol={gameServer.RegionSymbol}, version_lock={gameServer.VersionLock}, endpoint=<{gameServer.ExternalAddress}:{gameServer.Port}>)");
+ updateServerInfo();
}
private static void Registry_OnGameServerUnregistered(Core.Server.Services.ServerDB.RegisteredGameServer gameServer)
{
Info($"[{gameServer.Peer.Service.Name}] client({gameServer.Peer.Address}:{gameServer.Peer.Port}) unregistered game server (server_id={gameServer.ServerId}, region_symbol={gameServer.RegionSymbol}, version_lock={gameServer.VersionLock}, endpoint=<{gameServer.ExternalAddress}:{gameServer.Port}>)");
+ updateServerInfo();
+
}
private static void ServerDBService_OnGameServerRegistrationFailure(Peer peer, Core.Server.Messages.ServerDB.ERGameServerRegistrationRequest registrationRequest, string failureMessage)
{
@@ -310,5 +358,17 @@ private static void Debug(string msg)
Console.ResetColor();
}
}
+
+ // Handler for the console close event
+ private static bool ConsoleCloseHandler(CtrlType sig)
+ {
+ Console.WriteLine("Console is closing. Performing cleanup...");
+ Server?.Stop();
+
+ Thread.Sleep(1500);
+
+ // Allow the program to exit
+ return true;
+ }
}
}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/ApiClient.cs b/EchoRelay.Core/Monitoring/ApiClient.cs
new file mode 100644
index 0000000..f071e0e
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/ApiClient.cs
@@ -0,0 +1,42 @@
+using System.Security.Cryptography;
+using System.Text;
+
+namespace EchoRelay.Core.Monitoring;
+
+//TO DO : Encrypt data
+public class ApiClient
+{
+ private readonly HttpClient _httpClient;
+
+ public ApiClient(string baseUrl)
+ {
+ _httpClient = new HttpClient
+ {
+ BaseAddress = new Uri(baseUrl)
+ };
+ }
+
+ //TODO encrypt data to avoid people to send fake data
+ public async Task PostMonitoringData(string endpoint, string data)
+ {
+ HttpContent content = new StringContent(data, Encoding.UTF8, "application/json");
+ HttpResponseMessage response = await _httpClient.PostAsync(endpoint, content);
+
+ if (response.IsSuccessStatusCode)
+ {
+ return await response.Content.ReadAsStringAsync();
+ }
+ throw new Exception($"API request failed with status code: {response.StatusCode}");
+ }
+
+ public async Task DeleteMonitoringData(string endpoint)
+ {
+ HttpResponseMessage response = await _httpClient.DeleteAsync(endpoint);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new Exception($"DELETE request failed with status code: {response.StatusCode}");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/ApiManager.cs b/EchoRelay.Core/Monitoring/ApiManager.cs
new file mode 100644
index 0000000..9b45ca5
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/ApiManager.cs
@@ -0,0 +1,41 @@
+using System.Security.Cryptography;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class ApiManager
+{
+
+ ///
+ /// Uri to the monitoring API
+ ///
+ public string URI { get; } = "http://localhost:3000/api/";
+ //public string URI { get; } = "http://51.75.140.182:3000/api/";
+
+ public PeerStatsObject peerStatsObject;
+ ///
+ /// Public key to encrypt data
+ ///
+ public ApiClient Monitoring { get; }
+
+ public Server Server { get; }
+
+ public GameServer GameServer { get; }
+ public PeerStats PeerStats { get; }
+
+ private static ApiManager instance = new ApiManager();
+
+ private ApiManager()
+ {
+ Monitoring = new ApiClient(URI);
+ GameServer = new GameServer(Monitoring);
+ PeerStats = new PeerStats(Monitoring);
+ Server = new Server(Monitoring);
+ peerStatsObject = new PeerStatsObject();
+ }
+
+ // Method to get the singleton instance
+ public static ApiManager Instance
+ {
+ get { return instance; }
+ }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/GameServer.cs b/EchoRelay.Core/Monitoring/GameServer.cs
new file mode 100644
index 0000000..45fb1f2
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/GameServer.cs
@@ -0,0 +1,63 @@
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class GameServer
+{
+ private readonly ApiClient _apiClient;
+
+ public GameServer(ApiClient apiClient)
+ {
+ _apiClient = apiClient;
+ }
+
+ public async Task DeleteGameServerAsync(string sessionId)
+ {
+ // Define the endpoint for deleting a game server with the sessionID
+ string endpoint = $"deleteGameServer/{sessionId}";
+
+ try
+ {
+ await _apiClient.DeleteMonitoringData(endpoint);
+ Console.WriteLine($"Game server with session ID {sessionId} deleted successfully from monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error deleting the game server from monitoring: {ex.Message}");
+ }
+ }
+
+ public async Task AddGameServerAsync(GameServerObject jsonObject)
+ {
+ // Create a StringContent with the JSON data and set the content type
+ string jsonData = JsonConvert.SerializeObject(jsonObject);
+ string endpoint = "addGameServer/";
+
+ try
+ {
+ await _apiClient.PostMonitoringData(endpoint, jsonData);
+ Console.WriteLine($"Game server successfully added to monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error adding the game server from monitoring: {ex.Message}");
+ }
+ }
+
+ public async Task EditGameServer(GameServerObject jsonObject, string gameServerID)
+ {
+ // Create a StringContent with the JSON data and set the content type
+ string jsonData = JsonConvert.SerializeObject(jsonObject);
+ string endpoint = $"updateGameServer/{gameServerID}";
+
+ try
+ {
+ await _apiClient.PostMonitoringData(endpoint, jsonData);
+ Console.WriteLine($"Game server successfully edited in monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error editing the game server in monitoring: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/GameServerObject.cs b/EchoRelay.Core/Monitoring/GameServerObject.cs
new file mode 100644
index 0000000..a341780
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/GameServerObject.cs
@@ -0,0 +1,25 @@
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class GameServerObject
+{
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "serverIP")]
+ public string? ServerIp { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "region")]
+ public string? Region { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "level")]
+ public string? Level { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "gameMode")]
+ public string? GameMode { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "playerCount")]
+ public int PlayerCount { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "assigned")]
+ public bool Assigned { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "sessionID")]
+ public string? SessionId { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "gameServerID")]
+ public ulong GameServerId { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "public")]
+ public bool @Public { get; set; }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/PeerStats.cs b/EchoRelay.Core/Monitoring/PeerStats.cs
new file mode 100644
index 0000000..bac7c8c
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/PeerStats.cs
@@ -0,0 +1,30 @@
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class PeerStats
+{
+ private readonly ApiClient _apiClient;
+
+ public PeerStats(ApiClient apiClient)
+ {
+ _apiClient = apiClient;
+ }
+
+ public async Task EditPeerStats(PeerStatsObject jsonObject, string server)
+ {
+ // Create a StringContent with the JSON data and set the content type
+ string jsonData = JsonConvert.SerializeObject(jsonObject);
+ string endpoint = $"updatePeerStats/{server}";
+
+ try
+ {
+ await _apiClient.PostMonitoringData(endpoint, jsonData);
+ Console.WriteLine($"Peer stats successfully edited in monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error editing the Peer stats in monitoring: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/PeerStatsObject.cs b/EchoRelay.Core/Monitoring/PeerStatsObject.cs
new file mode 100644
index 0000000..c71817f
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/PeerStatsObject.cs
@@ -0,0 +1,23 @@
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class PeerStatsObject
+{
+ [JsonProperty(PropertyName = "serverIP")]
+ public string? ServerIp { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "gameServers")]
+ public int? GameServers { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "login")]
+ public int? Login { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "matching")]
+ public int? Matching { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "config")]
+ public int? Config { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "transaction")]
+ public int? Transaction { get; set; }
+
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "serverdb")]
+ public int? ServerDb { get; set; }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/Server.cs b/EchoRelay.Core/Monitoring/Server.cs
new file mode 100644
index 0000000..01983a7
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/Server.cs
@@ -0,0 +1,63 @@
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class Server
+{
+ private readonly ApiClient _apiClient;
+
+ public Server(ApiClient apiClient)
+ {
+ _apiClient = apiClient;
+ }
+
+ public async Task DeleteServerAsync(string server)
+ {
+ // Define the endpoint for deleting a game server with the server IP
+ string endpoint = $"deleteServer/{server}";
+
+ try
+ {
+ await _apiClient.DeleteMonitoringData(endpoint);
+ Console.WriteLine($"Server {server} deleted successfully from monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error deleting the server from monitoring: {ex.Message}");
+ }
+ }
+
+ public async Task AddServerAsync(ServerObject jsonObject)
+ {
+ // Create a StringContent with the JSON data and set the content type
+ string jsonData = JsonConvert.SerializeObject(jsonObject);
+ string endpoint = "addServer/";
+
+ try
+ {
+ await _apiClient.PostMonitoringData(endpoint, jsonData);
+ Console.WriteLine($"Server successfully added to monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error adding the server to monitoring: {ex.Message}");
+ }
+ }
+
+ public async Task EditServer(ServerObject jsonObject, string server)
+ {
+ // Create a StringContent with the JSON data and set the content type
+ string jsonData = JsonConvert.SerializeObject(jsonObject);
+ string endpoint = $"updateServer/{server}";
+
+ try
+ {
+ await _apiClient.PostMonitoringData(endpoint, jsonData);
+ Console.WriteLine($"Server successfully edited in monitoring.");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error editing the game server in monitoring: {ex.Message}");
+ }
+ }
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Monitoring/ServerObject.cs b/EchoRelay.Core/Monitoring/ServerObject.cs
new file mode 100644
index 0000000..d587bb8
--- /dev/null
+++ b/EchoRelay.Core/Monitoring/ServerObject.cs
@@ -0,0 +1,28 @@
+using EchoRelay.Core.Game;
+using Newtonsoft.Json;
+
+namespace EchoRelay.Core.Monitoring;
+
+public class ServerObject
+{
+ [JsonProperty(PropertyName = "ip")]
+ public string Ip { get; set; } = "";
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "apiservice_host")]
+ public string? ApiServiceHost { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "configservice_host")]
+ public string? ConfigServiceHost { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "loginservice_host")]
+ public string? LoginServiceHost { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "matchingservice_host")]
+ public string? MatchingServiceHost { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "serverdb_host")]
+ public string? ServerDbHost { get; set; }
+ [JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "transactionservice_host")]
+ public string? TransactionServiceHost { get; set; }
+
+ [JsonProperty(PropertyName = "publisher_lock")]
+ public string PublisherLock { get; set; } = "rad15_live";
+
+ [JsonProperty(PropertyName = "online")]
+ public bool Online { get; set; } = false;
+}
\ No newline at end of file
diff --git a/EchoRelay.Core/Server/Server.cs b/EchoRelay.Core/Server/Server.cs
index 87b931d..ed4fa41 100644
--- a/EchoRelay.Core/Server/Server.cs
+++ b/EchoRelay.Core/Server/Server.cs
@@ -13,6 +13,7 @@
using System.Collections.ObjectModel;
using System.Net;
using System.Net.WebSockets;
+using EchoRelay.Core.Monitoring;
using static EchoRelay.Core.Server.Services.Service;
namespace EchoRelay.Core.Server
@@ -36,6 +37,11 @@ public class Server
/// The settings for the server to operate under.
///
public ServerSettings Settings { get; private set; }
+ ///
+ /// The API manager for monitoring
+ ///
+ public ApiManager? apiManager { get; private set; }
+
///
/// The persistent storage layer for the server.
///
@@ -174,6 +180,9 @@ public Server(ServerStorage storage, ServerSettings settings)
{ Settings.ServerDBServicePath.ToLower(), ServerDBService },
{ Settings.TransactionServicePath.ToLower(), TransactionService },
}.AsReadOnly();
+
+ apiManager = ApiManager.Instance;
+
}
#endregion
@@ -186,6 +195,7 @@ public Server(ServerStorage storage, ServerSettings settings)
/// An exception thrown if the server is already started when this method is called.
public async Task Start(CancellationTokenSource? cancellationTokenSource = null)
{
+ Console.WriteLine("Starting server...");
// If we are running already, throw an exception.
if (Running)
{
@@ -212,6 +222,18 @@ public async Task Start(CancellationTokenSource? cancellationTokenSource = null)
// Fire our started event
OnServerStarted?.Invoke(this);
+ ServerObject server = new ServerObject();
+ ServiceConfig serviceConfig = Settings.GenerateServiceConfig(PublicIPAddress?.ToString() ?? "localhost", apiKey:false);
+ server.ApiServiceHost = serviceConfig.ApiServiceHost;
+ server.LoginServiceHost = serviceConfig.LoginServiceHost;
+ server.ConfigServiceHost = serviceConfig.ConfigServiceHost;
+ server.MatchingServiceHost = serviceConfig.MatchingServiceHost;
+ server.ServerDbHost = serviceConfig.ServerDBServiceHost;
+ server.TransactionServiceHost = serviceConfig.TransactionServiceHost;
+ server.Ip = PublicIPAddress?.ToString() ?? "localhost";
+ server.Online = true;
+ _ = Task.Run(() => apiManager?.Server.EditServer(server, server.Ip));
+
// Enter a loop to accept new web socket connections.
try
{
@@ -297,8 +319,20 @@ public async Task Start(CancellationTokenSource? cancellationTokenSource = null)
/// Stops the server and its underlying services.
/// Note: Servers are run in another task. This method may return before the server has stopped.
///
- public void Stop()
+ public async void Stop()
{
+ ServerObject server = new ServerObject();
+ server.Ip = PublicIPAddress?.ToString() ?? "localhost";
+ server.Online = false;
+ _ = Task.Run(() => apiManager?.Server.EditServer(server, server.Ip));
+ apiManager.peerStatsObject.ServerIp = PublicIPAddress?.ToString();
+ apiManager.peerStatsObject.Login = 0;
+ apiManager.peerStatsObject.Matching = 0;
+ apiManager.peerStatsObject.Config = 0;
+ apiManager.peerStatsObject.Transaction = 0;
+ apiManager.peerStatsObject.ServerDb = 0;
+ await apiManager.PeerStats.EditPeerStats(apiManager.peerStatsObject,
+ PublicIPAddress?.ToString() ?? "localhost");
// Cancel any cancellation token we have now.
_cancellationTokenSource?.Cancel();
}
diff --git a/EchoRelay.Core/Server/ServerSettings.cs b/EchoRelay.Core/Server/ServerSettings.cs
index 85961a3..370fd26 100644
--- a/EchoRelay.Core/Server/ServerSettings.cs
+++ b/EchoRelay.Core/Server/ServerSettings.cs
@@ -111,15 +111,19 @@ public ServerSettings(ushort port = 777, string apiServicePath = "/api", string
/// The address or domain to use for the base URL for all host endpoints.
/// Indicates whether sensitive gameserver-only fields should be included in the config.
/// The publisher/environment lock to generate with.
+ /// Used to generate a config without API key
+
/// Returns the generated .
- public ServiceConfig GenerateServiceConfig(string address, bool serverConfig = true, string publisherLock = "rad15_live", string? serverPlugin = null)
+ public ServiceConfig GenerateServiceConfig(string address, bool serverConfig = true, string publisherLock = "rad15_live", string? serverPlugin = null, bool? apiKey = null)
{
// Obtain our base host
string webSocketHost = $"ws://{address}:{Port}";
string httpHost = $"http://{address}:{Port}";
+ string serverDBHostNoKey;
// Construct our ServerDB path
string serverDBHost = webSocketHost + ServerDBServicePath;
+ serverDBHostNoKey = serverDBHost;
if (ServerDBApiKey != null)
{
serverDBHost += $"?api_key={HttpUtility.UrlEncode(ServerDBApiKey)}";
@@ -131,7 +135,7 @@ public ServiceConfig GenerateServiceConfig(string address, bool serverConfig = t
configServiceHost: webSocketHost + ConfigServicePath,
loginServiceHost: webSocketHost + LoginServicePath + $"?auth=AccountPassword&displayname=AccountName",
matchingServiceHost: webSocketHost + MatchingServicePath,
- serverdbServiceHost: serverConfig ? serverDBHost : null,
+ serverdbServiceHost: apiKey == null ? (serverConfig ? serverDBHost : null) : serverDBHostNoKey,
transactionServiceHost: webSocketHost + TransactionServicePath,
publisherLock: publisherLock,
serverPlugin: serverPlugin
diff --git a/EchoRelay.Core/Server/Services/ServerDB/GameServerRegistry.cs b/EchoRelay.Core/Server/Services/ServerDB/GameServerRegistry.cs
index 3899a3d..5ea6d32 100644
--- a/EchoRelay.Core/Server/Services/ServerDB/GameServerRegistry.cs
+++ b/EchoRelay.Core/Server/Services/ServerDB/GameServerRegistry.cs
@@ -2,6 +2,7 @@
using EchoRelay.Core.Server.Messages.ServerDB;
using EchoRelay.Core.Utils;
using System.Collections.Concurrent;
+using EchoRelay.Core.Monitoring;
using static EchoRelay.Core.Server.Messages.ServerDB.ERGameServerStartSession;
namespace EchoRelay.Core.Server.Services.ServerDB
@@ -28,6 +29,8 @@ public class GameServerRegistry
/// Event of a game server being unregistered.
///
public event GameServerRegistrationChangedEventHandler? OnGameServerUnregistered;
+
+ public ApiManager apiManager;
#endregion
#region Constructor
@@ -35,6 +38,7 @@ public GameServerRegistry()
{
RegisteredGameServers = new ConcurrentDictionary();
RegisteredGameServersBySessionId = new ConcurrentDictionary();
+ apiManager = ApiManager.Instance;
}
#endregion
@@ -43,9 +47,22 @@ public RegisteredGameServer AddGameServer(RegisteredGameServer registeredGameSer
{
// Add the game server to our lookup
RegisteredGameServers[registeredGameServer.ServerId] = registeredGameServer;
+
// Fire the relevant event for the game server being registered.
OnGameServerRegistered?.Invoke(registeredGameServer);
+
+ GameServerObject gameServerObject = new GameServerObject();
+ gameServerObject.ServerIp = registeredGameServer.Server.PublicIPAddress?.ToString();
+ gameServerObject.Region = "";
+ gameServerObject.SessionId = registeredGameServer.SessionId.ToString();
+ gameServerObject.GameServerId = registeredGameServer.ServerId;
+ gameServerObject.Assigned = false;
+ gameServerObject.GameMode = "";
+ gameServerObject.Public = registeredGameServer.SessionLobbyType == LobbyType.Public;
+ gameServerObject.Level = "";
+ gameServerObject.PlayerCount = registeredGameServer.SessionPlayerCount;
+ Task.Run(() => apiManager.GameServer.AddGameServerAsync(gameServerObject));
return registeredGameServer;
}
@@ -63,6 +80,7 @@ public RegisteredGameServer AddGameServer(RegisteredGameServer registeredGameSer
public void RemoveGameServer(RegisteredGameServer registeredGameServer)
{
+ Task.Run(() => apiManager.GameServer.DeleteGameServerAsync(registeredGameServer.ServerId.ToString()));
RemoveGameServer(registeredGameServer.ServerId);
}
public void RemoveGameServer(ulong serverId)
diff --git a/EchoRelay.Core/Server/Services/ServerDB/RegisteredGameServer.cs b/EchoRelay.Core/Server/Services/ServerDB/RegisteredGameServer.cs
index 83b23f2..410f1ac 100644
--- a/EchoRelay.Core/Server/Services/ServerDB/RegisteredGameServer.cs
+++ b/EchoRelay.Core/Server/Services/ServerDB/RegisteredGameServer.cs
@@ -7,6 +7,7 @@
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
+using EchoRelay.Core.Monitoring;
namespace EchoRelay.Core.Server.Services.ServerDB
{
@@ -22,6 +23,8 @@ public class RegisteredGameServer
/// The parent which the is registered to.
///
private GameServerRegistry Registry { get; }
+
+ public ApiManager apiManager { get; }
///
/// The registration request provided by the game server initially.
@@ -203,6 +206,7 @@ public RegisteredGameServer(GameServerRegistry registry, Peer peer, ERGameServer
SessionPlayerLimits = GameTypePlayerLimits.DefaultLimits;
_playerSessions = new Dictionary();
_accessLock = new AsyncLock();
+ apiManager = ApiManager.Instance;
}
#endregion
@@ -288,6 +292,18 @@ private async Task StartSessionInternal(XPlatformId requester, ERGameServerStart
// Send the start session message to the game server.
await Peer.Send(new ERGameServerStartSession(SessionId.Value, SessionChannel.Value, (byte)SessionPlayerLimits.TotalPlayerLimit, SessionLobbyType, mergedSessionSettings, entrantDescriptors.ToArray()));
+ GameServerObject gameServerObject = new GameServerObject();
+ gameServerObject.ServerIp = Server.PublicIPAddress?.ToString();
+ gameServerObject.Region = Server.SymbolCache.GetName(RegionSymbol);
+ gameServerObject.SessionId = SessionId.ToString();
+ gameServerObject.Assigned = true;
+ gameServerObject.GameServerId = ServerId;
+ gameServerObject.GameMode = Server.SymbolCache.GetName(SessionGameTypeSymbol.Value);
+ gameServerObject.Public = SessionLobbyType == ERGameServerStartSession.LobbyType.Public;
+ gameServerObject.Level = Server.SymbolCache.GetName(SessionLevelSymbol.Value);
+ gameServerObject.PlayerCount = SessionPlayerCount;
+ _ = Task.Run(() => apiManager.GameServer.EditGameServer(gameServerObject, ServerId.ToString()));
+
// Add the new session id to the parent registry's lookup.
Registry.RegisteredGameServersBySessionId[SessionId.Value] = this;
}
@@ -512,7 +528,15 @@ await _accessLock.ExecuteLocked(async () =>
// Fire the event for players being added
if(addedPlayersInfo.Length > 0)
+ {
OnPlayersAdded?.Invoke(this, addedPlayersInfo);
+ GameServerObject gameServerObject = new GameServerObject();
+ gameServerObject.ServerIp = Server.PublicIPAddress?.ToString();
+ gameServerObject.Assigned = true;
+ gameServerObject.PlayerCount = SessionPlayerCount;
+ _ = Task.Run(() => apiManager.GameServer.EditGameServer(gameServerObject, ServerId.ToString()));
+
+ }
}
public async Task KickPlayer(Guid playerSession)
@@ -544,7 +568,12 @@ await _accessLock.ExecuteLocked(() =>
});
// Fire the event for a player being removed
- OnPlayerRemoved?.Invoke(this, playerSession, peer);
+ OnPlayerRemoved?.Invoke(this, playerSession, peer);
+ GameServerObject gameServerObject = new GameServerObject();
+ gameServerObject.ServerIp = Server.PublicIPAddress?.ToString();
+ gameServerObject.Assigned = true;
+ gameServerObject.PlayerCount = SessionPlayerCount;
+ _ = Task.Run(() => apiManager.GameServer.EditGameServer(gameServerObject, ServerId.ToString()));
}
public async Task EndSession()
@@ -566,6 +595,20 @@ await _accessLock.ExecuteLocked(() => {
// Fire the event for the session ending.
OnSessionStateChanged?.Invoke(this);
+
+ GameServerObject gameServerObject = new GameServerObject();
+ gameServerObject.ServerIp = Server.PublicIPAddress?.ToString();
+ gameServerObject.Region = RegionSymbol.ToString();
+ gameServerObject.SessionId = "";
+ gameServerObject.Assigned = false;
+ gameServerObject.GameServerId = ServerId;
+ gameServerObject.GameMode = "";
+ gameServerObject.Public = SessionLobbyType == ERGameServerStartSession.LobbyType.Public;
+ gameServerObject.Level = "";
+ gameServerObject.PlayerCount = SessionPlayerCount;
+
+ _ = Task.Run(() => apiManager.GameServer.EditGameServer(gameServerObject, ServerId.ToString()));
+
}
#endregion
}