1
0
mirror of https://github.com/ncblakely/GiantsTools synced 2024-12-03 18:33:08 +01:00

Merge pull request #12 from ncblakely/users/tos/branches

Add support for branch selection.
This commit is contained in:
ncblakely 2022-09-09 19:40:44 -07:00 committed by GitHub
commit 3ca6c77bc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1347 additions and 523 deletions

View File

@ -13,5 +13,8 @@
[Required]
public Uri InstallerUri { get; set; }
[Required]
public string BranchName { get; set;}
}
}

View File

@ -2,5 +2,5 @@
namespace Giants.DataContract.Contracts.V1
{
public record VersionInfoUpdate(string AppName, AppVersion AppVersion, string FileName);
public record VersionInfoUpdate(string AppName, AppVersion AppVersion, string FileName, string BranchName, bool ForceUpdate);
}

View File

@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Windows.Forms;
using Newtonsoft.Json;
@ -10,6 +11,7 @@
{
private const string defaultConfigFileName = "GiantsDefault.config";
private const string playerConfigFileName = "Giants.config";
private bool dirty = false;
private IDictionary<string, dynamic> defaultConfig = new Dictionary<string, dynamic>();
private IDictionary<string, dynamic> userConfig = new Dictionary<string, dynamic>();
@ -34,14 +36,45 @@
}
}
public string GetString(string section, string key)
public void Write()
{
if (!this.dirty)
{
return;
}
try
{
string userConfigFilePath = this.GetUserConfigPath();
Directory.CreateDirectory(Path.GetDirectoryName(userConfigFilePath));
using (var file = File.OpenWrite(userConfigFilePath))
using (var streamWriter = new StreamWriter(file))
{
string serializedConfig = JsonConvert.SerializeObject(this.userConfig, Formatting.Indented);
streamWriter.Write(serializedConfig);
}
this.dirty = false;
}
catch (Exception e)
{
MessageBox.Show($"Unhandled exception saving updated configuration: {e}");
}
}
public bool TryGetObject(string section, string key, object defaultValue, out object value)
{
value = defaultValue;
if (this.userConfig.ContainsKey(section))
{
dynamic sectionObject = this.userConfig[section];
if (sectionObject != null && sectionObject.ContainsKey(key))
{
return (string)sectionObject[key];
value = sectionObject[key];
return true;
}
}
@ -50,14 +83,58 @@
dynamic sectionObject = this.defaultConfig[section];
if (sectionObject != null && sectionObject.ContainsKey(key))
{
return (string)sectionObject[key];
value = sectionObject[key];
return true;
}
}
return string.Empty;
return false;
}
// TODO: other accessors unimplemented as we only need master server host name for now
public bool TryGetString(string section, string key, string defaultValue, out string value)
{
value = defaultValue;
if (this.TryGetObject(section, key, defaultValue, out object objValue))
{
value = objValue.ToString();
return true;
}
return false;
}
public bool TryGetBool(string section, string key, bool defaultValue, out bool value)
{
value = defaultValue;
if (this.TryGetObject(section, key, defaultValue, out object objValue))
{
return bool.TryParse(objValue.ToString(), out value);
}
return false;
}
public void SetValue(string section, string key, dynamic value)
{
if (!this.userConfig.ContainsKey(section))
{
this.userConfig.Add(section, new Dictionary<string, dynamic>());
}
dynamic sectionObject = this.userConfig[section];
if (sectionObject != null && sectionObject.ContainsKey(key))
{
sectionObject[key] = value;
}
else
{
sectionObject.Add(key, value);
}
this.dirty = true;
}
private static IDictionary<string, dynamic> ReadConfig(string filePath)
{

View File

@ -0,0 +1,8 @@
namespace Giants.Launcher
{
public static class ConfigDefaults
{
public const string BranchNameDefault = "Release";
public const string MasterServerHostNameDefault = "https://giants.azurewebsites.net/";
}
}

View File

@ -2,6 +2,11 @@
{
public static class ConfigKeys
{
// Update
public const string BranchName = "branchName";
public const string EnableBranchSelection = "enableBranchSelection";
// Network
public const string MasterServerHostName = "masterServerHostName";
public const string BannedPlayers = "bannedPlayers";
}

View File

@ -2,6 +2,7 @@
{
public static class ConfigSections
{
public const string Update = "update";
public const string Network = "network";
}
}

View File

@ -21,13 +21,15 @@ namespace Giants.Launcher
private const string RegistryValue = "DestDir";
private readonly HttpClient httpClient;
private readonly VersionClient versionHttpClient;
private readonly BranchesClient branchHttpClient;
private readonly VersionClient versionHttpClient;
private readonly CommunityClient communityHttpClient;
private string commandLine;
private string gamePath = null;
private Updater updater;
private Config config;
private readonly Config config;
private string branchName;
private string communityAppUri;
public LauncherForm()
@ -39,17 +41,29 @@ namespace Giants.Launcher
// Set window title
this.SetTitle();
// Read newer file-based game settings
this.config = new Config();
this.updater = new Updater(
updateCompletedCallback: this.LauncherForm_DownloadCompletedCallback,
updateProgressCallback: this.LauncherForm_DownloadProgressCallback);
// Read newer file-based game settings
this.config = new Config();
this.config.Read();
string baseUrl = this.config.GetString(ConfigSections.Network, ConfigKeys.MasterServerHostName);
this.config.TryGetString(ConfigSections.Network, ConfigKeys.MasterServerHostName, ConfigDefaults.MasterServerHostNameDefault, out string baseUrl);
this.config.TryGetString(ConfigSections.Update, ConfigKeys.BranchName, defaultValue: ConfigDefaults.BranchNameDefault, out string branchName);
this.httpClient = new HttpClient(
this.branchName = branchName;
this.httpClient = new HttpClient(
new HttpClientHandler()
{
UseProxy = false
});
this.branchHttpClient = new BranchesClient(this.httpClient)
{
BaseUrl = baseUrl,
};
this.versionHttpClient = new VersionClient(this.httpClient)
{
BaseUrl = baseUrl
@ -62,12 +76,14 @@ namespace Giants.Launcher
private void btnExit_Click(object sender, EventArgs e)
{
this.config.Write();
Application.Exit();
}
private void btnPlay_Click(object sender, EventArgs e)
{
GameSettings.Save();
this.config.Write();
foreach (string c in Environment.GetCommandLineArgs())
{
@ -87,19 +103,44 @@ namespace Giants.Launcher
gameProcess.Start();
Application.Exit();
}
catch(Exception ex)
catch (Exception ex)
{
MessageBox.Show(string.Format("Failed to launch game process at: {0}. {1}", this.gamePath, ex.Message),
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void btnOptions_Click(object sender, EventArgs e)
private async void btnOptions_Click(object sender, EventArgs e)
{
OptionsForm form = new OptionsForm(Resources.AppName + " Options", this.gamePath);
OptionsForm form = new OptionsForm(
title: Resources.AppName + " Options",
gamePath: this.gamePath,
appName: ApplicationNames.Giants,
currentBranchName: this.branchName,
config: this.config,
branchesClient: this.branchHttpClient)
{
StartPosition = FormStartPosition.CenterParent
};
form.StartPosition = FormStartPosition.CenterParent;
form.ShowDialog();
this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection);
if (enableBranchSelection)
{
this.config.TryGetString(ConfigSections.Update, ConfigKeys.BranchName, defaultValue: ConfigDefaults.BranchNameDefault, out string branchName);
if (!this.branchName.Equals(branchName))
{
this.branchName = branchName;
VersionInfo gameVersionInfo = await this.GetVersionInfo(
GetApplicationName(ApplicationType.Game), this.branchName);
this.btnPlay.Enabled = false;
await this.updater.UpdateApplication(ApplicationType.Game, gameVersionInfo);
}
}
}
private async void LauncherForm_Load(object sender, EventArgs e)
@ -126,30 +167,15 @@ namespace Giants.Launcher
GameSettings.Load(this.gamePath);
if (GameSettings.Get<int>("NoAutoUpdate") == 0)
{
Version gameVersion = VersionHelper.GetGameVersion(this.gamePath);
if (gameVersion == null)
{
string message = string.Format(Resources.AppNotFound, Resources.AppName);
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
{
Task updateTask = this.CheckForUpdates();
Task discordTask = this.UpdateDiscordStatus();
var appVersions = new Dictionary<ApplicationType, Version>()
{
[ApplicationType.Game] = gameVersion,
[ApplicationType.Launcher] = VersionHelper.GetLauncherVersion()
};
await Task.WhenAll(updateTask, discordTask);
}
}
// Check for updates
Task updateTask = this.CheckForUpdates(appVersions);
Task discordTask = this.CheckDiscordStatus();
await Task.WhenAll(updateTask, discordTask);
}
}
private async Task CheckDiscordStatus()
private async Task UpdateDiscordStatus()
{
try
{
@ -165,31 +191,30 @@ namespace Giants.Launcher
}
}
private async Task CheckForUpdates(Dictionary<ApplicationType, Version> appVersions)
private async Task CheckForUpdates()
{
this.updater = new Updater(
appVersions: appVersions,
updateCompletedCallback: this.LauncherForm_DownloadCompletedCallback,
updateProgressCallback: this.LauncherForm_DownloadProgressCallback);
Task<VersionInfo> gameVersionInfo = this.GetVersionInfo(
GetApplicationName(ApplicationType.Game));
GetApplicationName(ApplicationType.Game), this.branchName);
Task<VersionInfo> launcherVersionInfo = this.GetVersionInfo(
GetApplicationName(ApplicationType.Launcher));
GetApplicationName(ApplicationType.Launcher), this.branchName);
Version localGameVersion = VersionHelper.GetGameVersion(this.gamePath);
Version localLauncherVersion = VersionHelper.GetLauncherVersion();
await Task.WhenAll(gameVersionInfo, launcherVersionInfo);
if (this.updater.IsUpdateRequired(ApplicationType.Game, gameVersionInfo.Result))
if (this.updater.IsUpdateRequired(ApplicationType.Game, gameVersionInfo.Result, localGameVersion))
{
this.btnPlay.Enabled = false;
await this.updater.UpdateApplication(ApplicationType.Game, gameVersionInfo.Result);
}
if (this.updater.IsUpdateRequired(ApplicationType.Launcher, launcherVersionInfo.Result))
{
this.btnPlay.Enabled = false;
await this.updater.UpdateApplication(ApplicationType.Launcher, launcherVersionInfo.Result);
}
if (this.updater.IsUpdateRequired(ApplicationType.Launcher, launcherVersionInfo.Result, localLauncherVersion))
{
this.btnPlay.Enabled = false;
await this.updater.UpdateApplication(ApplicationType.Launcher, launcherVersionInfo.Result);
}
}
private static string GetApplicationName(ApplicationType applicationType)
@ -197,28 +222,20 @@ namespace Giants.Launcher
switch (applicationType)
{
case ApplicationType.Game:
#if BETA
return ApplicationNames.GiantsBeta;
#else
return ApplicationNames.Giants;
#endif
case ApplicationType.Launcher:
#if BETA
return ApplicationNames.GiantsLauncherBeta;
#else
return ApplicationNames.GiantsLauncher;
#endif
case ApplicationType.Launcher:
return ApplicationNames.GiantsLauncher;
}
throw new ArgumentOutOfRangeException();
throw new ArgumentOutOfRangeException();
}
private async Task<VersionInfo> GetVersionInfo(string appName)
private async Task<VersionInfo> GetVersionInfo(string appName, string branchName)
{
VersionInfo versionInfo;
try
{
versionInfo = await this.versionHttpClient.GetVersionInfoAsync(appName);
versionInfo = await this.versionHttpClient.GetVersionInfoAsync(appName, branchName);
return versionInfo;
}
catch (ApiException ex)
@ -233,7 +250,7 @@ namespace Giants.Launcher
}
}
private void LauncherForm_MouseDown(object sender, MouseEventArgs e)
private void LauncherForm_MouseDown(object sender, MouseEventArgs e)
{
// Force window to be draggable even though we have no menu bar
if (e.Button == MouseButtons.Left)
@ -298,6 +315,14 @@ namespace Giants.Launcher
updaterProcess.Start();
this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection);
if (enableBranchSelection)
{
this.config.SetValue(ConfigSections.Update, ConfigKeys.BranchName, this.branchName);
}
this.config.Write();
Application.Exit();
return;
}
@ -334,15 +359,6 @@ namespace Giants.Launcher
private void SetTitle()
{
string title = Resources.AppName;
#if DEBUG
title += " DEBUG";
#endif
#if BETA
title += " BETA";
#endif
this.Text = title;
}
}

View File

@ -44,18 +44,22 @@
this.btnResetDefaults = new System.Windows.Forms.Button();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this.chkUpdates = new System.Windows.Forms.CheckBox();
this.cmbBranch = new System.Windows.Forms.ComboBox();
this.BranchGroupBox = new System.Windows.Forms.GroupBox();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.BranchGroupBox.SuspendLayout();
this.SuspendLayout();
//
// cmbRenderer
//
this.cmbRenderer.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbRenderer.FormattingEnabled = true;
this.cmbRenderer.Location = new System.Drawing.Point(124, 19);
this.cmbRenderer.Location = new System.Drawing.Point(165, 23);
this.cmbRenderer.Margin = new System.Windows.Forms.Padding(4);
this.cmbRenderer.Name = "cmbRenderer";
this.cmbRenderer.Size = new System.Drawing.Size(252, 21);
this.cmbRenderer.Size = new System.Drawing.Size(335, 24);
this.cmbRenderer.TabIndex = 0;
this.cmbRenderer.SelectedIndexChanged += new System.EventHandler(this.cmbRenderer_SelectedIndexChanged);
//
@ -63,18 +67,20 @@
//
this.cmbResolution.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbResolution.FormattingEnabled = true;
this.cmbResolution.Location = new System.Drawing.Point(124, 50);
this.cmbResolution.Location = new System.Drawing.Point(165, 62);
this.cmbResolution.Margin = new System.Windows.Forms.Padding(4);
this.cmbResolution.Name = "cmbResolution";
this.cmbResolution.Size = new System.Drawing.Size(252, 21);
this.cmbResolution.Size = new System.Drawing.Size(335, 24);
this.cmbResolution.TabIndex = 1;
//
// cmbAntialiasing
//
this.cmbAntialiasing.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbAntialiasing.FormattingEnabled = true;
this.cmbAntialiasing.Location = new System.Drawing.Point(124, 81);
this.cmbAntialiasing.Location = new System.Drawing.Point(165, 100);
this.cmbAntialiasing.Margin = new System.Windows.Forms.Padding(4);
this.cmbAntialiasing.Name = "cmbAntialiasing";
this.cmbAntialiasing.Size = new System.Drawing.Size(252, 21);
this.cmbAntialiasing.Size = new System.Drawing.Size(335, 24);
this.cmbAntialiasing.TabIndex = 2;
//
// groupBox1
@ -85,9 +91,11 @@
this.groupBox1.Controls.Add(this.cmbRenderer);
this.groupBox1.Controls.Add(this.cmbResolution);
this.groupBox1.Controls.Add(this.cmbAntialiasing);
this.groupBox1.Location = new System.Drawing.Point(12, 12);
this.groupBox1.Location = new System.Drawing.Point(16, 15);
this.groupBox1.Margin = new System.Windows.Forms.Padding(4);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(382, 116);
this.groupBox1.Padding = new System.Windows.Forms.Padding(4);
this.groupBox1.Size = new System.Drawing.Size(509, 143);
this.groupBox1.TabIndex = 4;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Graphics Settings";
@ -95,27 +103,30 @@
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(52, 84);
this.label3.Location = new System.Drawing.Point(69, 103);
this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(66, 13);
this.label3.Size = new System.Drawing.Size(83, 16);
this.label3.TabIndex = 6;
this.label3.Text = "Anti-aliasing:";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(58, 53);
this.label2.Location = new System.Drawing.Point(77, 65);
this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(60, 13);
this.label2.Size = new System.Drawing.Size(74, 16);
this.label2.TabIndex = 5;
this.label2.Text = "Resolution:";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(64, 22);
this.label1.Location = new System.Drawing.Point(85, 27);
this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(54, 13);
this.label1.Size = new System.Drawing.Size(67, 16);
this.label1.TabIndex = 4;
this.label1.Text = "Renderer:";
//
@ -124,9 +135,11 @@
this.groupBox2.Controls.Add(this.cmbMode);
this.groupBox2.Controls.Add(this.chkTripleBuffering);
this.groupBox2.Controls.Add(this.chkVSync);
this.groupBox2.Location = new System.Drawing.Point(12, 138);
this.groupBox2.Location = new System.Drawing.Point(16, 170);
this.groupBox2.Margin = new System.Windows.Forms.Padding(4);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(118, 93);
this.groupBox2.Padding = new System.Windows.Forms.Padding(4);
this.groupBox2.Size = new System.Drawing.Size(157, 114);
this.groupBox2.TabIndex = 5;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Mode";
@ -139,17 +152,19 @@
"Fullscreen",
"Windowed",
"Borderless"});
this.cmbMode.Location = new System.Drawing.Point(12, 19);
this.cmbMode.Location = new System.Drawing.Point(16, 23);
this.cmbMode.Margin = new System.Windows.Forms.Padding(4);
this.cmbMode.Name = "cmbMode";
this.cmbMode.Size = new System.Drawing.Size(88, 21);
this.cmbMode.Size = new System.Drawing.Size(116, 24);
this.cmbMode.TabIndex = 3;
//
// chkTripleBuffering
//
this.chkTripleBuffering.AutoSize = true;
this.chkTripleBuffering.Location = new System.Drawing.Point(12, 64);
this.chkTripleBuffering.Location = new System.Drawing.Point(16, 79);
this.chkTripleBuffering.Margin = new System.Windows.Forms.Padding(4);
this.chkTripleBuffering.Name = "chkTripleBuffering";
this.chkTripleBuffering.Size = new System.Drawing.Size(97, 17);
this.chkTripleBuffering.Size = new System.Drawing.Size(119, 20);
this.chkTripleBuffering.TabIndex = 2;
this.chkTripleBuffering.Text = "Triple Buffering";
this.chkTripleBuffering.UseVisualStyleBackColor = true;
@ -157,18 +172,20 @@
// chkVSync
//
this.chkVSync.AutoSize = true;
this.chkVSync.Location = new System.Drawing.Point(12, 43);
this.chkVSync.Location = new System.Drawing.Point(16, 53);
this.chkVSync.Margin = new System.Windows.Forms.Padding(4);
this.chkVSync.Name = "chkVSync";
this.chkVSync.Size = new System.Drawing.Size(88, 17);
this.chkVSync.Size = new System.Drawing.Size(107, 20);
this.chkVSync.TabIndex = 1;
this.chkVSync.Text = "Vertical Sync";
this.chkVSync.UseVisualStyleBackColor = true;
//
// btnOK
//
this.btnOK.Location = new System.Drawing.Point(238, 208);
this.btnOK.Location = new System.Drawing.Point(317, 272);
this.btnOK.Margin = new System.Windows.Forms.Padding(4);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
this.btnOK.Size = new System.Drawing.Size(100, 28);
this.btnOK.TabIndex = 6;
this.btnOK.Text = "OK";
this.btnOK.UseVisualStyleBackColor = true;
@ -177,9 +194,10 @@
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(319, 208);
this.btnCancel.Location = new System.Drawing.Point(425, 272);
this.btnCancel.Margin = new System.Windows.Forms.Padding(4);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.Size = new System.Drawing.Size(100, 28);
this.btnCancel.TabIndex = 7;
this.btnCancel.Text = "Cancel";
this.btnCancel.UseVisualStyleBackColor = true;
@ -187,9 +205,10 @@
//
// btnResetDefaults
//
this.btnResetDefaults.Location = new System.Drawing.Point(300, 138);
this.btnResetDefaults.Location = new System.Drawing.Point(400, 170);
this.btnResetDefaults.Margin = new System.Windows.Forms.Padding(4);
this.btnResetDefaults.Name = "btnResetDefaults";
this.btnResetDefaults.Size = new System.Drawing.Size(94, 23);
this.btnResetDefaults.Size = new System.Drawing.Size(125, 28);
this.btnResetDefaults.TabIndex = 8;
this.btnResetDefaults.Text = "Reset Defaults";
this.btnResetDefaults.UseVisualStyleBackColor = true;
@ -198,9 +217,11 @@
// groupBox3
//
this.groupBox3.Controls.Add(this.chkUpdates);
this.groupBox3.Location = new System.Drawing.Point(136, 138);
this.groupBox3.Location = new System.Drawing.Point(181, 167);
this.groupBox3.Margin = new System.Windows.Forms.Padding(4);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(127, 49);
this.groupBox3.Padding = new System.Windows.Forms.Padding(4);
this.groupBox3.Size = new System.Drawing.Size(169, 54);
this.groupBox3.TabIndex = 6;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Other";
@ -208,22 +229,44 @@
// chkUpdates
//
this.chkUpdates.AutoSize = true;
this.chkUpdates.Location = new System.Drawing.Point(8, 22);
this.chkUpdates.Location = new System.Drawing.Point(11, 24);
this.chkUpdates.Margin = new System.Windows.Forms.Padding(4);
this.chkUpdates.Name = "chkUpdates";
this.chkUpdates.Size = new System.Drawing.Size(115, 17);
this.chkUpdates.Size = new System.Drawing.Size(140, 20);
this.chkUpdates.TabIndex = 1;
this.chkUpdates.Text = "Check for Updates";
this.chkUpdates.UseVisualStyleBackColor = true;
//
// cmbBranch
//
this.cmbBranch.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbBranch.FormattingEnabled = true;
this.cmbBranch.Location = new System.Drawing.Point(27, 17);
this.cmbBranch.Name = "cmbBranch";
this.cmbBranch.Size = new System.Drawing.Size(124, 24);
this.cmbBranch.TabIndex = 9;
//
// BranchGroupBox
//
this.BranchGroupBox.Controls.Add(this.cmbBranch);
this.BranchGroupBox.Location = new System.Drawing.Point(181, 222);
this.BranchGroupBox.Name = "BranchGroupBox";
this.BranchGroupBox.Size = new System.Drawing.Size(169, 49);
this.BranchGroupBox.TabIndex = 10;
this.BranchGroupBox.TabStop = false;
this.BranchGroupBox.Text = "Branch";
this.BranchGroupBox.Visible = false;
//
// OptionsForm
//
this.AcceptButton = this.btnOK;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(406, 239);
this.ClientSize = new System.Drawing.Size(541, 316);
this.ControlBox = false;
this.Controls.Add(this.BranchGroupBox);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.btnResetDefaults);
this.Controls.Add(this.btnCancel);
@ -231,6 +274,7 @@
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
this.Margin = new System.Windows.Forms.Padding(4);
this.Name = "OptionsForm";
this.ShowIcon = false;
this.ShowInTaskbar = false;
@ -245,6 +289,7 @@
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.BranchGroupBox.ResumeLayout(false);
this.ResumeLayout(false);
}
@ -267,5 +312,7 @@
private System.Windows.Forms.GroupBox groupBox3;
private System.Windows.Forms.CheckBox chkUpdates;
private System.Windows.Forms.ComboBox cmbMode;
private System.Windows.Forms.ComboBox cmbBranch;
private System.Windows.Forms.GroupBox BranchGroupBox;
}
}

View File

@ -1,7 +1,9 @@
using System;
using Giants.WebApi.Clients;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Giants.Launcher
@ -9,16 +11,34 @@ namespace Giants.Launcher
public partial class OptionsForm : Form
{
private readonly string gamePath = null;
private readonly string appName;
private readonly Config config;
private readonly string currentBranchName;
private readonly bool enableBranchSelection;
private readonly BranchesClient branchesClient;
public OptionsForm(string title, string gamePath)
public OptionsForm(
string title,
string gamePath,
string appName,
Config config,
string currentBranchName,
BranchesClient branchesClient)
{
this.InitializeComponent();
this.Text = title;
this.gamePath = gamePath;
}
this.appName = appName;
this.config = config;
this.currentBranchName = currentBranchName;
this.branchesClient = branchesClient;
private void OptionsForm_Load(object sender, EventArgs e)
this.config.TryGetBool(ConfigSections.Update, ConfigKeys.EnableBranchSelection, defaultValue: false, out bool enableBranchSelection);
this.enableBranchSelection = enableBranchSelection;
}
private async void OptionsForm_Load(object sender, EventArgs e)
{
// Must come first as other options depend on it
this.PopulateRenderers();
@ -28,9 +48,33 @@ namespace Giants.Launcher
this.PopulateAnisotropy();
this.PopulateAntialiasing();
await this.PopulateBranches();
this.SetOptions();
}
private async Task PopulateBranches()
{
if (this.enableBranchSelection)
{
try
{
var branches = await this.branchesClient.GetBranchesAsync(this.appName);
cmbBranch.Items.AddRange(branches.ToArray());
cmbBranch.SelectedItem = this.currentBranchName;
BranchGroupBox.Visible = true;
cmbBranch.Visible = true;
}
catch (Exception e)
{
MessageBox.Show($"Unhandled exception retrieving branch information from the server: {e.Message}");
}
}
}
private void PopulateRenderers()
{
this.cmbRenderer.Items.Clear();
@ -72,12 +116,19 @@ namespace Giants.Launcher
this.cmbAntialiasing.SelectedIndex = 0;
this.chkUpdates.Checked = GameSettings.Get<int>(RegistryKeys.NoAutoUpdate) != 1;
if (this.enableBranchSelection)
{
this.cmbBranch.SelectedItem = this.currentBranchName;
}
}
private void PopulateAntialiasing()
{
var antialiasingOptions = new List<KeyValuePair<string, int>>();
antialiasingOptions.Add(new KeyValuePair<string, int>(Resources.OptionNone, 0));
var antialiasingOptions = new List<KeyValuePair<string, int>>
{
new KeyValuePair<string, int>(Resources.OptionNone, 0)
};
var renderer = (RendererInfo)this.cmbRenderer.SelectedItem;
if (renderer != null)
@ -228,6 +279,15 @@ namespace Giants.Launcher
GameSettings.Save();
if (this.enableBranchSelection)
{
string newBranch = this.cmbBranch.SelectedItem?.ToString();
if (!string.IsNullOrEmpty(newBranch) && !newBranch.Equals(this.currentBranchName, StringComparison.OrdinalIgnoreCase))
{
this.config.SetValue(ConfigSections.Update, ConfigKeys.BranchName, newBranch);
}
}
this.Close();
}

View File

@ -76,6 +76,7 @@
<ItemGroup>
<Compile Include="ApplicationNames.cs" />
<Compile Include="Config.cs" />
<Compile Include="ConfigDefaults.cs" />
<Compile Include="ConfigKeys.cs" />
<Compile Include="ConfigSections.cs" />
<Compile Include="Renderer\RendererInfo.cs" />
@ -181,7 +182,6 @@
<Target Name="PostBuild" AfterTargets="Build">
<Copy Condition="$(GameImagePath) != ''" SourceFiles="$(TargetPath)" DestinationFolder="$(GameImagePath)" SkipUnchangedFiles="true" />
<Copy Condition="$(GameImagePath) != ''" SourceFiles="@(ReferenceCopyLocalPaths)" DestinationFolder="$(GameImagePath)" SkipUnchangedFiles="true" />
<Copy Condition="$(GameImagePath) != ''" SourceFiles="@(_SourceItemsToCopyToOutputDirectory)" DestinationFolder="$(GameImagePath)" SkipUnchangedFiles="true" />
<CallTarget Condition="$(GIANTS_PATH) != ''" Targets="CopyTargetToGameFolder" />
</Target>
</Project>

View File

@ -2,6 +2,8 @@
{
public enum ApplicationType
{
None = 0,
Launcher,
Game,
}

View File

@ -1,38 +1,35 @@
using System;
using System.Collections.Generic;
using Giants.WebApi.Clients;
using System;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Giants.WebApi.Clients;
namespace Giants.Launcher
{
public class Updater
{
private readonly IDictionary<ApplicationType, Version> appVersions;
private readonly AsyncCompletedEventHandler updateCompletedCallback;
private readonly DownloadProgressChangedEventHandler updateProgressCallback;
public Updater(
IDictionary<ApplicationType, Version> appVersions,
AsyncCompletedEventHandler updateCompletedCallback,
DownloadProgressChangedEventHandler updateProgressCallback)
{
this.appVersions = appVersions;
this.updateCompletedCallback = updateCompletedCallback;
this.updateProgressCallback = updateProgressCallback;
}
public bool IsUpdateRequired(ApplicationType applicationType, VersionInfo versionInfo)
public bool IsUpdateRequired(ApplicationType applicationType, VersionInfo remoteVersionInfo, Version localVersion)
{
if (versionInfo != null && this.ToVersion(versionInfo.Version) > this.appVersions[applicationType])
if (remoteVersionInfo?.InstallerUri != null
&& this.ToVersion(remoteVersionInfo.Version) > localVersion)
{
// Display update prompt
string updateMsg = applicationType == ApplicationType.Game ?
string.Format(Resources.UpdateAvailableText, this.ToVersion(versionInfo.Version).ToString()) :
string.Format(Resources.LauncherUpdateAvailableText, this.ToVersion(versionInfo.Version).ToString());
string.Format(Resources.UpdateAvailableText, this.ToVersion(remoteVersionInfo.Version).ToString()) :
string.Format(Resources.LauncherUpdateAvailableText, this.ToVersion(remoteVersionInfo.Version).ToString());
if (MessageBox.Show(updateMsg, Resources.UpdateAvailableTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes)
{

View File

@ -15,6 +15,10 @@
}
catch (Exception)
{
string message = string.Format(Resources.AppNotFound, Resources.AppName);
MessageBox.Show(message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
return null;
}
}

View File

@ -0,0 +1,7 @@
namespace Giants.Services
{
public static class BranchConstants
{
public const string DefaultBranchName = "Release";
}
}

View File

@ -1,9 +1,5 @@
namespace Giants.Services
{
using System;
using System.Collections.Generic;
using System.Text;
public static class CacheKeys
{
public const string ServerInfo = nameof(ServerInfo);

View File

@ -4,10 +4,10 @@ namespace Giants.Services
{
public class VersionInfo : DataContract.V1.VersionInfo, IIdentifiable
{
public string id => GenerateId(this.AppName);
public string id => GenerateId(this.AppName, this.BranchName ?? BranchConstants.DefaultBranchName);
public string DocumentType => nameof(VersionInfo);
public static string GenerateId(string gameName) => $"{nameof(VersionInfo)}-{gameName}";
public static string GenerateId(string appName, string branchName) => $"{nameof(VersionInfo)}-{appName}-{branchName}";
}
}

View File

@ -1,37 +1,75 @@
namespace Giants.Services
{
using System;
using System.Net.Http.Headers;
using Autofac;
using Giants.Services.Core;
using Giants.Services.Services;
using Giants.Services.Store;
using Giants.Services.Utility;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
public static class ServicesModule
public class ServicesModule : Module
{
public static void RegisterServices(IServiceCollection services, IConfiguration configuration)
private readonly IConfiguration configuration;
public ServicesModule(IConfiguration configuration)
{
services.AddSingleton<IServerRegistryService, ServerRegistryService>();
services.AddSingleton<IServerRegistryStore, CosmosDbServerRegistryStore>();
services.AddSingleton<IDateTimeProvider, DefaultDateTimeProvider>();
services.AddSingleton<IMemoryCache, MemoryCache>();
services.AddSingleton<IVersioningStore, CosmosDbVersioningStore>();
services.AddSingleton<IVersioningService, VersioningService>();
services.AddSingleton<ICommunityService, CommunityService>();
services.AddSingleton<ICrashReportService, CrashReportService>();
this.configuration = configuration;
}
services.AddHostedService<InitializerService>();
services.AddHostedService<ServerRegistryCleanupService>();
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ServerRegistryService>()
.As<IServerRegistryService>()
.SingleInstance();
builder.RegisterType<CosmosDbServerRegistryStore>()
.As<IServerRegistryStore>()
.SingleInstance();
builder.RegisterType<DefaultDateTimeProvider>()
.As<IDateTimeProvider>()
.SingleInstance();
builder.RegisterType<MemoryCache>()
.As<IMemoryCache>()
.SingleInstance();
builder.RegisterType<CosmosDbVersioningStore>()
.As<IVersioningStore>()
.SingleInstance();
builder.RegisterType<VersioningService>()
.As<IVersioningService>()
.SingleInstance();
builder.RegisterType<CommunityService>()
.As<ICommunityService>()
.SingleInstance();
builder.RegisterType<CrashReportService>()
.As<ICrashReportService>()
.SingleInstance();
services.AddHttpClient("Sentry", c =>
var cosmosClient = new CosmosDbClient(
connectionString: this.configuration["CosmosDbEndpoint"],
authKeyOrResourceToken: this.configuration["CosmosDbKey"],
databaseId: this.configuration["DatabaseId"],
containerId: this.configuration["ContainerId"]);
cosmosClient.Initialize().GetAwaiter().GetResult();
builder.RegisterInstance(cosmosClient).SingleInstance();
builder.Register<ISimpleMemoryCache<VersionInfo>>(icc =>
{
c.BaseAddress = new Uri(configuration["SentryBaseUri"]);
var versionStore = icc.Resolve<IVersioningStore>();
string sentryAuthenticationToken = configuration["SentryAuthenticationToken"];
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sentryAuthenticationToken);
});
return new SimpleMemoryCache<VersionInfo>(
expirationPeriod: TimeSpan.FromMinutes(5),
memoryCache: icc.Resolve<IMemoryCache>(),
cacheKey: CacheKeys.VersionInfo,
getAllItems: async (cacheEntry) =>
{
return await versionStore.GetVersions();
});
})
.AsSelf()
.SingleInstance();
}
}
}

View File

@ -5,6 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="Azure.Storage.Blobs" Version="12.13.1" />
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.30.1" />

View File

@ -1,12 +1,15 @@
namespace Giants.Services
{
using Giants.DataContract.V1;
using System.Collections.Generic;
using System.Threading.Tasks;
public interface IVersioningService
{
Task<VersionInfo> GetVersionInfo(string appName);
Task<IEnumerable<string>> GetBranches(string appName);
Task UpdateVersionInfo(string appName, AppVersion appVersion, string fileName);
Task<VersionInfo> GetVersionInfo(string appName, string branchName);
Task UpdateVersionInfo(string appName, AppVersion appVersion, string fileName, string branchName, bool force);
}
}

View File

@ -1,32 +0,0 @@
namespace Giants.Services
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
public class InitializerService : IHostedService
{
private readonly IVersioningStore updaterStore;
private readonly IServerRegistryStore serverRegistryStore;
public InitializerService(
IVersioningStore updaterStore,
IServerRegistryStore serverRegistryStore)
{
// TODO: Pick these up from reflection and auto initialize
this.updaterStore = updaterStore;
this.serverRegistryStore = serverRegistryStore;
}
public async Task StartAsync(CancellationToken cancellationToken)
{
await this.serverRegistryStore.Initialize();
await this.updaterStore.Initialize();
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
}

View File

@ -1,8 +1,10 @@
using Giants.DataContract.V1;
using Giants.Services.Utility;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Giants.Services
@ -11,41 +13,54 @@ namespace Giants.Services
{
private readonly IVersioningStore versioningStore;
private readonly IConfiguration configuration;
private readonly ISimpleMemoryCache<VersionInfo> versionCache;
private readonly ILogger<VersioningService> logger;
private const string InstallerContainerName = "public";
public VersioningService(
ILogger<VersioningService> logger,
IVersioningStore updaterStore,
IConfiguration configuration,
ILogger<VersioningService> logger)
ISimpleMemoryCache<VersionInfo> versionCache)
{
this.logger = logger;
this.versioningStore = updaterStore;
this.configuration = configuration;
this.logger = logger;
this.versionCache = versionCache;
}
public Task<VersionInfo> GetVersionInfo(string appName)
public async Task<VersionInfo> GetVersionInfo(string appName, string branchName)
{
ArgumentUtility.CheckStringForNullOrEmpty(appName);
return this.versioningStore.GetVersionInfo(appName);
branchName ??= BranchConstants.DefaultBranchName;
var versions = await this.versionCache.GetItems();
return versions
.Where(x => x.AppName.Equals(appName, StringComparison.Ordinal)
&& !string.IsNullOrEmpty(x.BranchName)
&& x.BranchName.Equals(branchName, StringComparison.OrdinalIgnoreCase))
.FirstOrDefault();
}
public async Task UpdateVersionInfo(string appName, AppVersion appVersion, string fileName)
public async Task UpdateVersionInfo(string appName, AppVersion appVersion, string fileName, string branchName, bool force)
{
ArgumentUtility.CheckStringForNullOrEmpty(appName);
ArgumentUtility.CheckForNull(appVersion);
ArgumentUtility.CheckStringForNullOrEmpty(fileName);
ArgumentUtility.CheckStringForNullOrEmpty(branchName);
Uri storageAccountUri = new Uri(this.configuration["StorageAccountUri"]);
var storageAccountUri = new Uri(this.configuration["StorageAccountUri"]);
VersionInfo versionInfo = await this.GetVersionInfo(appName);
VersionInfo versionInfo = await this.GetVersionInfo(appName, branchName);
if (versionInfo == null)
{
throw new ArgumentException($"No version information for {appName} found.", nameof(appName));
throw new ArgumentException($"No version information for {appName} ({branchName}) found.");
}
if (appVersion < versionInfo.Version)
if (!force && (appVersion < versionInfo.Version))
{
throw new ArgumentException($"Version {appVersion.SerializeToJson()} is less than current version {versionInfo.Version.SerializeToJson()}", nameof(appVersion));
}
@ -61,11 +76,22 @@ namespace Giants.Services
AppName = appName,
Version = appVersion,
InstallerUri = installerUri,
BranchName = branchName,
};
this.logger.LogInformation("Updating version info for {appName}: {versionInfo}", appName, newVersionInfo.SerializeToJson());
await this.versioningStore.UpdateVersionInfo(newVersionInfo);
this.versionCache.Invalidate();
}
public async Task<IEnumerable<string>> GetBranches(string appName)
{
var allVersions = await this.versionCache.GetItems();
return allVersions
.Where(x => x.AppName.Equals(appName, StringComparison.OrdinalIgnoreCase))
.Select(x => x.BranchName).ToList();
}
}
}

View File

@ -18,8 +18,8 @@
private readonly IConfiguration configuration;
private readonly IMemoryCache memoryCache;
private readonly IDateTimeProvider dateTimeProvider;
private readonly CosmosDbClient client;
private readonly TimeSpan timeoutPeriod;
private CosmosDbClient client;
private const int ServerRefreshIntervalInMinutes = 1;
@ -27,12 +27,14 @@
ILogger<CosmosDbServerRegistryStore> logger,
IConfiguration configuration,
IMemoryCache memoryCache,
IDateTimeProvider dateTimeProvider)
IDateTimeProvider dateTimeProvider,
CosmosDbClient client)
{
this.logger = logger;
this.configuration = configuration;
this.memoryCache = memoryCache;
this.dateTimeProvider = dateTimeProvider;
this.client = client;
this.timeoutPeriod = TimeSpan.FromMinutes(Convert.ToDouble(this.configuration["ServerTimeoutPeriodInMinutes"]));
}
@ -176,17 +178,6 @@
}
}
public async Task Initialize()
{
this.client = new CosmosDbClient(
connectionString: this.configuration["CosmosDbEndpoint"],
authKeyOrResourceToken: this.configuration["CosmosDbKey"],
databaseId: this.configuration["DatabaseId"],
containerId: this.configuration["ContainerId"]);
await this.client.Initialize();
}
private async Task<ConcurrentDictionary<string, IList<ServerInfo>>> PopulateCache(ICacheEntry entry)
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(1);

View File

@ -1,63 +1,26 @@
namespace Giants.Services.Store
{
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
public class CosmosDbVersioningStore : IVersioningStore
{
private readonly ILogger<CosmosDbServerRegistryStore> logger;
private readonly IMemoryCache memoryCache;
private readonly IConfiguration configuration;
private CosmosDbClient client;
private readonly CosmosDbClient client;
public CosmosDbVersioningStore(
ILogger<CosmosDbServerRegistryStore> logger,
IMemoryCache memoryCache,
IConfiguration configuration)
CosmosDbClient cosmosDbClient)
{
this.logger = logger;
this.memoryCache = memoryCache;
this.configuration = configuration;
this.client = cosmosDbClient;
}
public async Task<VersionInfo> GetVersionInfo(string appName)
public Task<IEnumerable<VersionInfo>> GetVersions()
{
VersionInfo versionInfo = await this.memoryCache.GetOrCreateAsync<VersionInfo>(
key: GetCacheKey(appName),
factory: async (entry) =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
return await this.client.GetItemById<VersionInfo>(
VersionInfo.GenerateId(appName),
nameof(VersionInfo));
});
return versionInfo;
return this.client.GetItems<VersionInfo>();
}
public async Task UpdateVersionInfo(VersionInfo versionInfo)
{
await this.client.UpsertItem(versionInfo);
}
public async Task Initialize()
{
this.client = new CosmosDbClient(
connectionString: this.configuration["CosmosDbEndpoint"],
authKeyOrResourceToken: this.configuration["CosmosDbKey"],
databaseId: this.configuration["DatabaseId"],
containerId: this.configuration["ContainerId"]);
await this.client.Initialize();
}
private static string GetCacheKey(string gameName)
{
return $"{CacheKeys.VersionInfo}-{gameName}";
}
}
}

View File

@ -1,23 +0,0 @@
namespace Giants.Services
{
using System;
using System.Threading.Tasks;
public class FileVersioningStore : IVersioningStore
{
public Task<VersionInfo> GetVersionInfo(string appName)
{
throw new NotImplementedException();
}
public Task UpdateVersionInfo(VersionInfo versionInfo)
{
throw new NotImplementedException();
}
public Task Initialize()
{
throw new NotImplementedException();
}
}
}

View File

@ -7,8 +7,6 @@
public interface IServerRegistryStore
{
Task Initialize();
Task DeleteServer(string id, string partitionKey = null);
Task DeleteServers(IEnumerable<string> ids, string partitionKey = null);

View File

@ -1,13 +1,12 @@
namespace Giants.Services
{
using System.Collections.Generic;
using System.Threading.Tasks;
public interface IVersioningStore
{
Task<VersionInfo> GetVersionInfo(string appName);
Task<IEnumerable<VersionInfo>> GetVersions();
Task UpdateVersionInfo(VersionInfo versionInfo);
Task Initialize();
}
}

View File

@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Giants.Services.Utility
{
public interface ISimpleMemoryCache<TRecord>
{
Task<IEnumerable<TRecord>> GetItems();
void Invalidate();
}
}

View File

@ -0,0 +1,82 @@
using Microsoft.Extensions.Azure;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Giants.Services.Utility
{
/// <summary>
/// Wrapper around <see cref="MemoryCache"/> that caches all items of the specified type.
/// </summary>
/// <typeparam name="TRecord"></typeparam>
public class SimpleMemoryCache<TRecord> : ISimpleMemoryCache<TRecord>, IDisposable
{
private readonly TimeSpan? expirationPeriod;
private IMemoryCache memoryCache;
private readonly object cacheKey;
private readonly Func<ICacheEntry, Task<IEnumerable<TRecord>>> getAllItems;
private CancellationTokenSource resetCacheToken;
private bool disposedValue;
public SimpleMemoryCache(
TimeSpan? expirationPeriod,
IMemoryCache memoryCache,
object cacheKey,
Func<ICacheEntry, Task<IEnumerable<TRecord>>> getAllItems)
{
this.expirationPeriod = expirationPeriod;
this.memoryCache = memoryCache;
this.cacheKey = cacheKey;
this.getAllItems = getAllItems;
this.resetCacheToken = new CancellationTokenSource();
}
public async Task<IEnumerable<TRecord>> GetItems()
{
IEnumerable<TRecord> items = await this.memoryCache.GetOrCreateAsync(cacheKey, this.PopulateCache);
return items;
}
public void Invalidate()
{
this.resetCacheToken.Cancel();
this.resetCacheToken.Dispose();
this.resetCacheToken = new CancellationTokenSource();
}
private async Task<IEnumerable<TRecord>> PopulateCache(ICacheEntry cacheEntry)
{
if (this.expirationPeriod.HasValue)
{
cacheEntry.AbsoluteExpirationRelativeToNow = this.expirationPeriod;
}
cacheEntry.AddExpirationToken(new CancellationChangeToken(this.resetCacheToken.Token));
return await this.getAllItems(cacheEntry);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
this.memoryCache?.Dispose();
this.resetCacheToken?.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@ -2,6 +2,15 @@
"version": 1,
"dependencies": {
"net6.0": {
"Autofac": {
"type": "Direct",
"requested": "[6.4.0, )",
"resolved": "6.4.0",
"contentHash": "tkFxl6wAPuwVhrlN8wuNADnd+k2tv4ReP7ZZSL0vjfcN0RcfC9v25ogxK6b03HC7D4NwWjSLf1G/zTG8Bw43wQ==",
"dependencies": {
"System.Diagnostics.DiagnosticSource": "4.7.1"
}
},
"AutoMapper": {
"type": "Direct",
"requested": "[11.0.1, )",

View File

@ -52,17 +52,26 @@ namespace Giants.WebApi.Clients
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task DeleteServerAsync()
public virtual System.Threading.Tasks.Task DeleteServerAsync(string gameName, int? port)
{
return DeleteServerAsync(System.Threading.CancellationToken.None);
return DeleteServerAsync(gameName, port, System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task DeleteServerAsync(System.Threading.CancellationToken cancellationToken)
public virtual async System.Threading.Tasks.Task DeleteServerAsync(string gameName, int? port, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Servers");
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Servers?");
if (gameName != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("gameName") + "=").Append(System.Uri.EscapeDataString(ConvertToString(gameName, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
}
if (port != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("port") + "=").Append(System.Uri.EscapeDataString(ConvertToString(port, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
}
urlBuilder_.Length--;
var client_ = _httpClient;
var disposeClient_ = false;
@ -364,6 +373,220 @@ namespace Giants.WebApi.Clients
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class BranchesClient
{
private string _baseUrl = "https://localhost:44304";
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public BranchesClient(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<System.Collections.Generic.ICollection<string>> GetBranchesAsync(string appName)
{
return GetBranchesAsync(appName, System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<System.Collections.Generic.ICollection<string>> GetBranchesAsync(string appName, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Branches?");
if (appName != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("appName") + "=").Append(System.Uri.EscapeDataString(ConvertToString(appName, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
}
urlBuilder_.Length--;
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Method = new System.Net.Http.HttpMethod("GET");
request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
var objectResponse_ = await ReadObjectResponseAsync<System.Collections.Generic.ICollection<string>>(response_, headers_, cancellationToken).ConfigureAwait(false);
if (objectResponse_.Object == null)
{
throw new ApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null);
}
return objectResponse_.Object;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
{
this.Object = responseObject;
this.Text = responseText;
}
public T Object { get; }
public string Text { get; }
}
public bool ReadResponseAsString { get; set; }
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
{
if (response == null || response.Content == null)
{
return new ObjectResponseResult<T>(default(T), string.Empty);
}
if (ReadResponseAsString)
{
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
return new ObjectResponseResult<T>(typedBody, responseText);
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
}
}
else
{
try
{
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new System.IO.StreamReader(responseStream))
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
{
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
var typedBody = serializer.Deserialize<T>(jsonTextReader);
return new ObjectResponseResult<T>(typedBody, string.Empty);
}
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
}
}
}
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return "";
}
if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[]) value);
}
else if (value.GetType().IsArray)
{
var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
}
var result = System.Convert.ToString(value, cultureInfo);
return result == null ? "" : result;
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class CommunityClient
{
@ -573,6 +796,210 @@ namespace Giants.WebApi.Clients
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class CrashReportsClient
{
private string _baseUrl = "https://localhost:44304";
private System.Net.Http.HttpClient _httpClient;
private System.Lazy<Newtonsoft.Json.JsonSerializerSettings> _settings;
public CrashReportsClient(System.Net.Http.HttpClient httpClient)
{
_httpClient = httpClient;
_settings = new System.Lazy<Newtonsoft.Json.JsonSerializerSettings>(CreateSerializerSettings);
}
private Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings()
{
var settings = new Newtonsoft.Json.JsonSerializerSettings();
UpdateJsonSerializerSettings(settings);
return settings;
}
public string BaseUrl
{
get { return _baseUrl; }
set { _baseUrl = value; }
}
protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _settings.Value; } }
partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url);
partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder);
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task UploadAsync()
{
return UploadAsync(System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task UploadAsync(System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/CrashReports");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
request_.Content = new System.Net.Http.StringContent(string.Empty, System.Text.Encoding.UTF8, "application/json");
request_.Method = new System.Net.Http.HttpMethod("POST");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
{
this.Object = responseObject;
this.Text = responseText;
}
public T Object { get; }
public string Text { get; }
}
public bool ReadResponseAsString { get; set; }
protected virtual async System.Threading.Tasks.Task<ObjectResponseResult<T>> ReadObjectResponseAsync<T>(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary<string, System.Collections.Generic.IEnumerable<string>> headers, System.Threading.CancellationToken cancellationToken)
{
if (response == null || response.Content == null)
{
return new ObjectResponseResult<T>(default(T), string.Empty);
}
if (ReadResponseAsString)
{
var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject<T>(responseText, JsonSerializerSettings);
return new ObjectResponseResult<T>(typedBody, responseText);
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body string as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, responseText, headers, exception);
}
}
else
{
try
{
using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
using (var streamReader = new System.IO.StreamReader(responseStream))
using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader))
{
var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings);
var typedBody = serializer.Deserialize<T>(jsonTextReader);
return new ObjectResponseResult<T>(typedBody, string.Empty);
}
}
catch (Newtonsoft.Json.JsonException exception)
{
var message = "Could not deserialize the response body stream as " + typeof(T).FullName + ".";
throw new ApiException(message, (int)response.StatusCode, string.Empty, headers, exception);
}
}
}
private string ConvertToString(object value, System.Globalization.CultureInfo cultureInfo)
{
if (value == null)
{
return "";
}
if (value is System.Enum)
{
var name = System.Enum.GetName(value.GetType(), value);
if (name != null)
{
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
if (field != null)
{
var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute))
as System.Runtime.Serialization.EnumMemberAttribute;
if (attribute != null)
{
return attribute.Value != null ? attribute.Value : name;
}
}
var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo));
return converted == null ? string.Empty : converted;
}
}
else if (value is bool)
{
return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant();
}
else if (value is byte[])
{
return System.Convert.ToBase64String((byte[]) value);
}
else if (value.GetType().IsArray)
{
var array = System.Linq.Enumerable.OfType<object>((System.Array) value);
return string.Join(",", System.Linq.Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
}
var result = System.Convert.ToString(value, cultureInfo);
return result == null ? "" : result;
}
}
[System.CodeDom.Compiler.GeneratedCode("NSwag", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class VersionClient
{
@ -608,14 +1035,14 @@ namespace Giants.WebApi.Clients
partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response);
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task<VersionInfo> GetVersionInfoAsync(string appName)
public virtual System.Threading.Tasks.Task<VersionInfo> GetVersionInfoAsync(string appName, string branchName)
{
return GetVersionInfoAsync(appName, System.Threading.CancellationToken.None);
return GetVersionInfoAsync(appName, branchName, System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task<VersionInfo> GetVersionInfoAsync(string appName, System.Threading.CancellationToken cancellationToken)
public virtual async System.Threading.Tasks.Task<VersionInfo> GetVersionInfoAsync(string appName, string branchName, System.Threading.CancellationToken cancellationToken)
{
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Version?");
@ -623,6 +1050,10 @@ namespace Giants.WebApi.Clients
{
urlBuilder_.Append(System.Uri.EscapeDataString("appName") + "=").Append(System.Uri.EscapeDataString(ConvertToString(appName, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
}
if (branchName != null)
{
urlBuilder_.Append(System.Uri.EscapeDataString("branchName") + "=").Append(System.Uri.EscapeDataString(ConvertToString(branchName, System.Globalization.CultureInfo.InvariantCulture))).Append("&");
}
urlBuilder_.Length--;
var client_ = _httpClient;
@ -684,6 +1115,78 @@ namespace Giants.WebApi.Clients
}
}
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual System.Threading.Tasks.Task UpdateVersionInfoAsync(VersionInfoUpdate versionInfoUpdate)
{
return UpdateVersionInfoAsync(versionInfoUpdate, System.Threading.CancellationToken.None);
}
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
/// <exception cref="ApiException">A server side error occurred.</exception>
public virtual async System.Threading.Tasks.Task UpdateVersionInfoAsync(VersionInfoUpdate versionInfoUpdate, System.Threading.CancellationToken cancellationToken)
{
if (versionInfoUpdate == null)
throw new System.ArgumentNullException("versionInfoUpdate");
var urlBuilder_ = new System.Text.StringBuilder();
urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/api/Version");
var client_ = _httpClient;
var disposeClient_ = false;
try
{
using (var request_ = new System.Net.Http.HttpRequestMessage())
{
var content_ = new System.Net.Http.StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(versionInfoUpdate, _settings.Value));
content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
request_.Content = content_;
request_.Method = new System.Net.Http.HttpMethod("POST");
PrepareRequest(client_, request_, urlBuilder_);
var url_ = urlBuilder_.ToString();
request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute);
PrepareRequest(client_, request_, url_);
var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
var disposeResponse_ = true;
try
{
var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value);
if (response_.Content != null && response_.Content.Headers != null)
{
foreach (var item_ in response_.Content.Headers)
headers_[item_.Key] = item_.Value;
}
ProcessResponse(client_, response_);
var status_ = (int)response_.StatusCode;
if (status_ == 200)
{
return;
}
else
{
var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false);
throw new ApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null);
}
}
finally
{
if (disposeResponse_)
response_.Dispose();
}
}
}
finally
{
if (disposeClient_)
client_.Dispose();
}
}
protected struct ObjectResponseResult<T>
{
public ObjectResponseResult(T responseObject, string responseText)
@ -829,7 +1332,7 @@ namespace Giants.WebApi.Clients
[Newtonsoft.Json.JsonProperty("numPlayers", Required = Newtonsoft.Json.Required.Always)]
public int NumPlayers { get; set; }
[Newtonsoft.Json.JsonProperty("maxPlayers", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
[Newtonsoft.Json.JsonProperty("maxPlayers", Required = Newtonsoft.Json.Required.Always)]
public int MaxPlayers { get; set; }
[Newtonsoft.Json.JsonProperty("gameState", Required = Newtonsoft.Json.Required.Always)]
@ -920,6 +1423,30 @@ namespace Giants.WebApi.Clients
[System.ComponentModel.DataAnnotations.Required]
public System.Uri InstallerUri { get; set; }
[Newtonsoft.Json.JsonProperty("branchName", Required = Newtonsoft.Json.Required.Always)]
[System.ComponentModel.DataAnnotations.Required]
public string BranchName { get; set; }
}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v11.0.0.0))")]
public partial class VersionInfoUpdate
{
[Newtonsoft.Json.JsonProperty("appName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string AppName { get; set; }
[Newtonsoft.Json.JsonProperty("appVersion", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public AppVersion AppVersion { get; set; }
[Newtonsoft.Json.JsonProperty("fileName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string FileName { get; set; }
[Newtonsoft.Json.JsonProperty("branchName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public string BranchName { get; set; }
[Newtonsoft.Json.JsonProperty("forceUpdate", Required = Newtonsoft.Json.Required.DisallowNull, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
public bool ForceUpdate { get; set; }
}

View File

@ -1,5 +1,5 @@
{
"x-generator": "NSwag v13.7.0.0 (NJsonSchema v10.1.24.0 (Newtonsoft.Json v10.0.0.0))",
"x-generator": "NSwag v13.16.1.0 (NJsonSchema v10.7.2.0 (Newtonsoft.Json v10.0.0.0))",
"openapi": "3.0.0",
"info": {
"title": "My Title",
@ -17,6 +17,26 @@
"Servers"
],
"operationId": "Servers_DeleteServer",
"parameters": [
{
"name": "gameName",
"in": "query",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
},
{
"name": "port",
"in": "query",
"schema": {
"type": "integer",
"format": "int32"
},
"x-position": 2
}
],
"responses": {
"200": {
"description": ""
@ -68,6 +88,40 @@
}
}
},
"/api/Branches": {
"get": {
"tags": [
"Branches"
],
"operationId": "Branches_GetBranches",
"parameters": [
{
"name": "appName",
"in": "query",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 1
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
},
"/api/Community": {
"get": {
"tags": [
@ -88,6 +142,19 @@
}
}
},
"/api/CrashReports": {
"post": {
"tags": [
"CrashReports"
],
"operationId": "CrashReports_Upload",
"responses": {
"200": {
"description": ""
}
}
}
},
"/api/Version": {
"get": {
"tags": [
@ -103,6 +170,15 @@
"nullable": true
},
"x-position": 1
},
{
"name": "branchName",
"in": "query",
"schema": {
"type": "string",
"nullable": true
},
"x-position": 2
}
],
"responses": {
@ -117,6 +193,29 @@
}
}
}
},
"post": {
"tags": [
"Version"
],
"operationId": "Version_UpdateVersionInfo",
"requestBody": {
"x-name": "versionInfoUpdate",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/VersionInfoUpdate"
}
}
},
"required": true,
"x-position": 1
},
"responses": {
"200": {
"description": ""
}
}
}
}
},
@ -153,6 +252,7 @@
"mapName",
"gameType",
"numPlayers",
"maxPlayers",
"gameState",
"timeLimit",
"fragLimit",
@ -305,7 +405,8 @@
"required": [
"appName",
"version",
"installerUri"
"installerUri",
"branchName"
],
"properties": {
"appName": {
@ -319,9 +420,42 @@
"type": "string",
"format": "uri",
"minLength": 1
},
"branchName": {
"type": "string",
"minLength": 1
}
}
},
"VersionInfoUpdate": {
"type": "object",
"additionalProperties": false,
"properties": {
"appName": {
"type": "string",
"nullable": true
},
"appVersion": {
"nullable": true,
"oneOf": [
{
"$ref": "#/components/schemas/AppVersion"
}
]
},
"fileName": {
"type": "string",
"nullable": true
},
"branchName": {
"type": "string",
"nullable": true
},
"forceUpdate": {
"type": "boolean"
}
}
}
}
}
}
}

View File

@ -0,0 +1,30 @@
using Giants.DataContract.V1;
using Giants.Services;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Giants.WebApi.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("1.1")]
[Route("api/[controller]")]
public class BranchesController : ControllerBase
{
private readonly IVersioningService versioningService;
public BranchesController(IVersioningService branchService)
{
this.versioningService = branchService;
}
[HttpGet]
public async Task<IEnumerable<string>> GetBranches([FromQuery]string appName)
{
ArgumentUtility.CheckStringForNullOrEmpty(appName);
return await this.versioningService.GetBranches(appName);
}
}
}

View File

@ -4,6 +4,7 @@ using Giants.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Web.Resource;
using System;
using System.Threading.Tasks;
namespace Giants.WebApi.Controllers
@ -27,11 +28,16 @@ namespace Giants.WebApi.Controllers
}
[HttpGet]
public async Task<DataContract.V1.VersionInfo> GetVersionInfo(string appName)
public async Task<DataContract.V1.VersionInfo> GetVersionInfo(string appName, string branchName)
{
ArgumentUtility.CheckStringForNullOrEmpty(appName);
Services.VersionInfo versionInfo = await this.versioningService.GetVersionInfo(appName);
VersionInfo versionInfo = await this.versioningService.GetVersionInfo(appName, branchName);
if (versionInfo == null)
{
throw new ArgumentException($"No version information for {appName} ({branchName}) found.", appName);
}
return this.mapper.Map<DataContract.V1.VersionInfo>(versionInfo);
}
@ -44,7 +50,12 @@ namespace Giants.WebApi.Controllers
{
ArgumentUtility.CheckForNull(versionInfoUpdate);
await this.versioningService.UpdateVersionInfo(versionInfoUpdate.AppName, versionInfoUpdate.AppVersion, versionInfoUpdate.FileName);
await this.versioningService.UpdateVersionInfo(
appName: versionInfoUpdate.AppName,
appVersion: versionInfoUpdate.AppVersion,
fileName: versionInfoUpdate.FileName,
branchName: versionInfoUpdate.BranchName,
force: versionInfoUpdate.ForceUpdate);
}
}
}

View File

@ -10,6 +10,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Autofac" Version="6.4.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="AutoMapper" Version="11.0.1" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.21.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="5.0.0" />

View File

@ -1,5 +1,8 @@
namespace Giants.Web
{
using Autofac;
using Autofac.Core;
using Autofac.Extensions.DependencyInjection;
using AutoMapper;
using Giants.Services;
using Microsoft.AspNetCore.Authentication.JwtBearer;
@ -14,8 +17,10 @@ namespace Giants.Web
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Logging;
using NSwag.Generation.Processors;
using System;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class Program
@ -26,7 +31,11 @@ namespace Giants.Web
ConfigureServices(builder);
var app = builder.Build();
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
builder.Host.ConfigureContainer<ContainerBuilder>((containerBuilder) => ConfigureAutofacServices(containerBuilder, builder.Configuration));
var app = builder
.Build();
ConfigureApplication(app, app.Environment);
app.Run();
@ -87,17 +96,40 @@ namespace Giants.Web
services.AddHttpContextAccessor();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
ServicesModule.RegisterServices(services, builder.Configuration);
IMapper mapper = Services.Mapper.GetMapper();
services.AddSingleton(mapper);
services.AddHealthChecks();
RegisterHttpClients(services, builder.Configuration);
RegisterHostedServices(services);
builder.Logging.AddEventSourceLogger();
builder.Logging.AddApplicationInsights();
}
private static void ConfigureAutofacServices(ContainerBuilder containerBuilder, IConfiguration configuration)
{
containerBuilder.RegisterModule(new ServicesModule(configuration));
}
private static void RegisterHttpClients(IServiceCollection services, IConfiguration configuration)
{
services.AddHttpClient("Sentry", c =>
{
c.BaseAddress = new Uri(configuration["SentryBaseUri"]);
string sentryAuthenticationToken = configuration["SentryAuthenticationToken"];
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", sentryAuthenticationToken);
});
}
private static void RegisterHostedServices(IServiceCollection services)
{
services.AddHostedService<ServerRegistryCleanupService>();
}
private static void ConfigureApplication(WebApplication app, IWebHostEnvironment env)
{
if (env.IsDevelopment())

View File

@ -2,6 +2,25 @@
"version": 1,
"dependencies": {
"net6.0": {
"Autofac": {
"type": "Direct",
"requested": "[6.4.0, )",
"resolved": "6.4.0",
"contentHash": "tkFxl6wAPuwVhrlN8wuNADnd+k2tv4ReP7ZZSL0vjfcN0RcfC9v25ogxK6b03HC7D4NwWjSLf1G/zTG8Bw43wQ==",
"dependencies": {
"System.Diagnostics.DiagnosticSource": "4.7.1"
}
},
"Autofac.Extensions.DependencyInjection": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "nGrXNpQX2FiZpIBydK9cxZnnoqP/cUd3k/53uRERYEqLtWzKtE15R6L+j5q5ax5Rv/+3wAIkOaPePkahfqrwjg==",
"dependencies": {
"Autofac": "6.4.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0"
}
},
"AutoMapper": {
"type": "Direct",
"requested": "[11.0.1, )",
@ -1908,6 +1927,7 @@
"type": "Project",
"dependencies": {
"AutoMapper": "[11.0.1, )",
"Autofac": "[6.4.0, )",
"Azure.Storage.Blobs": "[12.13.1, )",
"Giants.DataContract": "[1.0.0, )",
"Microsoft.Azure.Cosmos": "[3.30.1, )",

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30320.27
# Visual Studio Version 17
VisualStudioVersion = 17.3.32811.315
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Giants.Services", "Giants.Services\Giants.Services.csproj", "{0CD61424-4E74-4A48-A726-729FEA32C50C}"
EndProject
@ -25,8 +25,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{0801
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "imp_gbs", "Plugins\imp_gbs\imp_gbs.vcxproj", "{448F061E-AE05-4E06-84A1-C95660FD048C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Giants.Services.Tests", "Giants.Services.Tests\Giants.Services.Tests.csproj", "{2AFB71CA-8313-472E-B242-0517343764B4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ServerConsole", "ServerConsoleExample\ServerConsole.vcxproj", "{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NavMeshGenerator", "NavMeshGenerator\NavMeshGenerator.vcxproj", "{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}"
@ -34,327 +32,107 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
DebugBeta|Any CPU = DebugBeta|Any CPU
DebugBeta|x64 = DebugBeta|x64
DebugBeta|x86 = DebugBeta|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
ReleaseBeta|Any CPU = ReleaseBeta|Any CPU
ReleaseBeta|x64 = ReleaseBeta|x64
ReleaseBeta|x86 = ReleaseBeta|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|x64.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|x64.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|x86.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Debug|x86.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|x64.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.DebugBeta|x86.Build.0 = Debug|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|Any CPU.Build.0 = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|x64.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|x64.Build.0 = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|x86.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.Release|x86.Build.0 = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{0CD61424-4E74-4A48-A726-729FEA32C50C}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|x64.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|x64.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|x86.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Debug|x86.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|x64.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.DebugBeta|x86.Build.0 = Debug|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|Any CPU.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|x64.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|x64.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|x86.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.Release|x86.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{D426FE47-231B-41F0-AC78-293D12EF66A0}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|x64.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|x64.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|x86.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Debug|x86.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|x64.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.DebugBeta|x86.Build.0 = Debug|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|Any CPU.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|x64.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|x64.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|x86.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.Release|x86.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{9A284C34-8F4B-4E20-B74B-1A0AF04EDBD1}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|x64.ActiveCfg = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|x64.Build.0 = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|x86.ActiveCfg = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Debug|x86.Build.0 = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|Any CPU.ActiveCfg = DebugBeta|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|Any CPU.Build.0 = DebugBeta|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|x64.Build.0 = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.DebugBeta|x86.Build.0 = Debug|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|Any CPU.Build.0 = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|x64.ActiveCfg = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|x64.Build.0 = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|x86.ActiveCfg = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.Release|x86.Build.0 = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|Any CPU.ActiveCfg = ReleaseBeta|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|Any CPU.Build.0 = ReleaseBeta|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{612FD606-F072-4A04-9054-65BC592E9D3E}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|x64.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|x64.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|x86.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Debug|x86.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|x64.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.DebugBeta|x86.Build.0 = Debug|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|Any CPU.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|x64.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|x64.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|x86.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.Release|x86.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{D4C21170-82D4-4D1F-81EC-036835AC1238}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Debug|Any CPU.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Debug|Any CPU.Build.0 = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Debug|x64.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Debug|x86.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Debug|x86.Build.0 = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.DebugBeta|Any CPU.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.DebugBeta|Any CPU.Build.0 = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.DebugBeta|x64.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.DebugBeta|x86.ActiveCfg = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.DebugBeta|x86.Build.0 = Debug|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Release|Any CPU.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Release|Any CPU.Build.0 = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Release|x64.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Release|x86.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.Release|x86.Build.0 = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.ReleaseBeta|Any CPU.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.ReleaseBeta|Any CPU.Build.0 = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.ReleaseBeta|x64.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{9A0AF60B-3C3B-45B7-B5E1-4C9997A68EBB}.ReleaseBeta|x86.Build.0 = Release|Win32
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|x64.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|x64.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|x86.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Debug|x86.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|x64.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.DebugBeta|x86.Build.0 = Debug|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|Any CPU.Build.0 = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|x64.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|x64.Build.0 = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|x86.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.Release|x86.Build.0 = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{6286A2C7-15F0-4D87-B928-4B012F9E0FFE}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|x64.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|x64.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|x86.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Debug|x86.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|x64.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.DebugBeta|x86.Build.0 = Debug|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|Any CPU.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|x64.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|x64.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|x86.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.Release|x86.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{F5F3D216-9787-4CFF-88DF-8259CF667F88}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|x64.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|x64.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|x86.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Debug|x86.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|x64.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.DebugBeta|x86.Build.0 = Debug|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|Any CPU.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x64.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x64.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x86.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.Release|x86.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{49423BA5-4A9F-47A3-9D2D-E83936272DD0}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.ActiveCfg = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|Any CPU.Build.0 = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x64.ActiveCfg = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x64.Build.0 = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.ActiveCfg = Debug|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.Build.0 = Debug|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|Any CPU.ActiveCfg = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|Any CPU.Build.0 = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|x64.ActiveCfg = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|x64.Build.0 = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|x86.ActiveCfg = Debug|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.DebugBeta|x86.Build.0 = Debug|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.ActiveCfg = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Debug|x86.Build.0 = Debug|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|Any CPU.ActiveCfg = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|Any CPU.Build.0 = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|x64.ActiveCfg = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|x64.Build.0 = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|x86.ActiveCfg = Release|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.Release|x86.Build.0 = Release|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|Any CPU.ActiveCfg = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|Any CPU.Build.0 = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x64.ActiveCfg = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x64.Build.0 = Release|x64
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{448F061E-AE05-4E06-84A1-C95660FD048C}.ReleaseBeta|x86.Build.0 = Release|Win32
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x64.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x64.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x86.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Debug|x86.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|Any CPU.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|Any CPU.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x64.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x64.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x86.ActiveCfg = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.DebugBeta|x86.Build.0 = Debug|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|Any CPU.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x64.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x64.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x86.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.Release|x86.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|Any CPU.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|Any CPU.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x64.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x64.Build.0 = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x86.ActiveCfg = Release|Any CPU
{2AFB71CA-8313-472E-B242-0517343764B4}.ReleaseBeta|x86.Build.0 = Release|Any CPU
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|Any CPU.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x64.ActiveCfg = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x64.Build.0 = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x86.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Debug|x86.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|Any CPU.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x64.ActiveCfg = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x64.Build.0 = Debug|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x86.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.DebugBeta|x86.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|Any CPU.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|Any CPU.Build.0 = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x64.ActiveCfg = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x64.Build.0 = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x86.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.Release|x86.Build.0 = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|Any CPU.ActiveCfg = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|Any CPU.Build.0 = Debug|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x64.ActiveCfg = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x64.Build.0 = Release|x64
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{8AEE9CFF-0E24-498F-B60A-627A7F4A727D}.ReleaseBeta|x86.Build.0 = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|Any CPU.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.ActiveCfg = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x64.Build.0 = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Debug|x86.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|Any CPU.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x64.ActiveCfg = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x64.Build.0 = Debug|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x86.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.DebugBeta|x86.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|Any CPU.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|Any CPU.Build.0 = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.ActiveCfg = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x64.Build.0 = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.Release|x86.Build.0 = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|Any CPU.ActiveCfg = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|Any CPU.Build.0 = Debug|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x64.ActiveCfg = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x64.Build.0 = Release|x64
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x86.ActiveCfg = Release|Win32
{CA9C0938-3ADA-4C73-A89A-E9447ABCE101}.ReleaseBeta|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE