diff --git a/Giants.Services/Core/Entities/ServerInfo.cs b/Giants.Services/Core/Entities/ServerInfo.cs index 4fa3530..6072e23 100644 --- a/Giants.Services/Core/Entities/ServerInfo.cs +++ b/Giants.Services/Core/Entities/ServerInfo.cs @@ -4,7 +4,7 @@ public class ServerInfo : DataContract.V1.ServerInfo, IIdentifiable { - public string id => this.HostIpAddress; + public string id => $"{this.HostIpAddress}-{this.GameName}-{this.Port}"; public string DocumentType => nameof(ServerInfo); @@ -17,6 +17,8 @@ return obj is ServerInfo info && base.Equals(obj) && this.HostIpAddress == info.HostIpAddress && + this.Port == info.Port && + this.GameName == info.GameName && this.DocumentType == info.DocumentType; } diff --git a/Giants.Services/Services/ServerRegistryService.cs b/Giants.Services/Services/ServerRegistryService.cs index b38962f..b8d11f5 100644 --- a/Giants.Services/Services/ServerRegistryService.cs +++ b/Giants.Services/Services/ServerRegistryService.cs @@ -10,12 +10,12 @@ public class ServerRegistryService : IServerRegistryService { - private static readonly string[] SupportedGameNames = new[] { "Giants", "Giants Beta" }; + private static readonly string[] SupportedGameNames = new[] { "Giants", "Giants Beta", "Giants Beta Dedicated", "Giants Dedicated" }; private readonly ILogger logger; private readonly IServerRegistryStore registryStore; private readonly IConfiguration configuration; private readonly int maxServerCount; - private readonly int maxServersPerIpGame; + private readonly int maxServersPerIp; public ServerRegistryService( ILogger logger, @@ -26,7 +26,7 @@ this.registryStore = registryStore; this.configuration = configuration; this.maxServerCount = Convert.ToInt32(this.configuration["MaxServerCount"]); - this.maxServersPerIpGame = Convert.ToInt32(this.configuration["MaxServersPerIpGame"]); + this.maxServersPerIp = Convert.ToInt32(this.configuration["MaxServersPerIp"]); } public async Task AddServer( @@ -35,13 +35,15 @@ ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo)); ArgumentUtility.CheckStringForNullOrEmpty(serverInfo.HostIpAddress, nameof(serverInfo.HostIpAddress)); + string gameName = serverInfo.GameName.Replace("Dedicated", string.Empty).Trim(); + serverInfo.GameName = gameName; if (!SupportedGameNames.Contains(serverInfo.GameName, StringComparer.OrdinalIgnoreCase)) { throw new ArgumentException($"Unsupported game name {serverInfo.GameName}", nameof(serverInfo)); } var existingServers = await this.registryStore.GetServerInfos(whereExpression: x => x.HostIpAddress == serverInfo.HostIpAddress); - if (existingServers.GroupBy(g => g.GameName).Any(g => g.Count() > this.maxServersPerIpGame)) + if (existingServers.GroupBy(g => new { g.HostIpAddress }).Any(g => g.Count() > this.maxServersPerIp)) { throw new InvalidOperationException("Exceeded maximum servers per IP."); } diff --git a/Giants.Services/Store/CosmosDbServerRegistryStore.cs b/Giants.Services/Store/CosmosDbServerRegistryStore.cs index ac8ba08..9a0c746 100644 --- a/Giants.Services/Store/CosmosDbServerRegistryStore.cs +++ b/Giants.Services/Store/CosmosDbServerRegistryStore.cs @@ -99,7 +99,8 @@ if (allServerInfo.ContainsKey(serverInfo.HostIpAddress)) { - ServerInfo existingServerInfo = FindExistingServerForHostIp(allServerInfo[serverInfo.HostIpAddress], serverInfo); + IList serverInfoForHostIp = allServerInfo[serverInfo.HostIpAddress]; + ServerInfo existingServerInfo = FindExistingServerForHostIp(serverInfoForHostIp, serverInfo); // DDOS protection: skip write to Cosmos if parameters have not changed, // or it's not been long enough. @@ -108,24 +109,35 @@ this.logger.LogInformation("State for {IPAddress} is unchanged. Skipping write to store.", serverInfo.HostIpAddress); return; } + + this.logger.LogInformation("State for {IPAddress} has changed. Committing update to store.", serverInfo.HostIpAddress); + await this.client.UpsertItem( + item: serverInfo, + partitionKey: new PartitionKey(serverInfo.DocumentType)); + + // Update cache + if (existingServerInfo != null) + { + var newServerInfo = serverInfoForHostIp.Where(s => !s.Equals(serverInfo)).ToList(); + newServerInfo.Add(serverInfo); + allServerInfo[serverInfo.HostIpAddress] = newServerInfo; + + this.logger.LogInformation("Updating cache for request from {IPAddress} (replaced existing server).", serverInfo.HostIpAddress); + } else { - this.logger.LogInformation("State for {IPAddress} has changed. Committing update to store.", serverInfo.HostIpAddress); + allServerInfo[serverInfo.HostIpAddress].Add(serverInfo); + this.logger.LogInformation("Updating cache for request from {IPAddress} (added new server).", serverInfo.HostIpAddress); } } - - await this.client.UpsertItem( - item: serverInfo, - partitionKey: new PartitionKey(serverInfo.DocumentType)); - - this.logger.LogInformation("Updating cache for request from {IPAddress}.", serverInfo.HostIpAddress); - - if (allServerInfo.ContainsKey(serverInfo.HostIpAddress)) - { - allServerInfo[serverInfo.HostIpAddress].Add(serverInfo); - } else { + // Update cache + await this.client.UpsertItem( + item: serverInfo, + partitionKey: new PartitionKey(serverInfo.DocumentType)); + + this.logger.LogInformation("Updating cache for request from {IPAddress} (no existing servers).", serverInfo.HostIpAddress); allServerInfo.TryAdd(serverInfo.HostIpAddress, new List() { serverInfo }); } } @@ -199,15 +211,7 @@ private static ServerInfo FindExistingServerForHostIp(IList serverInfoForHostIp, ServerInfo candidateServerInfo) { - foreach (var existingServerInfo in serverInfoForHostIp) - { - if (existingServerInfo.Equals(candidateServerInfo)) - { - return existingServerInfo; - } - } - - return null; + return serverInfoForHostIp.FirstOrDefault(s => s.Equals(candidateServerInfo)); } } } \ No newline at end of file diff --git a/Giants.WebApi/appsettings.json b/Giants.WebApi/appsettings.json index fc6e249..f82cc71 100644 --- a/Giants.WebApi/appsettings.json +++ b/Giants.WebApi/appsettings.json @@ -12,7 +12,7 @@ "ServerTimeoutPeriodInMinutes": "7", "ServerCleanupIntervalInMinutes": "1", "MaxServerCount": 1000, - "MaxServersPerIpGame": 5, + "MaxServersPerIp": 5, "DiscordUri": "https://discord.gg/Avj4azU", "BlobConnectionString": "", "CrashBlobContainerName": "crashes"