mirror of
https://github.com/ncblakely/GiantsTools
synced 2024-11-24 15:15:37 +01:00
Merge pull request #4 from ncblakely/users/tos/merge
Merge missing commits from 1.5.0.x branch.
This commit is contained in:
commit
05490c58bc
@ -16,7 +16,6 @@ namespace Giants.Launcher
|
|||||||
public partial class LauncherForm : Form
|
public partial class LauncherForm : Form
|
||||||
{
|
{
|
||||||
// Constant settings
|
// Constant settings
|
||||||
private const string GameName = "Giants: Citizen Kabuto";
|
|
||||||
private const string GamePath = "GiantsMain.exe";
|
private const string GamePath = "GiantsMain.exe";
|
||||||
private const string RegistryKey = @"HKEY_CURRENT_USER\Software\PlanetMoon\Giants";
|
private const string RegistryKey = @"HKEY_CURRENT_USER\Software\PlanetMoon\Giants";
|
||||||
private const string RegistryValue = "DestDir";
|
private const string RegistryValue = "DestDir";
|
||||||
@ -38,7 +37,7 @@ namespace Giants.Launcher
|
|||||||
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
|
||||||
|
|
||||||
// Set window title
|
// Set window title
|
||||||
this.Text = GameName;
|
this.SetTitle();
|
||||||
|
|
||||||
// Read newer file-based game settings
|
// Read newer file-based game settings
|
||||||
this.config = new Config();
|
this.config = new Config();
|
||||||
@ -97,7 +96,7 @@ namespace Giants.Launcher
|
|||||||
|
|
||||||
private void btnOptions_Click(object sender, EventArgs e)
|
private void btnOptions_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
OptionsForm form = new OptionsForm(GameName + " Options", this.gamePath);
|
OptionsForm form = new OptionsForm(Resources.AppName + " Options", this.gamePath);
|
||||||
|
|
||||||
form.StartPosition = FormStartPosition.CenterParent;
|
form.StartPosition = FormStartPosition.CenterParent;
|
||||||
form.ShowDialog();
|
form.ShowDialog();
|
||||||
@ -116,7 +115,7 @@ namespace Giants.Launcher
|
|||||||
|
|
||||||
if (this.gamePath == null || !File.Exists(this.gamePath))
|
if (this.gamePath == null || !File.Exists(this.gamePath))
|
||||||
{
|
{
|
||||||
string message = string.Format(Resources.AppNotFound, GameName);
|
string message = string.Format(Resources.AppNotFound, Resources.AppName);
|
||||||
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
return;
|
return;
|
||||||
@ -131,7 +130,7 @@ namespace Giants.Launcher
|
|||||||
Version gameVersion = VersionHelper.GetGameVersion(this.gamePath);
|
Version gameVersion = VersionHelper.GetGameVersion(this.gamePath);
|
||||||
if (gameVersion == null)
|
if (gameVersion == null)
|
||||||
{
|
{
|
||||||
string message = string.Format(Resources.AppNotFound, GameName);
|
string message = string.Format(Resources.AppNotFound, Resources.AppName);
|
||||||
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||||
Application.Exit();
|
Application.Exit();
|
||||||
}
|
}
|
||||||
@ -330,6 +329,21 @@ namespace Giants.Launcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
Process.Start(this.communityAppUri);
|
Process.Start(this.communityAppUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTitle()
|
||||||
|
{
|
||||||
|
string title = Resources.AppName;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
title += " DEBUG";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BETA
|
||||||
|
title += " BETA";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this.Text = title;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ namespace Giants.Launcher
|
|||||||
{
|
{
|
||||||
// Must come first as other options depend on it
|
// Must come first as other options depend on it
|
||||||
this.PopulateRenderers();
|
this.PopulateRenderers();
|
||||||
|
this.SetRenderer();
|
||||||
|
|
||||||
this.PopulateResolution();
|
this.PopulateResolution();
|
||||||
this.PopulateAnisotropy();
|
this.PopulateAnisotropy();
|
||||||
@ -40,6 +41,24 @@ namespace Giants.Launcher
|
|||||||
.ToArray());
|
.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetRenderer()
|
||||||
|
{
|
||||||
|
string selectedRenderer = GameSettings.Get<string>(RegistryKeys.Renderer);
|
||||||
|
RendererInfo renderer =
|
||||||
|
GameSettings.CompatibleRenderers.Find(
|
||||||
|
r => Path.GetFileName(r.FilePath).Equals(selectedRenderer, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (renderer != null)
|
||||||
|
{
|
||||||
|
this.cmbRenderer.SelectedItem = renderer;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
renderer = GameSettings.CompatibleRenderers.Find(r => r.FileName == "gg_dx7r.dll");
|
||||||
|
this.cmbRenderer.SelectedItem = renderer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void SetOptions()
|
private void SetOptions()
|
||||||
{
|
{
|
||||||
var resolutions = (List<ScreenResolution>)this.cmbResolution.DataSource;
|
var resolutions = (List<ScreenResolution>)this.cmbResolution.DataSource;
|
||||||
@ -53,19 +72,6 @@ namespace Giants.Launcher
|
|||||||
this.cmbAntialiasing.SelectedIndex = 0;
|
this.cmbAntialiasing.SelectedIndex = 0;
|
||||||
|
|
||||||
this.chkUpdates.Checked = GameSettings.Get<int>(RegistryKeys.NoAutoUpdate) != 1;
|
this.chkUpdates.Checked = GameSettings.Get<int>(RegistryKeys.NoAutoUpdate) != 1;
|
||||||
|
|
||||||
RendererInfo renderer = GameSettings.CompatibleRenderers.Find(
|
|
||||||
r => StringComparer.OrdinalIgnoreCase.Compare(Path.GetFileName(r.FilePath), GameSettings.Get<string>(RegistryKeys.Renderer)) == 0);
|
|
||||||
|
|
||||||
if (renderer != null)
|
|
||||||
{
|
|
||||||
this.cmbRenderer.SelectedItem = renderer;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
renderer = GameSettings.CompatibleRenderers.Find(r => r.FileName == "gg_dx7r.dll");
|
|
||||||
this.cmbRenderer.SelectedItem = renderer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PopulateAntialiasing()
|
private void PopulateAntialiasing()
|
||||||
|
@ -6,9 +6,13 @@ namespace Giants.Launcher
|
|||||||
{
|
{
|
||||||
static class NativeMethods
|
static class NativeMethods
|
||||||
{
|
{
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static extern bool SetDllDirectory(string lpPathName);
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll")]
|
||||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||||
|
|
||||||
|
@ -58,6 +58,8 @@ namespace Giants.Launcher
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
NativeMethods.SetDllDirectory(Environment.CurrentDirectory);
|
||||||
|
|
||||||
// Make interop call to native renderer DLLs to get capability info
|
// Make interop call to native renderer DLLs to get capability info
|
||||||
var interopCaps = new RendererInterop.GFXCapabilityInfo();
|
var interopCaps = new RendererInterop.GFXCapabilityInfo();
|
||||||
string path = Path.Combine(file.DirectoryName, file.Name);
|
string path = Path.Combine(file.DirectoryName, file.Name);
|
||||||
|
9
Giants.Launcher/Properties/Resources.Designer.cs
generated
9
Giants.Launcher/Properties/Resources.Designer.cs
generated
@ -60,6 +60,15 @@ namespace Giants.Launcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Giants: Citizen Kabuto.
|
||||||
|
/// </summary>
|
||||||
|
internal static string AppName {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AppName", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Could not locate an installation of {0}. The launcher will now exit..
|
/// Looks up a localized string similar to Could not locate an installation of {0}. The launcher will now exit..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -205,4 +205,7 @@
|
|||||||
<data name="ErrorNoConfigFile" xml:space="preserve">
|
<data name="ErrorNoConfigFile" xml:space="preserve">
|
||||||
<value>Settings file {0} was not found.</value>
|
<value>Settings file {0} was not found.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AppName" xml:space="preserve">
|
||||||
|
<value>Giants: Citizen Kabuto</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
public class ServerInfo : DataContract.V1.ServerInfo, IIdentifiable
|
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);
|
public string DocumentType => nameof(ServerInfo);
|
||||||
|
|
||||||
@ -17,6 +17,8 @@
|
|||||||
return obj is ServerInfo info &&
|
return obj is ServerInfo info &&
|
||||||
base.Equals(obj) &&
|
base.Equals(obj) &&
|
||||||
this.HostIpAddress == info.HostIpAddress &&
|
this.HostIpAddress == info.HostIpAddress &&
|
||||||
|
this.Port == info.Port &&
|
||||||
|
this.GameName == info.GameName &&
|
||||||
this.DocumentType == info.DocumentType;
|
this.DocumentType == info.DocumentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
{
|
{
|
||||||
Task DeleteServer(string ipAddress);
|
Task DeleteServer(string ipAddress);
|
||||||
|
|
||||||
|
Task DeleteServer(string ipAddress, string gameName, int port);
|
||||||
|
|
||||||
Task<IEnumerable<ServerInfo>> GetAllServers();
|
Task<IEnumerable<ServerInfo>> GetAllServers();
|
||||||
|
|
||||||
Task AddServer(ServerInfo server);
|
Task AddServer(ServerInfo server);
|
||||||
|
@ -4,16 +4,18 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
public class ServerRegistryService : IServerRegistryService
|
public class ServerRegistryService : IServerRegistryService
|
||||||
{
|
{
|
||||||
private static readonly string[] SupportedGameNames = new[] { "Giants" };
|
private static readonly string[] SupportedGameNames = new[] { "Giants", "Giants Beta", "Giants Beta Dedicated", "Giants Dedicated" };
|
||||||
private readonly ILogger<ServerRegistryService> logger;
|
private readonly ILogger<ServerRegistryService> logger;
|
||||||
private readonly IServerRegistryStore registryStore;
|
private readonly IServerRegistryStore registryStore;
|
||||||
private readonly IConfiguration configuration;
|
private readonly IConfiguration configuration;
|
||||||
private readonly int maxServerCount;
|
private readonly int maxServerCount;
|
||||||
|
private readonly int maxServersPerIp;
|
||||||
|
|
||||||
public ServerRegistryService(
|
public ServerRegistryService(
|
||||||
ILogger<ServerRegistryService> logger,
|
ILogger<ServerRegistryService> logger,
|
||||||
@ -24,6 +26,7 @@
|
|||||||
this.registryStore = registryStore;
|
this.registryStore = registryStore;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.maxServerCount = Convert.ToInt32(this.configuration["MaxServerCount"]);
|
this.maxServerCount = Convert.ToInt32(this.configuration["MaxServerCount"]);
|
||||||
|
this.maxServersPerIp = Convert.ToInt32(this.configuration["MaxServersPerIp"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task AddServer(
|
public async Task AddServer(
|
||||||
@ -32,11 +35,19 @@
|
|||||||
ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo));
|
ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo));
|
||||||
ArgumentUtility.CheckStringForNullOrEmpty(serverInfo.HostIpAddress, nameof(serverInfo.HostIpAddress));
|
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))
|
if (!SupportedGameNames.Contains(serverInfo.GameName, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
throw new ArgumentException($"Unsupported game name {serverInfo.GameName}", nameof(serverInfo));
|
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 => new { g.HostIpAddress }).Any(g => g.Count() > this.maxServersPerIp))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Exceeded maximum servers per IP.");
|
||||||
|
}
|
||||||
|
|
||||||
await this.registryStore.UpsertServerInfo(serverInfo);
|
await this.registryStore.UpsertServerInfo(serverInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,16 +57,36 @@
|
|||||||
.Take(this.maxServerCount);
|
.Take(this.maxServerCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Old API, soon to be deprecated
|
||||||
public async Task DeleteServer(string ipAddress)
|
public async Task DeleteServer(string ipAddress)
|
||||||
{
|
{
|
||||||
ArgumentUtility.CheckStringForNullOrEmpty(ipAddress, nameof(ipAddress));
|
ArgumentUtility.CheckStringForNullOrEmpty(ipAddress, nameof(ipAddress));
|
||||||
|
|
||||||
ServerInfo serverInfo = await this.registryStore.GetServerInfo(ipAddress);
|
var serverInfos = await this.registryStore.GetServerInfos(whereExpression: x => x.HostIpAddress == ipAddress);
|
||||||
|
|
||||||
if (serverInfo != null)
|
foreach (var serverInfo in serverInfos)
|
||||||
{
|
{
|
||||||
await this.registryStore.DeleteServer(serverInfo.id);
|
await this.registryStore.DeleteServer(serverInfo.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task DeleteServer(string ipAddress, string gameName, int port)
|
||||||
|
{
|
||||||
|
ArgumentUtility.CheckStringForNullOrEmpty(ipAddress, nameof(ipAddress));
|
||||||
|
ArgumentUtility.CheckStringForNullOrEmpty(gameName, nameof(gameName));
|
||||||
|
ArgumentUtility.CheckForNonnegativeInt(port, nameof(port));
|
||||||
|
|
||||||
|
var existingServerInfo = (await this.registryStore.GetServerInfos(
|
||||||
|
whereExpression:
|
||||||
|
x => x.HostIpAddress == ipAddress &&
|
||||||
|
x.Port == port &&
|
||||||
|
x.GameName.Equals(gameName, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (existingServerInfo != null)
|
||||||
|
{
|
||||||
|
await this.registryStore.DeleteServer(existingServerInfo.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,10 +41,11 @@
|
|||||||
bool includeExpired = false,
|
bool includeExpired = false,
|
||||||
string partitionKey = null)
|
string partitionKey = null)
|
||||||
{
|
{
|
||||||
ConcurrentDictionary<string, ServerInfo> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
ConcurrentDictionary<string, IList<ServerInfo>> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
||||||
|
|
||||||
IQueryable<ServerInfo> serverInfoQuery = serverInfo
|
IQueryable<ServerInfo> serverInfoQuery = serverInfo
|
||||||
.Values
|
.Values
|
||||||
|
.SelectMany(s => s)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
if (whereExpression != null)
|
if (whereExpression != null)
|
||||||
@ -67,10 +68,11 @@
|
|||||||
Expression<Func<ServerInfo, bool>> whereExpression = null,
|
Expression<Func<ServerInfo, bool>> whereExpression = null,
|
||||||
string partitionKey = null)
|
string partitionKey = null)
|
||||||
{
|
{
|
||||||
ConcurrentDictionary<string, ServerInfo> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
ConcurrentDictionary<string, IList<ServerInfo>> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
||||||
|
|
||||||
IQueryable<ServerInfo> serverInfoQuery = serverInfo
|
IQueryable<ServerInfo> serverInfoQuery = serverInfo
|
||||||
.Values
|
.Values
|
||||||
|
.SelectMany(s => s)
|
||||||
.AsQueryable();
|
.AsQueryable();
|
||||||
|
|
||||||
if (serverInfoQuery != null)
|
if (serverInfoQuery != null)
|
||||||
@ -88,65 +90,55 @@
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ServerInfo> GetServerInfo(string ipAddress)
|
|
||||||
{
|
|
||||||
ArgumentUtility.CheckStringForNullOrEmpty(ipAddress, nameof(ipAddress));
|
|
||||||
|
|
||||||
ConcurrentDictionary<string, ServerInfo> serverInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
|
||||||
if (serverInfo.ContainsKey(ipAddress))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return serverInfo[ipAddress];
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
this.logger.LogInformation("Cached server for {IPAddress} no longer found: {Exception}", ipAddress, e.ToString());
|
|
||||||
// May have been removed from the cache by another thread. Ignore and query DB instead.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await this.client.GetItemById<ServerInfo>(ipAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpsertServerInfo(ServerInfo serverInfo)
|
public async Task UpsertServerInfo(ServerInfo serverInfo)
|
||||||
{
|
{
|
||||||
ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo));
|
ArgumentUtility.CheckForNull(serverInfo, nameof(serverInfo));
|
||||||
|
|
||||||
// Check cache before we write to store
|
// Check cache before we write to store
|
||||||
ConcurrentDictionary<string, ServerInfo> allServerInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
ConcurrentDictionary<string, IList<ServerInfo>> allServerInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
||||||
|
|
||||||
if (allServerInfo.ContainsKey(serverInfo.HostIpAddress))
|
if (allServerInfo.ContainsKey(serverInfo.HostIpAddress))
|
||||||
{
|
{
|
||||||
ServerInfo existingServerInfo = allServerInfo[serverInfo.HostIpAddress];
|
IList<ServerInfo> serverInfoForHostIp = allServerInfo[serverInfo.HostIpAddress];
|
||||||
|
ServerInfo existingServerInfo = FindExistingServerForHostIp(serverInfoForHostIp, serverInfo);
|
||||||
|
|
||||||
// DDOS protection: skip write to Cosmos if parameters have not changed,
|
// DDOS protection: skip write to Cosmos if parameters have not changed,
|
||||||
// or it's not been long enough.
|
// or it's not been long enough.
|
||||||
if (existingServerInfo.Equals(serverInfo)
|
if (existingServerInfo != null && Math.Abs((existingServerInfo.LastHeartbeat - serverInfo.LastHeartbeat).TotalMinutes) < ServerRefreshIntervalInMinutes)
|
||||||
&& Math.Abs((existingServerInfo.LastHeartbeat - serverInfo.LastHeartbeat).TotalMinutes) < ServerRefreshIntervalInMinutes)
|
|
||||||
{
|
{
|
||||||
this.logger.LogInformation("State for {IPAddress} is unchanged. Skipping write to store.", serverInfo.HostIpAddress);
|
this.logger.LogInformation("State for {IPAddress} is unchanged. Skipping write to store.", serverInfo.HostIpAddress);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
this.logger.LogInformation("State for {IPAddress} has changed. Committing update to store.", serverInfo.HostIpAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
this.logger.LogInformation("State for {IPAddress} has changed. Committing update to store.", serverInfo.HostIpAddress);
|
||||||
await this.client.UpsertItem(
|
await this.client.UpsertItem(
|
||||||
item: serverInfo,
|
item: serverInfo,
|
||||||
partitionKey: new PartitionKey(serverInfo.DocumentType));
|
partitionKey: new PartitionKey(serverInfo.DocumentType));
|
||||||
|
|
||||||
this.logger.LogInformation("Updating cache for request from {IPAddress}.", serverInfo.HostIpAddress);
|
// Update cache
|
||||||
|
if (existingServerInfo != null)
|
||||||
if (allServerInfo.ContainsKey(serverInfo.HostIpAddress))
|
|
||||||
{
|
{
|
||||||
allServerInfo[serverInfo.HostIpAddress] = serverInfo;
|
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
|
else
|
||||||
{
|
{
|
||||||
allServerInfo.TryAdd(serverInfo.HostIpAddress, serverInfo);
|
allServerInfo[serverInfo.HostIpAddress].Add(serverInfo);
|
||||||
|
this.logger.LogInformation("Updating cache for request from {IPAddress} (added new server).", serverInfo.HostIpAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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>() { serverInfo });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,8 +147,21 @@
|
|||||||
await this.client.DeleteItem<ServerInfo>(id, partitionKey);
|
await this.client.DeleteItem<ServerInfo>(id, partitionKey);
|
||||||
|
|
||||||
// Remove from cache
|
// Remove from cache
|
||||||
ConcurrentDictionary<string, ServerInfo> allServerInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
ConcurrentDictionary<string, IList<ServerInfo>> allServerInfo = await this.memoryCache.GetOrCreateAsync(CacheKeys.ServerInfo, this.PopulateCache);
|
||||||
allServerInfo.TryRemove(id, out ServerInfo _);
|
if (allServerInfo.ContainsKey(id))
|
||||||
|
{
|
||||||
|
var serverInfoCopy = allServerInfo[id].Where(s => s.id != id).ToList();
|
||||||
|
if (!serverInfoCopy.Any())
|
||||||
|
{
|
||||||
|
// No remaining servers, remove the key
|
||||||
|
allServerInfo.TryRemove(id, out IList<ServerInfo> _);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Shallow-copy and replace to keep thread safety guarantee
|
||||||
|
allServerInfo[id] = serverInfoCopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task DeleteServers(IEnumerable<string> ids, string partitionKey = null)
|
public async Task DeleteServers(IEnumerable<string> ids, string partitionKey = null)
|
||||||
@ -182,15 +187,31 @@
|
|||||||
await this.client.Initialize();
|
await this.client.Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ConcurrentDictionary<string, ServerInfo>> PopulateCache(ICacheEntry entry)
|
private async Task<ConcurrentDictionary<string, IList<ServerInfo>>> PopulateCache(ICacheEntry entry)
|
||||||
{
|
{
|
||||||
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
|
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);
|
||||||
|
|
||||||
IDictionary<string, ServerInfo> serverInfo =
|
var allServerInfo = (await this.client.GetItems<ServerInfo>());
|
||||||
(await this.client.GetItems<ServerInfo>())
|
var serverInfoDictionary = new ConcurrentDictionary<string, IList<ServerInfo>>();
|
||||||
.ToDictionary(x => x.HostIpAddress, y => y);
|
|
||||||
|
|
||||||
return new ConcurrentDictionary<string, ServerInfo>(serverInfo);
|
foreach (var serverInfo in allServerInfo)
|
||||||
|
{
|
||||||
|
if (!serverInfoDictionary.ContainsKey(serverInfo.HostIpAddress))
|
||||||
|
{
|
||||||
|
serverInfoDictionary[serverInfo.HostIpAddress] = new List<ServerInfo>() { serverInfo };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
serverInfoDictionary[serverInfo.HostIpAddress].Add(serverInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverInfoDictionary;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServerInfo FindExistingServerForHostIp(IList<ServerInfo> serverInfoForHostIp, ServerInfo candidateServerInfo)
|
||||||
|
{
|
||||||
|
return serverInfoForHostIp.FirstOrDefault(s => s.Equals(candidateServerInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -13,8 +13,6 @@
|
|||||||
|
|
||||||
Task DeleteServers(IEnumerable<string> ids, string partitionKey = null);
|
Task DeleteServers(IEnumerable<string> ids, string partitionKey = null);
|
||||||
|
|
||||||
Task<ServerInfo> GetServerInfo(string ipAddress);
|
|
||||||
|
|
||||||
Task<IEnumerable<ServerInfo>> GetServerInfos(Expression<Func<ServerInfo, bool>> whereExpression = null, bool includeExpired = false, string partitionKey = null);
|
Task<IEnumerable<ServerInfo>> GetServerInfos(Expression<Func<ServerInfo, bool>> whereExpression = null, bool includeExpired = false, string partitionKey = null);
|
||||||
|
|
||||||
Task<IEnumerable<TSelect>> GetServerInfos<TSelect>(
|
Task<IEnumerable<TSelect>> GetServerInfos<TSelect>(
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ApiVersion("1.0")]
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class CommunityController : ControllerBase
|
public class CommunityController : ControllerBase
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ApiVersion("1.0")]
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class CrashReportsController : ControllerBase
|
public class CrashReportsController : ControllerBase
|
||||||
{
|
{
|
||||||
|
@ -14,6 +14,7 @@ namespace Giants.Web.Controllers
|
|||||||
{
|
{
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ApiVersion("1.0")]
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class ServersController : ControllerBase
|
public class ServersController : ControllerBase
|
||||||
{
|
{
|
||||||
@ -44,6 +45,17 @@ namespace Giants.Web.Controllers
|
|||||||
await this.serverRegistryService.DeleteServer(requestIpAddress);
|
await this.serverRegistryService.DeleteServer(requestIpAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpDelete]
|
||||||
|
[MapToApiVersion("1.1")]
|
||||||
|
public async Task DeleteServer(string gameName, int port)
|
||||||
|
{
|
||||||
|
string requestIpAddress = this.GetRequestIpAddress();
|
||||||
|
|
||||||
|
this.logger.LogInformation("Deleting server from {IPAddress}", requestIpAddress);
|
||||||
|
|
||||||
|
await this.serverRegistryService.DeleteServer(requestIpAddress, gameName, port);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IEnumerable<ServerInfoWithHostAddress>> GetServers()
|
public async Task<IEnumerable<ServerInfoWithHostAddress>> GetServers()
|
||||||
{
|
{
|
||||||
|
@ -7,6 +7,7 @@ namespace Giants.WebApi.Controllers
|
|||||||
{
|
{
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[ApiVersion("1.0")]
|
[ApiVersion("1.0")]
|
||||||
|
[ApiVersion("1.1")]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class VersionController : ControllerBase
|
public class VersionController : ControllerBase
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"ServerTimeoutPeriodInMinutes": "7",
|
"ServerTimeoutPeriodInMinutes": "7",
|
||||||
"ServerCleanupIntervalInMinutes": "1",
|
"ServerCleanupIntervalInMinutes": "1",
|
||||||
"MaxServerCount": 1000,
|
"MaxServerCount": 1000,
|
||||||
|
"MaxServersPerIp": 5,
|
||||||
"DiscordUri": "https://discord.gg/Avj4azU",
|
"DiscordUri": "https://discord.gg/Avj4azU",
|
||||||
"BlobConnectionString": "",
|
"BlobConnectionString": "",
|
||||||
"CrashBlobContainerName": "crashes"
|
"CrashBlobContainerName": "crashes"
|
||||||
|
@ -82,6 +82,9 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Label="Vcpkg">
|
||||||
|
<VcpkgEnabled>false</VcpkgEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
Binary file not shown.
@ -111,6 +111,7 @@
|
|||||||
<EnablePREfast>true</EnablePREfast>
|
<EnablePREfast>true</EnablePREfast>
|
||||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@ -165,6 +166,7 @@
|
|||||||
<EnablePREfast>true</EnablePREfast>
|
<EnablePREfast>true</EnablePREfast>
|
||||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
<SubSystem>Windows</SubSystem>
|
<SubSystem>Windows</SubSystem>
|
||||||
@ -220,7 +222,6 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ServerDialog.cpp" />
|
<ClCompile Include="ServerDialog.cpp" />
|
||||||
<ClCompile Include="ServerConsoleApp.cpp" />
|
<ClCompile Include="ServerConsoleApp.cpp" />
|
||||||
<ClCompile Include="Utils.cpp" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="res\ServerConsole.rc2" />
|
<None Include="res\ServerConsole.rc2" />
|
||||||
@ -233,7 +234,6 @@
|
|||||||
<ClInclude Include="ServerDialog.h" />
|
<ClInclude Include="ServerDialog.h" />
|
||||||
<ClInclude Include="ServerConsoleApp.h" />
|
<ClInclude Include="ServerConsoleApp.h" />
|
||||||
<ClInclude Include="targetver.h" />
|
<ClInclude Include="targetver.h" />
|
||||||
<ClInclude Include="Utils.h" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="ServerConsole.rc" />
|
<ResourceCompile Include="ServerConsole.rc" />
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include <initguid.h>
|
|
||||||
#include "ServerConsoleApp.h"
|
#include "ServerConsoleApp.h"
|
||||||
#include "ServerDialog.h"
|
#include "ServerDialog.h"
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "ServerConsoleApp.h"
|
#include "ServerConsoleApp.h"
|
||||||
#include "ServerDialog.h"
|
#include "ServerDialog.h"
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
IMPLEMENT_DYNAMIC(ServerDialog, CDialogEx)
|
IMPLEMENT_DYNAMIC(ServerDialog, CDialogEx)
|
||||||
|
|
||||||
@ -111,9 +110,10 @@ void ServerDialog::OnCancel()
|
|||||||
{
|
{
|
||||||
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
AFX_MANAGE_STATE(AfxGetStaticModuleState());
|
||||||
|
|
||||||
KillTimer((UINT_PTR)this);
|
|
||||||
ShowWindow(SW_HIDE);
|
ShowWindow(SW_HIDE);
|
||||||
DestroyWindow();
|
DestroyWindow();
|
||||||
|
KillTimer((UINT_PTR)this);
|
||||||
|
m_pParentWnd->SendMessage(WM_CLOSE, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerDialog::RefreshPlayers()
|
void ServerDialog::RefreshPlayers()
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
#include "pch.h"
|
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
|
|
||||||
std::wstring to_wstring(const std::string_view& sourceString)
|
|
||||||
{
|
|
||||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
|
|
||||||
|
|
||||||
return converter.from_bytes(sourceString.data());
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
std::wstring to_wstring(const std::string_view& sourceString);
|
|
@ -38,6 +38,9 @@
|
|||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup />
|
<PropertyGroup />
|
||||||
|
<PropertyGroup Label="Vcpkg">
|
||||||
|
<VcpkgEnabled>false</VcpkgEnabled>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<FxCompile>
|
<FxCompile>
|
||||||
<ShaderType>Effect</ShaderType>
|
<ShaderType>Effect</ShaderType>
|
||||||
|
Loading…
Reference in New Issue
Block a user